feat: 多语言 初步引入

This commit is contained in:
xkeyC 2024-03-15 00:01:06 +08:00
parent eae02be2af
commit b2c13a8a6f
45 changed files with 525 additions and 446 deletions

View File

@ -3,6 +3,8 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:synchronized/synchronized.dart';
export 'package:starcitizen_doctor/generated/l10n.dart';
var _logLock = Lock();
File? _logFile;

View File

@ -1,7 +1,9 @@
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'app.dart';
@ -38,10 +40,18 @@ class App extends HookConsumerWidget {
title: "StarCitizenToolBox",
restorationScopeId: "StarCitizenToolBox",
themeMode: ThemeMode.dark,
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
FluentLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
builder: (context, child) {
return MediaQuery(
data:
MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
child: child ?? const SizedBox(),
);
},
@ -55,10 +65,10 @@ class App extends HookConsumerWidget {
micaBackgroundColor: appState.themeConf.micaColor,
buttonTheme: ButtonThemeData(
defaultButtonStyle: ButtonStyle(
shape: ButtonState.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
side: BorderSide(color: Colors.white.withOpacity(.01)))),
))),
shape: ButtonState.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
side: BorderSide(color: Colors.white.withOpacity(.01)))),
))),
debugShowCheckedModeBanner: false,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
@ -98,10 +108,10 @@ Widget _defaultWebviewTitleBar(BuildContext context) {
const SizedBox(width: 12),
(state.isLoading)
? const SizedBox(
width: 24,
height: 24,
child: ProgressRing(),
)
width: 24,
height: 24,
child: ProgressRing(),
)
: const SizedBox(width: 24),
const SizedBox(width: 12),
SelectableText(state.url ?? ""),

View File

@ -31,9 +31,9 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(height: 12),
Button(
onPressed: () => _onCheckUpdate(context, ref),
child: const Padding(
padding: EdgeInsets.all(4),
child: Text("检查更新"),
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(S.current.about_check_update),
)),
const SizedBox(height: 32),
Container(
@ -103,7 +103,7 @@ class AboutUI extends HookConsumerWidget {
const Icon(FontAwesomeIcons.link),
const SizedBox(width: 8),
Text(
"在线反馈",
S.current.about_online_feedback,
style: TextStyle(
fontSize: 14, color: Colors.white.withOpacity(.6)),
),
@ -120,7 +120,7 @@ class AboutUI extends HookConsumerWidget {
const Icon(FontAwesomeIcons.qq),
const SizedBox(width: 8),
Text(
"QQ群: 940696487",
S.current.about_action_qq_group,
style: TextStyle(
fontSize: 14, color: Colors.white.withOpacity(.6)),
),
@ -138,7 +138,7 @@ class AboutUI extends HookConsumerWidget {
const Icon(FontAwesomeIcons.envelope),
const SizedBox(width: 8),
Text(
"邮箱: scbox@xkeyc.com",
S.current.about_action_email,
style: TextStyle(
fontSize: 14, color: Colors.white.withOpacity(.6)),
),
@ -155,7 +155,7 @@ class AboutUI extends HookConsumerWidget {
const Icon(FontAwesomeIcons.github),
const SizedBox(width: 8),
Text(
"开源",
S.current.about_action_open_source,
style: TextStyle(
fontSize: 14, color: Colors.white.withOpacity(.6)),
),
@ -209,13 +209,13 @@ class AboutUI extends HookConsumerWidget {
{required BuildContext context,
required String name,
required int value}) {
const names = {
"launch": "启动",
"gameLaunch": "启动游戏",
"firstLaunch": "累计用户",
"install_localization": "汉化安装",
"performance_apply": "性能调优",
"p4k_download": "P4K分流"
final names = {
"launch": S.current.about_analytics_launch,
"gameLaunch": S.current.about_analytics_launch_game,
"firstLaunch": S.current.about_analytics_total_users,
"install_localization": S.current.about_analytics_install_translation,
"performance_apply": S.current.about_analytics_performance_optimization,
"p4k_download": S.current.about_analytics_p4k_redirection
};
return Container(
padding: const EdgeInsets.all(12),
@ -256,7 +256,7 @@ class AboutUI extends HookConsumerWidget {
await ref.read(appGlobalModelProvider.notifier).checkUpdate(context);
if (!hasUpdate) {
if (!context.mounted) return;
showToast(context, "已经是最新版本!");
showToast(context, S.current.about_info_latest_version);
}
}
}

View File

@ -24,7 +24,7 @@ class HomeCountdownDialogUI extends HookConsumerWidget {
Navigator.of(context).pop();
}),
const SizedBox(width: 12),
const Text("节日倒计时"),
Text(S.current.home_holiday_countdown),
],
),
content: homeState.countdownFestivalListData == null
@ -85,7 +85,7 @@ class HomeCountdownDialogUI extends HookConsumerWidget {
),
const SizedBox(height: 12),
Text(
"* 以上节日日期由人工收录、维护,可能存在错误,欢迎反馈!",
S.current.home_holiday_countdown_disclaimer,
style: TextStyle(
fontSize: 13, color: Colors.white.withOpacity(.3)),
)

View File

@ -14,12 +14,12 @@ class HomeGameLoginDialogUI extends HookConsumerWidget {
useEffect(() {
ref.read(homeGameLoginUIModelProvider.notifier).launchWebLogin(context);
return null;
}, const []);
}, []);
return ContentDialog(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .56,
),
title: (loginState.loginStatus == 2) ? null : const Text("一键启动"),
title: (loginState.loginStatus == 2) ? null : Text(S.current.home_action_one_click_launch),
content: AnimatedSize(
duration: const Duration(milliseconds: 230),
child: Padding(
@ -33,13 +33,13 @@ class HomeGameLoginDialogUI extends HookConsumerWidget {
Center(
child: Column(
children: [
const Text("登录中..."),
Text(S.current.home_title_logging_in),
const SizedBox(height: 12),
const ProgressRing(),
if (loginState.isDeviceSupportWinHello ?? false)
const SizedBox(height: 24),
Text(
"* 若开启了自动填充,请留意弹出的 Windows Hello 窗口",
S.current.home_info_auto_fill_notice,
style: TextStyle(
fontSize: 13, color: Colors.white.withOpacity(.6)),
)
@ -52,9 +52,9 @@ class HomeGameLoginDialogUI extends HookConsumerWidget {
child: Column(
children: [
const SizedBox(height: 12),
const Text(
"欢迎回来!",
style: TextStyle(fontSize: 20),
Text(
S.current.home_login_title_welcome_back,
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 24),
if (loginState.avatarUrl != null)
@ -74,7 +74,7 @@ class HomeGameLoginDialogUI extends HookConsumerWidget {
fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
const Text("正在为您启动游戏..."),
Text(S.current.home_login_title_launching_game),
const SizedBox(height: 12),
const ProgressRing(),
],

View File

@ -25,7 +25,7 @@ part 'home_game_login_dialog_ui_model.g.dart';
@freezed
class HomeGameLoginState with _$HomeGameLoginState {
const factory HomeGameLoginState({
factory HomeGameLoginState({
required int loginStatus,
String? nickname,
String? avatarUrl,
@ -41,7 +41,7 @@ class HomeGameLoginState with _$HomeGameLoginState {
class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
@override
HomeGameLoginState build() {
return const HomeGameLoginState(loginStatus: 0);
return HomeGameLoginState(loginStatus: 0);
}
final LocalAuthentication _localAuth = LocalAuthentication();
@ -53,9 +53,9 @@ class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
state = state.copyWith(isDeviceSupportWinHello: isDeviceSupportWinHello);
if (!context.mounted) return;
goWebView(
context, "登录 RSI 账户", "https://robertsspaceindustries.com/connect",
loginMode: true, rsiLoginCallback: (message, ok) async {
goWebView(context, S.current.home_action_login_rsi_account,
"https://robertsspaceindustries.com/connect", loginMode: true,
rsiLoginCallback: (message, ok) async {
// dPrint(
// "======rsiLoginCallback=== $ok ===== data==\n${json.encode(message)}");
if (message == null || !ok) {
@ -98,12 +98,13 @@ class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context,
"是否开启自动密码填充?",
S.current.home_action_q_auto_password_fill_prompt,
const Text(
"盒子将使用 PIN 与 Windows 凭据加密保存您的密码,密码只存储在您的设备中。\n\n当下次登录需要输入密码时您只需授权PIN即可自动填充登录。"));
if (ok == true) {
if (await _localAuth.authenticate(
localizedReason: "输入PIN以启用加密") ==
localizedReason:
S.current.home_login_info_enter_pin_to_encrypt) ==
true) {
await _savePwd(inputEmail, inputPassword);
}
@ -129,12 +130,12 @@ class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context,
"游戏版本过期",
S.current.home_login_info_game_version_outdated,
Text(
"RSI 服务器报告版本号:${releaseInfo?["versionLabel"]} \n\n本地版本号:${buildInfo["RequestedP4ChangeNum"]} \n\n建议使用 RSI Launcher 更新游戏!"),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .4),
cancel: "忽略");
cancel: S.current.home_login_info_action_ignore);
if (ok == true) {
if (!context.mounted) return;
Navigator.pop(context);
@ -163,7 +164,7 @@ class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context,
"盒子一键启动",
S.current.home_login_action_title_box_one_click_launch,
const Text(
"本功能可以帮您更加便利的启动游戏。\n\n为确保账户安全 ,本功能使用汉化浏览器保留登录状态,且不会保存您的密码信息(除非你启用了自动填充功能)。"
"\n\n使用此功能登录账号时请确保您的 SC汉化盒子 是从可信任的来源下载。",
@ -182,7 +183,8 @@ class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
}
if (!await WebviewWindow.isWebviewAvailable()) {
if (!context.mounted) return;
await showToast(context, "需要安装 WebView2 Runtime");
await showToast(context,
S.current.home_login_action_title_need_webview2_runtime);
if (!context.mounted) return;
await launchUrlString(
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");

View File

@ -185,7 +185,7 @@ class __$$LoginStatusImplCopyWithImpl<$Res>
/// @nodoc
class _$LoginStatusImpl implements _LoginStatus {
const _$LoginStatusImpl(
_$LoginStatusImpl(
{required this.loginStatus,
this.nickname,
this.avatarUrl,
@ -270,7 +270,7 @@ class _$LoginStatusImpl implements _LoginStatus {
}
abstract class _LoginStatus implements HomeGameLoginState {
const factory _LoginStatus(
factory _LoginStatus(
{required final int loginStatus,
final String? nickname,
final String? avatarUrl,

View File

@ -7,7 +7,7 @@ part of 'home_game_login_dialog_ui_model.dart';
// **************************************************************************
String _$homeGameLoginUIModelHash() =>
r'c26dfd89985ff9246104135c288b673b7f15acf0';
r'bcd5fc6d85345207797dd51253b2608035e1fc36';
/// See also [HomeGameLoginUIModel].
@ProviderFor(HomeGameLoginUIModel)

View File

@ -36,9 +36,9 @@ class HomeMdContentDialogUI extends HookConsumerWidget {
),
actions: [
FilledButton(
child: const Padding(
padding: EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
child: Text("关闭"),
child: Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
child: Text(S.current.action_close),
),
onPressed: () {
Navigator.pop(context);

View File

@ -24,13 +24,13 @@ class HomeDownloaderUI extends HookConsumerWidget {
const SizedBox(width: 24),
const SizedBox(width: 12),
for (final item in <MapEntry<String, IconData>, String>{
const MapEntry("settings", FluentIcons.settings): "限速设置",
const MapEntry("settings", FluentIcons.settings): S.current.downloader_speed_limit_settings,
if (state.tasks.isNotEmpty)
const MapEntry("pause_all", FluentIcons.pause): "全部暂停",
const MapEntry("pause_all", FluentIcons.pause): S.current.downloader_action_pause_all,
if (state.waitingTasks.isNotEmpty)
const MapEntry("resume_all", FluentIcons.download): "恢复全部",
const MapEntry("resume_all", FluentIcons.download): S.current.downloader_action_resume_all,
if (state.tasks.isNotEmpty || state.waitingTasks.isNotEmpty)
const MapEntry("cancel_all", FluentIcons.cancel): "全部取消",
const MapEntry("cancel_all", FluentIcons.cancel): S.current.downloader_action_cancel_all,
}.entries)
Padding(
padding: const EdgeInsets.only(left: 6, right: 6),
@ -52,9 +52,9 @@ class HomeDownloaderUI extends HookConsumerWidget {
],
),
if (model.getTasksLen() == 0)
const Expanded(
Expanded(
child: Center(
child: Text("无下载任务"),
child: Text(S.current.downloader_info_no_download_tasks),
))
else
Expanded(
@ -171,22 +171,22 @@ class HomeDownloaderUI extends HookConsumerWidget {
if (type != "stopped")
DropDownButton(
closeAfterClick: false,
title: const Padding(
padding: EdgeInsets.all(3),
child: Text('选项'),
title: Padding(
padding: const EdgeInsets.all(3),
child: Text(S.current.downloader_action_options),
),
items: [
if (task.status == "paused")
MenuFlyoutItem(
leading:
const Icon(FluentIcons.download),
text: const Text('继续下载'),
text: Text(S.current.downloader_action_continue_download),
onPressed: () =>
model.resumeTask(task.gid))
else if (task.status == "active")
MenuFlyoutItem(
leading: const Icon(FluentIcons.pause),
text: const Text('暂停下载'),
text: Text(S.current.downloader_action_pause_download),
onPressed: () =>
model.pauseTask(task.gid)),
const MenuFlyoutSeparator(),
@ -195,7 +195,7 @@ class HomeDownloaderUI extends HookConsumerWidget {
FluentIcons.chrome_close,
size: 14,
),
text: const Text('取消下载'),
text: Text(S.current.downloader_action_cancel_download),
onPressed: () =>
model.cancelTask(context, task.gid)),
MenuFlyoutItem(
@ -203,7 +203,7 @@ class HomeDownloaderUI extends HookConsumerWidget {
FluentIcons.folder_open,
size: 14,
),
text: const Text('打开文件夹'),
text: Text(S.current.action_open_folder),
onPressed: () => model.openFolder(task)),
],
),

View File

@ -21,7 +21,7 @@ part 'home_downloader_ui_model.freezed.dart';
@freezed
class HomeDownloaderUIState with _$HomeDownloaderUIState {
const factory HomeDownloaderUIState({
factory HomeDownloaderUIState({
@Default([]) List<Aria2Task> tasks,
@Default([]) List<Aria2Task> waitingTasks,
@Default([]) List<Aria2Task> stoppedTasks,
@ -40,23 +40,23 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
bool _disposed = false;
final statusMap = {
"active": "下载中...",
"waiting": "等待中",
"paused": "已暂停",
"error": "下载失败",
"complete": "下载完成",
"removed": "已删除",
"active": S.current.downloader_info_downloading_status,
"waiting": S.current.downloader_info_waiting,
"paused": S.current.downloader_info_paused,
"error": S.current.downloader_info_download_failed,
"complete": S.current.downloader_info_download_completed,
"removed": S.current.downloader_info_deleted,
};
final listHeaderStatusMap = {
"active": "下载中",
"waiting": "等待中",
"stopped": "已结束",
"active": S.current.downloader_title_downloading,
"waiting": S.current.downloader_info_waiting,
"stopped": S.current.downloader_title_ended,
};
@override
HomeDownloaderUIState build() {
state = const HomeDownloaderUIState();
state = HomeDownloaderUIState();
_listenDownloader();
ref.onDispose(() {
_disposed = true;
@ -79,7 +79,7 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
return;
case "cancel_all":
final userOK = await showConfirmDialogs(
context, "确认取消全部任务?", const Text("如果文件不再需要,你可能需要手动删除下载文件。"));
context, "确认取消全部任务?", Text(S.current.downloader_info_manual_file_deletion_note));
if (userOK == true) {
if (!aria2cState.isRunning) return;
try {
@ -170,7 +170,7 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
if (gid != null) {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context, "确认取消下载?", const Text("如果文件不再需要,你可能需要手动删除下载文件。"));
context, "确认取消下载?", Text(S.current.downloader_info_manual_file_deletion_note));
if (ok == true) {
final aria2c = ref.read(aria2cModelProvider).aria2c;
await aria2c?.remove(gid);
@ -242,22 +242,22 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context,
"限速设置",
S.current.downloader_speed_limit_settings,
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"SC 汉化盒子使用 p2p 网络来加速文件下载,如果您流量有限,可在此处将上传带宽设置为 1(byte)。",
S.current.downloader_info_p2p_network_note,
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(.6),
),
),
const SizedBox(height: 24),
const Text("请输入下载单位1、100k、10m 0或留空为不限速。"),
Text(S.current.downloader_info_download_unit_input_prompt),
const SizedBox(height: 12),
const Text("上传限速:"),
Text(S.current.downloader_input_upload_speed_limit),
const SizedBox(height: 6),
TextFormBox(
placeholder: "1、100k、10m、0",
@ -266,7 +266,7 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
inputFormatters: [ifr],
),
const SizedBox(height: 12),
const Text("下载限速:"),
Text(S.current.downloader_input_download_speed_limit),
const SizedBox(height: 6),
TextFormBox(
placeholder: "1、100k、10m、0",
@ -276,7 +276,7 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
),
const SizedBox(height: 24),
Text(
"* P2P 上传仅在下载文件时进行,下载完成后会关闭 p2p 连接。如您想参与做种,请通过关于页面联系我们。",
S.current.downloader_input_info_p2p_upload_note,
style: TextStyle(
fontSize: 13,
color: Colors.white.withOpacity(.6),

View File

@ -136,7 +136,7 @@ class __$$HomeDownloaderUIStateImplCopyWithImpl<$Res>
/// @nodoc
class _$HomeDownloaderUIStateImpl implements _HomeDownloaderUIState {
const _$HomeDownloaderUIStateImpl(
_$HomeDownloaderUIStateImpl(
{final List<Aria2Task> tasks = const [],
final List<Aria2Task> waitingTasks = const [],
final List<Aria2Task> stoppedTasks = const [],
@ -211,7 +211,7 @@ class _$HomeDownloaderUIStateImpl implements _HomeDownloaderUIState {
}
abstract class _HomeDownloaderUIState implements HomeDownloaderUIState {
const factory _HomeDownloaderUIState(
factory _HomeDownloaderUIState(
{final List<Aria2Task> tasks,
final List<Aria2Task> waitingTasks,
final List<Aria2Task> stoppedTasks,

View File

@ -7,7 +7,7 @@ part of 'home_downloader_ui_model.dart';
// **************************************************************************
String _$homeDownloaderUIModelHash() =>
r'947ebb9abb262aea6121c74481753da0eebb9a79';
r'88e1c9a667672d303cb244305dc0aec89d77ffe5';
/// See also [HomeDownloaderUIModel].
@ProviderFor(HomeDownloaderUIModel)

View File

@ -27,7 +27,7 @@ class HomeGameDoctorUI extends HookConsumerWidget {
model.doCheck(context);
});
return null;
}, const []);
}, []);
return makeDefaultPage(context,
title: "一键诊断 -> ${homeState.scInstalledPath}",
@ -40,9 +40,9 @@ class HomeGameDoctorUI extends HookConsumerWidget {
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
for (final item in const {
"rsi_log": "RSI启动器log",
"game_log": "游戏运行log",
for (final item in {
"rsi_log": S.current.doctor_action_rsi_launcher_log,
"game_log": S.current.doctor_action_game_run_log,
}.entries)
Padding(
padding: const EdgeInsets.only(left: 6, right: 6),
@ -76,14 +76,14 @@ class HomeGameDoctorUI extends HookConsumerWidget {
))
else if (state.checkResult == null ||
state.checkResult!.isEmpty) ...[
const Expanded(
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 12),
Text("扫描完毕,没有找到问题!", maxLines: 1),
SizedBox(height: 64),
const SizedBox(height: 12),
Text(S.current.doctor_info_scan_complete_no_issues, maxLines: 1),
const SizedBox(height: 64),
],
),
))
@ -104,7 +104,7 @@ class HomeGameDoctorUI extends HookConsumerWidget {
const SizedBox(height: 12),
Text(state.isFixingString.isNotEmpty
? state.isFixingString
: "正在处理..."),
: S.current.doctor_info_processing),
],
),
),
@ -122,7 +122,7 @@ class HomeGameDoctorUI extends HookConsumerWidget {
return GestureDetector(
onTap: () async {
await showToast(context,
"您即将前往由 深空治疗中心QQ群号536454632 提供的游戏异常救援服务,主要解决游戏安装失败与频繁闪退,如游戏玩法问题,请勿加群。");
S.current.doctor_info_game_rescue_service_note);
launchUrlString(
"https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=-M4wEme_bCXbUGT4LFKLH0bAYTFt70Ad&authKey=vHVr0TNgRmKu%2BHwywoJV6EiLa7La2VX74Vkyixr05KA0H9TqB6qWlCdY%2B9jLQ4Ha&noverify=0&group_code=536454632");
},
@ -140,7 +140,7 @@ class HomeGameDoctorUI extends HookConsumerWidget {
children: [
Image.asset("assets/rescue.png", width: 24, height: 24),
const SizedBox(width: 12),
const Text("需要帮助? 点击加群寻求免费人工支援!"),
Text(S.current.doctor_info_need_help),
],
),
)),
@ -155,7 +155,7 @@ class HomeGameDoctorUI extends HookConsumerWidget {
Text(state.lastScreenInfo, maxLines: 1),
const SizedBox(height: 12),
Text(
"注意:本工具检测结果仅供参考,若您不理解以下操作,请提供截图给有经验的玩家!",
S.current.doctor_info_tool_check_result_note,
style: TextStyle(color: Colors.red, fontSize: 16),
),
const SizedBox(height: 24),
@ -177,17 +177,17 @@ class HomeGameDoctorUI extends HookConsumerWidget {
final errorNames = {
"unSupport_system":
MapEntry("不支持的操作系统,游戏可能无法运行", "请升级您的系统 (${item.value})"),
"no_live_path": MapEntry("安装目录缺少LIVE文件夹可能导致安装失败",
"no_live_path": MapEntry(S.current.doctor_info_result_missing_live_folder,
"点击修复为您创建 LIVE 文件夹,完成后重试安装。(${item.value})"),
"nvme_PhysicalBytes": MapEntry("新型 NVME 设备,与 RSI 启动器暂不兼容,可能导致安装失败",
"nvme_PhysicalBytes": MapEntry(S.current.doctor_info_result_incompatible_nvme_device,
"为注册表项添加 ForcedPhysicalSectorSizeInBytes 值 模拟旧设备。硬盘分区(${item.value})"),
"eac_file_miss": const MapEntry("EasyAntiCheat 文件丢失",
"未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件"),
"eac_not_install": const MapEntry("EasyAntiCheat 未安装 或 未正常退出",
"EasyAntiCheat 未安装,请点击修复为您一键安装。(在游戏正常启动并结束前,该问题会一直出现,若您因为其他原因游戏闪退,可忽略此条目)"),
"eac_file_miss": MapEntry(S.current.doctor_info_result_missing_easyanticheat_files,
S.current.doctor_info_result_verify_files_with_rsi_launcher),
"eac_not_install": MapEntry(S.current.doctor_info_result_easyanticheat_not_installed,
S.current.doctor_info_result_install_easyanticheat),
"cn_user_name":
const MapEntry("中文用户名!", "中文用户名可能会导致游戏启动/安装错误! 点击修复按钮查看修改教程!"),
"cn_install_path": MapEntry("中文安装路径!",
MapEntry("中文用户名!", S.current.doctor_info_result_chinese_username_error),
"cn_install_path": MapEntry(S.current.doctor_info_result_chinese_install_path,
"中文安装路径!这可能会导致游戏 启动/安装 错误!(${item.value}请在RSI启动器更换安装路径。"),
"low_ram": MapEntry(
"物理内存过低", "您至少需要 16GB 的物理内存Memory才可运行此游戏。当前大小${item.value}"),
@ -224,9 +224,9 @@ class HomeGameDoctorUI extends HookConsumerWidget {
: () async {
await model.doFix(context, item);
},
child: const Padding(
padding: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
child: Text("修复"),
child: Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
child: Text(S.current.doctor_info_action_fix),
),
),
),
@ -262,10 +262,10 @@ class HomeGameDoctorUI extends HookConsumerWidget {
onPressed: () {
launchUrlString(item.value);
},
child: const Padding(
child: Padding(
padding:
EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
child: Text("查看解决方案"),
const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
child: Text(S.current.doctor_action_view_solution),
),
)
: null,
@ -284,7 +284,7 @@ class HomeGameDoctorUI extends HookConsumerWidget {
case "game_log":
if (homeState.scInstalledPath == "not_install" ||
homeState.scInstalledPath == null) {
showToast(context, "请在首页选择游戏安装目录。");
showToast(context, S.current.doctor_tip_title_select_game_directory);
return;
}
SystemHelper.openDir("${homeState.scInstalledPath}\\Game.log");

View File

@ -17,7 +17,7 @@ part 'game_doctor_ui_model.freezed.dart';
@freezed
class HomeGameDoctorState with _$HomeGameDoctorState {
const factory HomeGameDoctorState({
factory HomeGameDoctorState({
@Default(false) bool isChecking,
@Default(false) bool isFixing,
@Default("") String lastScreenInfo,
@ -30,7 +30,7 @@ class HomeGameDoctorState with _$HomeGameDoctorState {
class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
@override
HomeGameDoctorState build() {
state = const HomeGameDoctorState();
state = HomeGameDoctorState();
return state;
}
@ -43,13 +43,13 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
state = state.copyWith(isFixing: true, isFixingString: "");
switch (item.key) {
case "unSupport_system":
showToast(context, "若您的硬件达标,请尝试安装最新的 Windows 系统。");
showToast(context, S.current.doctor_action_result_try_latest_windows);
break;
case "no_live_path":
try {
await Directory(item.value).create(recursive: true);
if (!context.mounted) break;
showToast(context, "创建文件夹成功,请尝试继续下载游戏!");
showToast(context, S.current.doctor_action_result_create_folder_success);
checkResult.remove(item);
state = state.copyWith(checkResult: checkResult);
} catch (e) {
@ -61,7 +61,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
if (r == "") {
if (!context.mounted) break;
showToast(context,
"修复成功,请尝试重启后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
S.current.doctor_action_result_fix_success);
checkResult.remove(item);
state = state.copyWith(checkResult: checkResult);
} else {
@ -71,7 +71,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
break;
case "eac_file_miss":
showToast(
context, "未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件");
context, S.current.doctor_info_result_verify_files_with_rsi_launcher);
break;
case "eac_not_install":
final eacJsonPath = "${item.value}\\Settings.json";
@ -84,7 +84,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID");
if (result.stderr == "") {
if (!context.mounted) break;
showToast(context, "修复成功,请尝试启动游戏。(若问题无法解决,请使用工具箱的 《重装 EAC》");
showToast(context, S.current.doctor_action_result_game_start_success);
checkResult.remove(item);
state = state.copyWith(checkResult: checkResult);
} else {
@ -97,13 +97,13 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
}
break;
case "cn_user_name":
showToast(context, "即将跳转,教程来自互联网,请谨慎操作...");
showToast(context, S.current.doctor_action_result_redirect_warning);
await Future.delayed(const Duration(milliseconds: 300));
launchUrlString(
"https://btfy.eu.org/?q=5L+u5pS5d2luZG93c+eUqOaIt+WQjeS7juS4reaWh+WIsOiLseaWhw==");
break;
default:
showToast(context, "该问题暂不支持自动处理,请提供截图寻求帮助");
showToast(context, S.current.doctor_action_result_issue_not_supported);
break;
}
state = state.copyWith(isFixing: false, isFixingString: "");
@ -112,7 +112,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
// ignore: avoid_build_context_in_providers
doCheck(BuildContext context) async {
if (state.isChecking) return;
state = state.copyWith(isChecking: true, lastScreenInfo: "正在分析...");
state = state.copyWith(isChecking: true, lastScreenInfo: S.current.doctor_action_analyzing);
dPrint("-------- start docker check -----");
if (!context.mounted) return;
await _statCheck(context);
@ -126,9 +126,9 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
final checkResult = <MapEntry<String, String>>[];
// TODO for debug
// checkResult?.add(const MapEntry("unSupport_system", "android"));
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "C"));
// checkResult?.add(const MapEntry("no_live_path", ""));
// checkResult?.add(MapEntry("unSupport_system", "android"));
// checkResult?.add(MapEntry("nvme_PhysicalBytes", "C"));
// checkResult?.add(MapEntry("no_live_path", ""));
await _checkPreInstall(context, scInstalledPath, checkResult);
if (!context.mounted) return;
@ -137,7 +137,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
await _checkGameRunningLog(context, scInstalledPath, checkResult);
if (checkResult.isEmpty) {
const lastScreenInfo = "分析完毕,没有发现问题";
final lastScreenInfo = S.current.doctor_action_result_analysis_no_issue;
state = state.copyWith(checkResult: null, lastScreenInfo: lastScreenInfo);
} else {
final lastScreenInfo = "分析完毕,发现 ${checkResult.length} 个问题";
@ -147,7 +147,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
if (scInstalledPath == "not_install" && (checkResult.isEmpty)) {
if (!context.mounted) return;
showToast(context, "扫描完毕,没有发现问题,若仍然安装失败,请尝试使用工具箱中的 RSI启动器管理员模式。");
showToast(context, S.current.doctor_action_result_toast_scan_no_issue);
}
}
@ -155,7 +155,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
Future _checkGameRunningLog(BuildContext context, String scInstalledPath,
List<MapEntry<String, String>> checkResult) async {
if (scInstalledPath == "not_install") return;
const lastScreenInfo = "正在检查Game.log";
final lastScreenInfo = S.current.doctor_action_tip_checking_game_log;
state = state.copyWith(lastScreenInfo: lastScreenInfo);
final logs = await SCLoggerHelper.getGameRunningLogs(scInstalledPath);
if (logs == null) return;
@ -174,7 +174,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
Future _checkEAC(BuildContext context, String scInstalledPath,
List<MapEntry<String, String>> checkResult) async {
if (scInstalledPath == "not_install") return;
const lastScreenInfo = "正在检查EAC";
final lastScreenInfo = S.current.doctor_action_info_checking_eac;
state = state.copyWith(lastScreenInfo: lastScreenInfo);
final eacPath = "$scInstalledPath\\EasyAntiCheat";
@ -205,7 +205,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
// ignore: avoid_build_context_in_providers
Future _checkPreInstall(BuildContext context, String scInstalledPath,
List<MapEntry<String, String>> checkResult) async {
const lastScreenInfo = "正在检查:运行环境";
final lastScreenInfo = S.current.doctor_action_info_checking_runtime;
state = state.copyWith(lastScreenInfo: lastScreenInfo);
if (!(Platform.operatingSystemVersion.contains("Windows 10") ||
@ -226,7 +226,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
if (ramSize < 16) {
checkResult.add(MapEntry("low_ram", "$ramSize"));
}
state = state.copyWith(lastScreenInfo: "正在检查:安装信息");
state = state.copyWith(lastScreenInfo: S.current.doctor_action_info_checking_install_info);
//
try {
final listData = await SCLoggerHelper.getGameInstallPath(

View File

@ -147,7 +147,7 @@ class __$$HomeGameDoctorStateImplCopyWithImpl<$Res>
/// @nodoc
class _$HomeGameDoctorStateImpl implements _HomeGameDoctorState {
const _$HomeGameDoctorStateImpl(
_$HomeGameDoctorStateImpl(
{this.isChecking = false,
this.isFixing = false,
this.lastScreenInfo = "",
@ -217,7 +217,7 @@ class _$HomeGameDoctorStateImpl implements _HomeGameDoctorState {
}
abstract class _HomeGameDoctorState implements HomeGameDoctorState {
const factory _HomeGameDoctorState(
factory _HomeGameDoctorState(
{final bool isChecking,
final bool isFixing,
final String lastScreenInfo,

View File

@ -7,7 +7,7 @@ part of 'game_doctor_ui_model.dart';
// **************************************************************************
String _$homeGameDoctorUIModelHash() =>
r'1e32d75095de065cf2cdedf444f74ffc753ce66f';
r'10e8103fca9565ee6363c093e1d0bf2bc9e68f41';
/// See also [HomeGameDoctorUIModel].
@ProviderFor(HomeGameDoctorUIModel)

View File

@ -41,7 +41,7 @@ class HomeUI extends HookConsumerWidget {
action: homeState.appPlacardData?.link == null
? null
: Button(
child: const Text('查看详情'),
child: Text(S.current.doctor_action_view_details),
onPressed: () => _showPlacard(context, homeState),
),
onClose: homeState.appPlacardData?.alwaysShow == true
@ -68,7 +68,7 @@ class HomeUI extends HookConsumerWidget {
const SizedBox(height: 12),
Text(homeState.isFixingString.isNotEmpty
? homeState.isFixingString
: "正在处理..."),
: S.current.doctor_info_processing),
],
),
),
@ -79,7 +79,7 @@ class HomeUI extends HookConsumerWidget {
List<Widget> makeIndex(BuildContext context, HomeUIModel model,
HomeUIModelState homeState, WidgetRef ref) {
const double width = 280;
double width = 280;
return [
Stack(
children: [
@ -122,16 +122,16 @@ class HomeUI extends HookConsumerWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text("安装位置:"),
Text(S.current.home_install_location),
const SizedBox(width: 6),
Expanded(
child: ComboBox<String>(
value: homeState.scInstalledPath,
isExpanded: true,
items: [
const ComboBoxItem(
ComboBoxItem(
value: "not_install",
child: Text("未安装 或 安装失败"),
child: Text(S.current.home_not_installed_or_failed),
),
for (final path in homeState.scInstallPaths)
ComboBoxItem(
@ -207,10 +207,10 @@ class HomeUI extends HookConsumerWidget {
colorFilter: makeSvgColor(Colors.white),
height: 18,
),
name: "星际公民官网汉化",
webTitle: "星际公民官网汉化",
name: S.current.home_action_star_citizen_website_localization,
webTitle: S.current.home_action_star_citizen_website_localization,
webURL: "https://robertsspaceindustries.com",
info: "罗伯茨航天工业公司,万物的起源",
info: S.current.home_action_info_roberts_space_industries_origin,
useLocalization: true,
width: width,
touchKey: "webLocalization_rsi"),
@ -225,10 +225,10 @@ class HomeUI extends HookConsumerWidget {
const SizedBox(width: 12),
],
),
name: "UEX 汉化",
webTitle: "UEX 汉化",
name: S.current.home_action_uex_localization,
webTitle: S.current.home_action_uex_localization,
webURL: "https://uexcorp.space/",
info: "采矿、精炼、贸易计算器、价格、船信息",
info: S.current.home_action_info_mining_refining_trade_calculator,
useLocalization: true,
width: width,
touchKey: "webLocalization_uex"),
@ -243,15 +243,15 @@ class HomeUI extends HookConsumerWidget {
const SizedBox(width: 12),
],
),
name: "DPS计算器汉化",
webTitle: "DPS计算器汉化",
name: S.current.home_action_dps_calculator_localization,
webTitle: S.current.home_action_dps_calculator_localization,
webURL: "https://www.erkul.games/live/calculator",
info: "在线改船,查询伤害数值和配件购买地点",
info: S.current.home_action_info_ship_upgrade_damage_value_query,
useLocalization: true,
width: width,
touchKey: "webLocalization_dps"),
const SizedBox(height: 12),
const Text("外部浏览器拓展:"),
Text(S.current.home_action_external_browser_extension),
const SizedBox(height: 12),
Row(
children: [
@ -434,11 +434,11 @@ class HomeUI extends HookConsumerWidget {
Widget makeIndexActionLists(BuildContext context, HomeUIModel model,
HomeUIModelState homeState, WidgetRef ref) {
final items = [
_HomeItemData("game_doctor", "一键诊断", "一键诊断星际公民常见问题",
_HomeItemData("game_doctor", "一键诊断", S.current.home_action_info_one_click_diagnosis_star_citizen,
FluentIcons.auto_deploy_settings),
_HomeItemData(
"localization", "汉化管理", "快捷安装汉化资源", FluentIcons.locale_language),
_HomeItemData("performance", "性能优化", "调整引擎配置文件,优化游戏性能",
"localization", "汉化管理", S.current.home_action_info_quick_install_localization_resources, FluentIcons.locale_language),
_HomeItemData("performance", "性能优化", S.current.home_action_info_engine_config_optimization,
FluentIcons.process_meta_task),
];
return Padding(
@ -589,11 +589,11 @@ class HomeUI extends HookConsumerWidget {
Widget makeGameStatusCard(BuildContext context, HomeUIModel model,
double width, HomeUIModelState homeState) {
const statusCnName = {
"Platform": "平台",
"Persistent Universe": "持续宇宙",
"Electronic Access": "电子访问",
"Arena Commander": "竞技场指挥官"
final statusCnName = {
"Platform": S.current.home_action_rsi_status_platform,
"Persistent Universe": S.current.home_action_rsi_status_persistent_universe,
"Electronic Access": S.current.home_action_rsi_status_electronic_access,
"Arena Commander": S.current.home_action_rsi_status_arena_commander
};
return Tilt(
@ -601,7 +601,7 @@ class HomeUI extends HookConsumerWidget {
borderRadius: BorderRadius.circular(12),
child: GestureDetector(
onTap: () {
model.goWebView(context, "RSI 服务器状态",
model.goWebView(context, S.current.home_action_rsi_status_rsi_server_status,
"https://status.robertsspaceindustries.com/",
useLocalization: true);
},
@ -619,7 +619,7 @@ class HomeUI extends HookConsumerWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text("状态:"),
Text(S.current.home_action_rsi_status_status),
for (final item in homeState.scServerStatus ?? [])
Row(
children: [
@ -741,7 +741,7 @@ class HomeUI extends HookConsumerWidget {
context: context,
builder: (context) {
return HomeMdContentDialogUI(
title: homeState.appPlacardData?.title ?? "公告详情",
title: homeState.appPlacardData?.title ?? S.current.home_announcement_details,
url: homeState.appPlacardData?.link,
);
});
@ -756,7 +756,7 @@ class HomeUI extends HookConsumerWidget {
_onMenuTap(BuildContext context, String key, HomeUIModelState homeState,
WidgetRef ref) async {
const String gameInstallReqInfo =
String gameInstallReqInfo =
"该功能需要一个有效的安装位置\n\n如果您的游戏未下载完成,请等待下载完毕后使用此功能。\n\n如果您的游戏已下载完毕但未识别,请启动一次游戏后重新打开盒子 或 在设置选项中手动设置安装位置。";
switch (key) {
case "localization":

View File

@ -77,7 +77,8 @@ class HomeUIModel extends _$HomeUIModel {
Future<void> reScanPath() async {
state = state.copyWith(
scInstalledPath: "not_install", lastScreenInfo: "正在扫描 ...");
scInstalledPath: "not_install",
lastScreenInfo: S.current.home_action_info_scanning);
try {
final listData = await SCLoggerHelper.getLauncherLogList();
if (listData == null) {
@ -97,10 +98,11 @@ class HomeUIModel extends _$HomeUIModel {
lastScreenInfo: lastScreenInfo);
} catch (e) {
state = state.copyWith(
scInstalledPath: "not_install", lastScreenInfo: "解析 log 文件失败!");
scInstalledPath: "not_install",
lastScreenInfo: S.current.home_action_info_log_file_parse_fail);
AnalyticsApi.touch("error_launchLogs");
// showToast(context!,
// "解析 log 文件失败! \n请关闭游戏退出RSI启动器后重试若仍有问题请使用工具箱中的 RSI Launcher log 修复。");
// "${S.current.home_action_info_log_file_parse_fail} \n请关闭游戏退出RSI启动器后重试若仍有问题请使用工具箱中的 RSI Launcher log 修复。");
}
}
@ -138,7 +140,7 @@ class HomeUIModel extends _$HomeUIModel {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context,
"星际公民网站汉化",
S.current.home_action_title_star_citizen_website_localization,
const Text(
"本插功能件仅供大致浏览使用,不对任何有关本功能产生的问题负责!在涉及账号操作前请注意确认网站的原本内容!"
"\n\n\n使用此功能登录账号时请确保您的 SC汉化盒子 是从可信任的来源下载。",
@ -157,7 +159,8 @@ class HomeUIModel extends _$HomeUIModel {
}
if (!await WebviewWindow.isWebviewAvailable()) {
if (!context.mounted) return;
showToast(context, "需要安装 WebView2 Runtime");
showToast(context,
S.current.home_login_action_title_need_webview2_runtime);
launchUrlString(
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
return;
@ -166,7 +169,10 @@ class HomeUIModel extends _$HomeUIModel {
final webViewModel = WebViewModel(context,
loginMode: loginMode, loginCallback: rsiLoginCallback);
if (useLocalization) {
state = state.copyWith(isFixing: true, isFixingString: "正在初始化汉化资源...");
state = state.copyWith(
isFixing: true,
isFixingString:
S.current.home_action_info_initializing_resources);
try {
await webViewModel.initLocalization(state.webLocalizationVersionsData!);
} catch (e) {
@ -280,14 +286,14 @@ class HomeUIModel extends _$HomeUIModel {
_appUpdateTimer?.cancel();
_appUpdateTimer = null;
//
final toastNotifier =
ToastNotificationManager.createToastNotifierWithId("SC汉化盒子");
final toastNotifier = ToastNotificationManager.createToastNotifierWithId(
S.current.home_title_app_name);
if (toastNotifier != null) {
final toastContent = ToastNotificationManager.getTemplateContent(
ToastTemplateType.toastText02);
if (toastContent != null) {
final xmlNodeList = toastContent.getElementsByTagName('text');
const title = '汉化有新版本!';
final title = S.current.home_localization_new_version_available;
final content = '您在 ${updates.first} 安装的汉化有新版本啦!';
xmlNodeList.item(0)?.appendChild(toastContent.createTextNode(title));
xmlNodeList
@ -304,7 +310,7 @@ class HomeUIModel extends _$HomeUIModel {
// ignore: avoid_build_context_in_providers
launchRSI(BuildContext context) async {
if (state.scInstalledPath == "not_install") {
showToast(context, "该功能需要一个有效的安装位置");
showToast(context, S.current.home_info_valid_installation_required);
return;
}
@ -322,11 +328,11 @@ class HomeUIModel extends _$HomeUIModel {
} else {
final ok = await showConfirmDialogs(
context,
"一键启动功能提示",
S.current.home_info_one_click_launch_warning,
const Text("为确保账户安全,一键启动功能已在开发版中禁用,我们将在微软商店版本中提供此功能。"
"\n\n微软商店版由微软提供可靠的分发下载与数字签名,可有效防止软件被恶意篡改。\n\n提示:您无需使用盒子启动游戏也可使用汉化。"),
confirm: "安装微软商店版本",
cancel: "取消");
confirm: S.current.home_action_install_microsoft_store_version,
cancel: S.current.home_action_cancel);
if (ok == true) {
await launchUrlString(
"https://apps.microsoft.com/detail/9NF3SWFWNKL1?launch=true");

View File

@ -6,7 +6,7 @@ part of 'home_ui_model.dart';
// RiverpodGenerator
// **************************************************************************
String _$homeUIModelHash() => r'a911826a7b852408123bf4b8999ac80c3c582fd4';
String _$homeUIModelHash() => r'8268fab911b162f2f3f8a5a86449ea15a759569b';
/// See also [HomeUIModel].
@ProviderFor(HomeUIModel)

View File

@ -35,13 +35,14 @@ class LocalizationDialogUI extends HookConsumerWidget {
AnimatedSize(
duration: const Duration(milliseconds: 130),
child: state.patchStatus?.key == true &&
state.patchStatus?.value == "游戏内置"
state.patchStatus?.value ==
S.current.home_action_info_game_built_in
? Padding(
padding: const EdgeInsets.only(bottom: 12),
child: InfoBar(
title: const Text("警告"),
content: const Text(
"您正在使用游戏内置文本官方文本目前为机器翻译截至3.21.0),建议您在下方安装社区汉化。"),
title: Text(S.current.home_action_info_warning),
content: Text(
S.current.localization_info_machine_translation_warning),
severity: InfoBarSeverity.info,
style: InfoBarThemeData(decoration: (severity) {
return const BoxDecoration(
@ -56,7 +57,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
),
),
makeListContainer(
"汉化状态",
S.current.localization_info_translation_status,
[
if (state.patchStatus == null)
makeLoading(context)
@ -80,31 +81,34 @@ class LocalizationDialogUI extends HookConsumerWidget {
children: [
Text("已安装版本:${state.patchStatus?.value}"),
const Spacer(),
if (state.patchStatus?.value != "游戏内置")
if (state.patchStatus?.value !=
S.current.home_action_info_game_built_in)
Row(
children: [
Button(
onPressed: model.goFeedback,
child: const Padding(
padding: EdgeInsets.all(4),
child: Padding(
padding: const EdgeInsets.all(4),
child: Row(
children: [
Icon(FluentIcons.feedback),
SizedBox(width: 6),
Text("汉化反馈"),
const Icon(FluentIcons.feedback),
const SizedBox(width: 6),
Text(
S.current.localization_action_translation_feedback),
],
),
)),
const SizedBox(width: 16),
Button(
onPressed: model.doDelIniFile(),
child: const Padding(
padding: EdgeInsets.all(4),
child: Padding(
padding: const EdgeInsets.all(4),
child: Row(
children: [
Icon(FluentIcons.delete),
SizedBox(width: 6),
Text("卸载汉化"),
const Icon(FluentIcons.delete),
const SizedBox(width: 6),
Text(
S.current.localization_action_uninstall_translation),
],
),
)),
@ -130,9 +134,9 @@ class LocalizationDialogUI extends HookConsumerWidget {
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Text(
"备注:",
style: TextStyle(fontSize: 18),
Text(
S.current.localization_info_note,
style: const TextStyle(fontSize: 18),
),
const SizedBox(height: 6),
Text(
@ -154,14 +158,14 @@ class LocalizationDialogUI extends HookConsumerWidget {
],
context),
makeListContainer(
"社区汉化",
S.current.localization_info_community_translation,
[
if (state.apiLocalizationData == null)
makeLoading(context)
else if (state.apiLocalizationData!.isEmpty)
Center(
child: Text(
"该语言/版本 暂无可用汉化,敬请期待!",
S.current.localization_info_no_translation_available,
style: TextStyle(
fontSize: 13,
color: Colors.white.withOpacity(.8)),
@ -181,7 +185,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
? FluentIcons.chevron_up
: FluentIcons.chevron_down),
const SizedBox(width: 12),
const Text("高级功能"),
Text(
S.current.localization_action_advanced_features),
],
),
onPressed: model.toggleCustomize),
@ -192,14 +197,14 @@ class LocalizationDialogUI extends HookConsumerWidget {
const SizedBox(height: 12),
state.enableCustomize
? makeListContainer(
"自定义文本",
S.current.localization_info_custom_text,
[
if (state.customizeList == null)
makeLoading(context)
else if (state.customizeList!.isEmpty)
Center(
child: Text(
"暂无自定义文本",
S.current.localization_info_no_custom_text,
style: TextStyle(
fontSize: 13,
color: Colors.white.withOpacity(.8)),
@ -222,13 +227,14 @@ class LocalizationDialogUI extends HookConsumerWidget {
Button(
onPressed:
model.doLocalInstall(file),
child: const Padding(
padding: EdgeInsets.only(
child: Padding(
padding: const EdgeInsets.only(
left: 8,
right: 8,
top: 4,
bottom: 4),
child: Text("安装"),
child: Text(
S.current.localization_action_install),
))
],
)
@ -238,13 +244,13 @@ class LocalizationDialogUI extends HookConsumerWidget {
actions: [
Button(
onPressed: () => model.openDir(context),
child: const Padding(
padding: EdgeInsets.all(4),
child: Padding(
padding: const EdgeInsets.all(4),
child: Row(
children: [
Icon(FluentIcons.folder_open),
SizedBox(width: 6),
Text("打开文件夹"),
const Icon(FluentIcons.folder_open),
const SizedBox(width: 6),
Text(S.current.action_open_folder),
],
),
)),
@ -324,8 +330,10 @@ class LocalizationDialogUI extends HookConsumerWidget {
: FluentIcons.disable_updates),
),
Text(isInstalled
? "已安装"
: ((item.value.enable ?? false) ? "安装" : "不可用")),
? S.current.localization_info_installed
: ((item.value.enable ?? false)
? "安装"
: S.current.localization_info_unavailable)),
],
),
)),
@ -396,7 +404,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
),
onPressed: model.onBack(context)),
const SizedBox(width: 12),
const Text("汉化管理"),
Text(S.current.home_action_localization_management),
const SizedBox(width: 24),
Text(
"${model.getScInstallPath()}",
@ -407,9 +415,9 @@ class LocalizationDialogUI extends HookConsumerWidget {
height: 36,
child: Row(
children: [
const Text(
"语言: ",
style: TextStyle(fontSize: 16),
Text(
S.current.localization_info_language,
style: const TextStyle(fontSize: 16),
),
ComboBox<String>(
value: state.selectedLanguage,

View File

@ -26,7 +26,7 @@ part 'localization_ui_model.freezed.dart';
@freezed
class LocalizationUIState with _$LocalizationUIState {
const factory LocalizationUIState({
factory LocalizationUIState({
String? selectedLanguage,
Map<String, ScLocalizationData>? apiLocalizationData,
@Default("") String workingVersion,
@ -38,7 +38,7 @@ class LocalizationUIState with _$LocalizationUIState {
@riverpod
class LocalizationUIModel extends _$LocalizationUIModel {
static const languageSupport = {
static const languageSupport = {
"chinese_(simplified)": "简体中文",
"chinese_(traditional)": "繁體中文",
};
@ -122,7 +122,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
if (!context.mounted) return;
final ok = await showConfirmDialogs(
context,
"是否移除不兼容的汉化参数",
S.current.localization_info_remove_incompatible_translation_params,
const Text(
"USER.cfg 包含不兼容的汉化参数,这可能是以前的汉化文件的残留信息。\n\n这将可能导致汉化无效或乱码,点击确认为您一键移除(不会影响其他配置)。"),
constraints: BoxConstraints(
@ -288,7 +288,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
// check file
final globalIni = await compute(_readArchive, savePath.absolute.path);
if (globalIni.isEmpty) {
throw "文件受损,请重新下载";
throw S.current.localization_info_corrupted_file;
}
await _installFormString(globalIni, value.versionName ?? "");
} catch (e) {
@ -371,7 +371,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
static Future<String> _getInstalledIniVersion(String iniPath) async {
final iniFile = File(iniPath);
if (!await iniFile.exists()) return "游戏内置";
if (!await iniFile.exists()) return S.current.home_action_info_game_built_in;
final iniStringSplit = (await iniFile.readAsString()).split("\n");
for (var i = iniStringSplit.length - 1; i > 0; i--) {
if (iniStringSplit[i]
@ -382,7 +382,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
return v;
}
}
return "自定义文件";
return S.current.localization_info_custom_files;
}
Future<List<String>> checkLangUpdate({bool skipReload = false}) async {
@ -408,7 +408,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
if (element.path.contains(lang)) {
final installedVersion =
await _getInstalledIniVersion("${element.path}\\global.ini");
if (installedVersion == "游戏内置") continue;
if (installedVersion == S.current.home_action_info_game_built_in) continue;
final curData = _allVersionLocalizationData[lang];
dPrint("check Localization update $scInstallPath");
if (!(curData?.keys.contains(installedVersion) ?? false)) {

View File

@ -160,7 +160,7 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res>
/// @nodoc
class _$LocalizationUIStateImpl implements _LocalizationUIState {
const _$LocalizationUIStateImpl(
_$LocalizationUIStateImpl(
{this.selectedLanguage,
final Map<String, ScLocalizationData>? apiLocalizationData,
this.workingVersion = "",
@ -245,7 +245,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
}
abstract class _LocalizationUIState implements LocalizationUIState {
const factory _LocalizationUIState(
factory _LocalizationUIState(
{final String? selectedLanguage,
final Map<String, ScLocalizationData>? apiLocalizationData,
final String workingVersion,

View File

@ -7,7 +7,7 @@ part of 'localization_ui_model.dart';
// **************************************************************************
String _$localizationUIModelHash() =>
r'654fd38b5f38bee5fd2cab69ab003846a311a4ff';
r'33c538abcdbfd667e844d0dcb5b8b7d6264794ab';
/// See also [LocalizationUIModel].
@ProviderFor(LocalizationUIModel)

View File

@ -30,9 +30,10 @@ class HomePerformanceUI extends HookConsumerWidget {
children: [
if (state.showGraphicsPerformanceTip)
InfoBar(
title: const Text("图形优化提示"),
content: const Text(
"该功能对优化显卡瓶颈有很大帮助,但对 CPU 瓶颈可能起反效果,如果您显卡性能强劲,可以尝试使用更好的画质来获得更高的显卡利用率。",
title: Text(
S.current.performance_info_graphic_optimization_hint),
content: Text(
S.current.performance_info_graphic_optimization_warning,
),
onClose: () => model.closeTip(),
),
@ -44,15 +45,15 @@ class HomePerformanceUI extends HookConsumerWidget {
style: const TextStyle(fontSize: 18),
),
const SizedBox(width: 32),
const Text(
"预设:",
style: TextStyle(fontSize: 18),
Text(
S.current.performance_action_preset,
style: const TextStyle(fontSize: 18),
),
for (final item in const {
"low": "",
"medium": "",
"high": "",
"ultra": "超级"
for (final item in {
"low": S.current.performance_action_low,
"medium": S.current.performance_action_medium,
"high": S.current.performance_action_high,
"ultra": S.current.performance_action_super
}.entries)
Padding(
padding: const EdgeInsets.only(left: 6, right: 6),
@ -65,7 +66,8 @@ class HomePerformanceUI extends HookConsumerWidget {
onPressed: () =>
model.onChangePreProfile(item.key)),
),
const Text("(预设只修改图形设置)"),
Text(
S.current.performance_action_info_preset_only_changes_graphics),
const Spacer(),
Button(
onPressed: () => model.refresh(),
@ -76,23 +78,23 @@ class HomePerformanceUI extends HookConsumerWidget {
),
const SizedBox(width: 12),
Button(
child: const Text(
" 恢复默认 ",
style: TextStyle(fontSize: 16),
child: Text(
S.current.performance_action_reset_to_default,
style: const TextStyle(fontSize: 16),
),
onPressed: () => model.clean(context)),
const SizedBox(width: 24),
Button(
child: const Text(
"应用",
style: TextStyle(fontSize: 16),
child: Text(
S.current.performance_action_apply,
style: const TextStyle(fontSize: 16),
),
onPressed: () => model.applyProfile(false)),
const SizedBox(width: 6),
Button(
child: const Text(
"应用并清理着色器(推荐)",
style: TextStyle(fontSize: 16),
child: Text(
S.current.performance_action_apply_and_clear_shaders,
style: const TextStyle(fontSize: 16),
),
onPressed: () => model.applyProfile(true)),
],

View File

@ -11,6 +11,7 @@ import 'package:starcitizen_doctor/api/analytics.dart';
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
import 'package:starcitizen_doctor/data/game_performance_data.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
part 'performance_ui_model.freezed.dart';
@ -19,7 +20,7 @@ part 'performance_ui_model.g.dart';
@freezed
class HomePerformanceUIState with _$HomePerformanceUIState {
const factory HomePerformanceUIState({
factory HomePerformanceUIState({
@Default(true) bool showGraphicsPerformanceTip,
@Default(false) bool enabled,
Map<String, List<GamePerformanceData>>? performanceMap,
@ -41,7 +42,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
@override
HomePerformanceUIState build() {
state = const HomePerformanceUIState();
state = HomePerformanceUIState();
_init();
return state;
}
@ -111,7 +112,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
switch (key) {
case "low":
state.performanceMap?.forEach((key, v) {
if (key.contains("图形")) {
if (key.contains(S.current.performance_info_graphics)) {
for (var element in v) {
element.value = element.min;
}
@ -120,7 +121,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
break;
case "medium":
state.performanceMap?.forEach((key, v) {
if (key.contains("图形")) {
if (key.contains(S.current.performance_info_graphics)) {
for (var element in v) {
element.value = ((element.max ?? 0) ~/ 2);
}
@ -129,7 +130,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
break;
case "high":
state.performanceMap?.forEach((key, v) {
if (key.contains("图形")) {
if (key.contains(S.current.performance_info_graphics)) {
for (var element in v) {
element.value = ((element.max ?? 0) / 1.5).ceil();
}
@ -138,7 +139,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
break;
case "ultra":
state.performanceMap?.forEach((key, v) {
if (key.contains("图形")) {
if (key.contains(S.current.performance_info_graphics)) {
for (var element in v) {
element.value = element.max;
}
@ -154,14 +155,14 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
}
clean(BuildContext context) async {
state = state.copyWith(workingString: "删除配置文件...");
state = state.copyWith(workingString: S.current.performance_info_delete_config_file);
if (await confFile.exists()) {
await confFile.delete(recursive: true);
}
state = state.copyWith(workingString: "清理着色器");
state = state.copyWith(workingString: S.current.performance_action_clear_shaders);
if (!context.mounted) return;
await cleanShaderCache(context);
state = state.copyWith(workingString: "完成...");
state = state.copyWith(workingString: S.current.performance_info_done);
await await Future.delayed(const Duration(milliseconds: 300));
await _init();
state = state.copyWith(workingString: "");
@ -180,14 +181,14 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
}
await Future.delayed(const Duration(milliseconds: 300));
if (context != null && context.mounted) {
showToast(context, "清理着色器后首次进入游戏可能会出现卡顿,请耐心等待游戏初始化完毕。");
showToast(context, S.current.performance_info_shader_clearing_warning);
}
}
applyProfile(bool cleanShader) async {
if (state.performanceMap == null) return;
AnalyticsApi.touch("performance_apply");
state = state.copyWith(workingString: "生成配置文件");
state = state.copyWith(workingString: S.current.performance_info_generate_config_file);
String conf = "";
for (var v in state.performanceMap!.entries) {
for (var c in v.value) {
@ -206,20 +207,20 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
}
}
}
state = state.copyWith(workingString: "写出配置文件");
state = state.copyWith(workingString: S.current.performance_info_write_out_config_file);
if (await confFile.exists()) {
await confFile.delete();
}
await confFile.create();
await confFile.writeAsString(conf);
if (cleanShader) {
state = state.copyWith(workingString: "清理着色器");
state = state.copyWith(workingString: S.current.performance_action_clear_shaders);
await cleanShaderCache(null);
}
state = state.copyWith(workingString: "完成...");
state = state.copyWith(workingString: S.current.performance_info_done);
await await Future.delayed(const Duration(milliseconds: 300));
await _init();
state = state.copyWith(workingString: "清理着色器");
state = state.copyWith(workingString: S.current.performance_action_clear_shaders);
}
updateState() {

View File

@ -138,7 +138,7 @@ class __$$HomePerformanceUIStateImplCopyWithImpl<$Res>
/// @nodoc
class _$HomePerformanceUIStateImpl implements _HomePerformanceUIState {
const _$HomePerformanceUIStateImpl(
_$HomePerformanceUIStateImpl(
{this.showGraphicsPerformanceTip = true,
this.enabled = false,
final Map<String, List<GamePerformanceData>>? performanceMap,
@ -203,7 +203,7 @@ class _$HomePerformanceUIStateImpl implements _HomePerformanceUIState {
}
abstract class _HomePerformanceUIState implements HomePerformanceUIState {
const factory _HomePerformanceUIState(
factory _HomePerformanceUIState(
{final bool showGraphicsPerformanceTip,
final bool enabled,
final Map<String, List<GamePerformanceData>>? performanceMap,

View File

@ -7,7 +7,7 @@ part of 'performance_ui_model.dart';
// **************************************************************************
String _$homePerformanceUIModelHash() =>
r'85e3390e954b35ffeb7cbacf85619b5a61f866bb';
r'0519b95b68b4bffcd940513fa800654c81da2502';
/// See also [HomePerformanceUIModel].
@ProviderFor(HomePerformanceUIModel)

View File

@ -87,11 +87,11 @@ class IndexUI extends HookConsumerWidget {
}
Map<IconData, String> get pageMenus => {
FluentIcons.home: "首页",
FluentIcons.game: "大厅",
FluentIcons.toolbox: "工具",
FluentIcons.settings: "设置",
FluentIcons.info: "关于",
FluentIcons.home: S.current.app_index_menu_home,
FluentIcons.game: S.current.app_index_menu_lobby,
FluentIcons.toolbox: S.current.app_index_menu_tools,
FluentIcons.settings: S.current.app_index_menu_settings,
FluentIcons.info: S.current.app_index_menu_about,
};
List<NavigationPaneItem> getNavigationPaneItems(

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
import 'package:url_launcher/url_launcher_string.dart';
class PartyRoomUI extends HookConsumerWidget {
@ -11,22 +12,22 @@ class PartyRoomUI extends HookConsumerWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"联机大厅,敬请期待 ",
style: TextStyle(fontSize: 20),
Text(
S.current.lobby_online_lobby_coming_soon,
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 12),
GestureDetector(
onTap: () {
launchUrlString("https://wj.qq.com/s2/14112124/f4c8/");
},
child: const Row(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("诚邀您参与 "),
Text(S.current.lobby_invitation_to_participate),
Text(
"问卷调查。",
style: TextStyle(
S.current.lobby_survey,
style: const TextStyle(
color: Colors.blue,
),
)

View File

@ -2,6 +2,7 @@ import 'package:fluent_ui/fluent_ui.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
import 'package:starcitizen_doctor/ui/settings/settings_ui_model.dart';
class SettingsUI extends HookConsumerWidget {
@ -17,59 +18,59 @@ class SettingsUI extends HookConsumerWidget {
margin: const EdgeInsets.all(16),
child: Column(
children: [
makeSettingsItem(const Icon(FluentIcons.link, size: 20), "创建设置快捷方式",
subTitle: "在桌面创建《SC汉化盒子》快捷方式", onTap: ()=> model.addShortCut(context)),
makeSettingsItem(const Icon(FluentIcons.link, size: 20), S.current.setting_action_create_settings_shortcut,
subTitle: S.current.setting_action_create_desktop_shortcut, onTap: ()=> model.addShortCut(context)),
if (ConstConf.isMSE) ...[
const SizedBox(height: 12),
makeSettingsItem(
const Icon(FluentIcons.reset_device, size: 20), "重置自动密码填充",
const Icon(FluentIcons.reset_device, size: 20), S.current.setting_action_reset_auto_password_fill,
subTitle:
"启用:${sate.isEnableAutoLogin ? "已启用" : "已禁用"} 设备支持:${sate.isDeviceSupportWinHello ? "支持" : "不支持"} 邮箱:${sate.autoLoginEmail} 密码:${sate.isEnableAutoLoginPwd ? "已加密保存" : "未保存"}",
onTap: ()=> model.onResetAutoLogin(context)),
],
const SizedBox(height: 12),
makeSettingsItem(const Icon(FontAwesomeIcons.microchip, size: 20),
"启动游戏时忽略能效核心( 适用于Intel 12th+ 处理器 ",
S.current.setting_action_ignore_efficiency_cores_on_launch,
subTitle:
"已设置的核心数量:${sate.inputGameLaunchECore} (此功能适用于首页的盒子一键启动 或 工具中的RSI启动器管理员模式当为 0 时不启用此功能 ",
onTap:()=> model.setGameLaunchECore(context)),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.folder_open, size: 20),
"设置启动器文件RSI Launcher.exe",
S.current.setting_action_set_launcher_file,
subTitle: sate.customLauncherPath != null
? "${sate.customLauncherPath}"
: "手动设置启动器位置,建议仅在无法自动扫描安装位置时使用",
: S.current.setting_action_info_manual_launcher_location_setting,
onTap: ()=> model.setLauncherPath(context), onDel: () {
model.delName("custom_launcher_path");
}),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.game, size: 20),
"设置游戏文件 StarCitizen.exe",
S.current.setting_action_set_game_file,
subTitle: sate.customGamePath != null
? "${sate.customGamePath}"
: "手动设置游戏安装位置,建议仅在无法自动扫描安装位置时使用",
: S.current.setting_action_info_manual_game_location_setting,
onTap: ()=> model.setGamePath(context), onDel: () {
model.delName("custom_game_path");
}),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.delete, size: 20), "清理汉化文件缓存",
makeSettingsItem(const Icon(FluentIcons.delete, size: 20), S.current.setting_action_clear_translation_file_cache,
subTitle:
"缓存大小 ${(sate.locationCacheSize / 1024 / 1024).toStringAsFixed(2)}MB清理盒子下载的汉化文件缓存不会影响已安装的汉化",
onTap: ()=> model.cleanLocationCache(context)),
const SizedBox(height: 12),
makeSettingsItem(
const Icon(FluentIcons.speed_high, size: 20), "工具站访问加速",
const Icon(FluentIcons.speed_high, size: 20), S.current.setting_action_tool_site_access_acceleration,
onTap: () =>
model.onChangeToolSiteMirror(!sate.isEnableToolSiteMirrors),
subTitle:
"使用镜像服务器加速访问 Dps Uex 等工具网站,若访问异常请关闭该功能。 为保护账户安全任何情况下都不会加速RSI官网。",
S.current.setting_action_info_mirror_server_info,
onSwitch: model.onChangeToolSiteMirror,
switchStatus: sate.isEnableToolSiteMirrors),
const SizedBox(height: 12),
makeSettingsItem(
const Icon(FluentIcons.document_set, size: 20), "查看log",
const Icon(FluentIcons.document_set, size: 20), S.current.setting_action_view_log,
onTap: () => model.showLogs(),
subTitle: "查看汉化盒子的 log 文件,以定位盒子的 bug"),
subTitle: S.current.setting_action_info_view_log_file),
],
),
);

View File

@ -21,7 +21,7 @@ part 'settings_ui_model.freezed.dart';
@freezed
class SettingsUIState with _$SettingsUIState {
const factory SettingsUIState({
factory SettingsUIState({
@Default(false) isDeviceSupportWinHello,
@Default("-") String autoLoginEmail,
@Default(false) bool isEnableAutoLogin,
@ -38,7 +38,7 @@ class SettingsUIState with _$SettingsUIState {
class SettingsUIModel extends _$SettingsUIModel {
@override
SettingsUIState build() {
state = const SettingsUIState();
state = SettingsUIState();
_initState();
return state;
}
@ -57,15 +57,15 @@ class SettingsUIModel extends _$SettingsUIModel {
}
Future<void> onResetAutoLogin(BuildContext context) async {
final ok = await showConfirmDialogs(context, "确认重置自动填充?",
const Text("这将会删除本地的账号记录,或在下次启动游戏时将自动填充选择 ‘否’ 以禁用自动填充。"));
final ok = await showConfirmDialogs(context, S.current.setting_action_info_confirm_reset_autofill,
Text(S.current.setting_action_info_delete_local_account_warning));
if (ok) {
final userBox = await Hive.openBox("rsi_account_data");
await userBox.deleteFromDisk();
Win32Credentials.delete("SCToolbox_RSI_Account_secret");
if (!context.mounted) return;
showToast(context, "已清理自动填充数据");
showToast(context, S.current.setting_action_info_autofill_data_cleared);
_initState();
}
}
@ -89,7 +89,7 @@ class SettingsUIModel extends _$SettingsUIModel {
userBox.get("gameLaunch_eCore_count", defaultValue: "0");
if (!context.mounted) return;
final input = await showInputDialogs(context,
title: "请输入要忽略的 CPU 核心数",
title: S.current.setting_action_info_enter_cpu_core_to_ignore,
content:
"Tip您的设备拥有几个能效核心就输入几非大小核设备请保持 0\n\n此功能适用于首页的盒子一键启动 或 工具中的 RSI启动器管理员模式当为 0 时不启用此功能。",
initialValue: defaultInput,
@ -108,7 +108,7 @@ class SettingsUIModel extends _$SettingsUIModel {
Future<void> setLauncherPath(BuildContext context) async {
final r = await FilePicker.platform.pickFiles(
dialogTitle: "请选择RSI启动器位置RSI Launcher.exe",
dialogTitle: S.current.setting_action_info_select_rsi_launcher_location,
type: FileType.custom,
allowedExtensions: ["exe"]);
if (r == null || r.files.firstOrNull?.path == null) return;
@ -116,17 +116,17 @@ class SettingsUIModel extends _$SettingsUIModel {
if (fileName.endsWith("\\RSI Launcher.exe")) {
await _saveCustomPath("custom_launcher_path", fileName);
if (!context.mounted) return;
showToast(context, "设置成功,在对应页面点击刷新即可扫描出新路径");
showToast(context, S.current.setting_action_info_setting_success);
_initState();
} else {
if (!context.mounted) return;
showToast(context, "文件有误!");
showToast(context, S.current.setting_action_info_file_error);
}
}
Future<void> setGamePath(BuildContext context) async {
final r = await FilePicker.platform.pickFiles(
dialogTitle: "请选择游戏安装位置StarCitizen.exe",
dialogTitle: S.current.setting_action_info_select_game_install_location,
type: FileType.custom,
allowedExtensions: ["exe"]);
if (r == null || r.files.firstOrNull?.path == null) return;
@ -139,11 +139,11 @@ class SettingsUIModel extends _$SettingsUIModel {
String extractedPath = fileName.replaceFirst(pathRegex, '');
await _saveCustomPath("custom_game_path", extractedPath);
if (!context.mounted) return;
showToast(context, "设置成功,在对应页面点击刷新即可扫描出新路径");
showToast(context, S.current.setting_action_info_setting_success);
_initState();
} else {
if (!context.mounted) return;
showToast(context, "文件有误!");
showToast(context, S.current.setting_action_info_file_error);
}
}
@ -175,7 +175,7 @@ class SettingsUIModel extends _$SettingsUIModel {
Future<void> cleanLocationCache(BuildContext context) async {
final ok = await showConfirmDialogs(
context, "确认清理汉化缓存?", const Text("这不会影响已安装的汉化。"));
context, "确认清理汉化缓存?", Text(S.current.setting_action_info_clear_cache_warning));
if (ok == true) {
final dir =
Directory("${appGlobalState.applicationSupportDir}/Localizations");
@ -187,7 +187,7 @@ class SettingsUIModel extends _$SettingsUIModel {
Future<void> addShortCut(BuildContext context) async {
if (ConstConf.isMSE) {
showToast(context, "因微软版功能限制,请在接下来打开的窗口中 手动将《SC汉化盒子》拖动到桌面即可创建快捷方式。");
showToast(context, S.current.setting_action_info_microsoft_version_limitation);
await Future.delayed(const Duration(seconds: 1));
Process.run("explorer.exe", ["shell:AppsFolder"]);
return;
@ -208,7 +208,7 @@ class SettingsUIModel extends _$SettingsUIModel {
""";
await Process.run(SystemHelper.powershellPath, [script]);
if (!context.mounted) return;
showToast(context, "创建完毕,请返回桌面查看");
showToast(context, S.current.setting_action_info_shortcut_created);
}
_loadToolSiteMirrorState() async {

View File

@ -197,7 +197,7 @@ class __$$SettingsUIStateImplCopyWithImpl<$Res>
/// @nodoc
class _$SettingsUIStateImpl implements _SettingsUIState {
const _$SettingsUIStateImpl(
_$SettingsUIStateImpl(
{this.isDeviceSupportWinHello = false,
this.autoLoginEmail = "-",
this.isEnableAutoLogin = false,
@ -287,7 +287,7 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
}
abstract class _SettingsUIState implements SettingsUIState {
const factory _SettingsUIState(
factory _SettingsUIState(
{final dynamic isDeviceSupportWinHello,
final String autoLoginEmail,
final bool isEnableAutoLogin,

View File

@ -6,7 +6,7 @@ part of 'settings_ui_model.dart';
// RiverpodGenerator
// **************************************************************************
String _$settingsUIModelHash() => r'34ac24f658a081350be7d2b3bda810d101b888a1';
String _$settingsUIModelHash() => r'de69e289c526d61a6287a9ca7bae848a4d594c20';
/// See also [SettingsUIModel].
@ProviderFor(SettingsUIModel)

View File

@ -59,12 +59,12 @@ class UpgradeDialogUI extends HookConsumerWidget {
mainAxisSize: MainAxisSize.min,
children: [
if (description.value == null) ...[
const Center(
Center(
child: Column(
children: [
ProgressRing(),
SizedBox(height: 16),
Text("正在获取新版本详情...")
const ProgressRing(),
const SizedBox(height: 16),
Text(S.current.app_upgrade_info_getting_new_version_details)
],
),
)
@ -85,7 +85,7 @@ class UpgradeDialogUI extends HookConsumerWidget {
color: Colors.white.withOpacity(.1),
borderRadius: BorderRadius.circular(7)),
child: Text(
"提示:当前正在使用分流服务器进行更新,可能会出现下载速度下降,但有助于我们进行成本控制,若下载异常请点击这里跳转手动安装。",
S.current.app_upgrade_info_update_server_tip,
style: TextStyle(
fontSize: 14, color: Colors.white.withOpacity(.7)),
),
@ -97,7 +97,7 @@ class UpgradeDialogUI extends HookConsumerWidget {
Row(
children: [
Text(progress.value == 100
? "正在安装: "
? S.current.app_upgrade_info_installing
: "正在下载: ${progress.value.toStringAsFixed(2)}% "),
Expanded(
child: ProgressBar(
@ -122,18 +122,18 @@ class UpgradeDialogUI extends HookConsumerWidget {
description,
isUsingDiversion,
progress),
child: const Padding(
padding: EdgeInsets.only(
child: Padding(
padding: const EdgeInsets.only(
top: 4, bottom: 4, left: 8, right: 8),
child: Text("立即更新"),
child: Text(S.current.app_upgrade_action_update_now),
)),
if (ConstConf.appVersionCode >= (minVersionCode ?? 0))
Button(
onPressed: () => _doCancel(context),
child: const Padding(
padding: EdgeInsets.only(
child: Padding(
padding: const EdgeInsets.only(
top: 4, bottom: 4, left: 8, right: 8),
child: Text("下次吧"),
child: Text(S.current.app_upgrade_action_next_time),
)),
],
),
@ -240,7 +240,7 @@ class UpgradeDialogUI extends HookConsumerWidget {
isUpgrading.value = false;
progress.value = 0;
if (!context.mounted) return;
showToast(context, "下载失败,请尝试手动安装!");
showToast(context, S.current.app_upgrade_info_download_failed);
return;
}
@ -255,7 +255,7 @@ class UpgradeDialogUI extends HookConsumerWidget {
isUpgrading.value = false;
progress.value = 0;
if (!context.mounted) return;
showToast(context, "运行失败,请尝试手动安装!");
showToast(context, S.current.app_upgrade_info_run_failed);
Process.run(SystemHelper.powershellPath,
["explorer.exe", "/select,\"$fileName\""]);
}

View File

@ -22,7 +22,7 @@ class SplashUI extends HookConsumerWidget {
final appModel = ref.read(appGlobalModelProvider.notifier);
_initApp(context, appModel, stepState, ref);
return null;
}, const []);
}, []);
return makeDefaultPage(context,
content: Center(
@ -33,9 +33,9 @@ class SplashUI extends HookConsumerWidget {
const SizedBox(height: 32),
const ProgressRing(),
const SizedBox(height: 32),
if (step == 0) const Text("正在检测可用性,这可能需要一点时间..."),
if (step == 1) const Text("正在检查更新..."),
if (step == 2) const Text("即将完成..."),
if (step == 0) Text(S.current.app_splash_checking_availability),
if (step == 1) Text(S.current.app_splash_checking_for_updates),
if (step == 2) Text(S.current.app_splash_almost_done),
],
),
),

View File

@ -12,11 +12,11 @@ import 'package:starcitizen_doctor/common/utils/log.dart';
class HostsBoosterDialogUI extends HookConsumerWidget {
const HostsBoosterDialogUI({super.key});
static const _hostsMap = {
static final _hostsMap = {
"Recaptcha": ["www.recaptcha.net", "recaptcha.net"],
"RSI 官网": ["robertsspaceindustries.com"],
"RSI Zendesk 客服站": ["cloudimperiumservicesllc.zendesk.com"],
"RSI 客服站": ["support.robertsspaceindustries.com"],
S.current.tools_hosts_info_rsi_official_website: ["robertsspaceindustries.com"],
S.current.tools_hosts_info_rsi_zendesk: ["cloudimperiumservicesllc.zendesk.com"],
S.current.tools_hosts_info_rsi_customer_service: ["support.robertsspaceindustries.com"],
};
@override
@ -37,12 +37,12 @@ class HostsBoosterDialogUI extends HookConsumerWidget {
checkedMap.value = Map.from(checkedMap.value);
}
}
workingText.value = "正在查询 DNS 并测试可访问性 请耐心等待...";
workingText.value = S.current.tools_hosts_info_dns_query_and_test;
final ipsMap = await _doCheckDns(workingMap, checkedMap);
workingText.value = "正在写入 Hosts ...";
workingText.value = S.current.tools_hosts_info_writing_hosts;
if (!context.mounted) return;
await _doWriteHosts(ipsMap).unwrap(context: context);
workingText.value = "读取配置 ...";
workingText.value = S.current.tools_hosts_info_reading_config;
await _readHostsState(workingMap, checkedMap);
workingText.value = "";
}
@ -51,7 +51,7 @@ class HostsBoosterDialogUI extends HookConsumerWidget {
// Hosts
_readHostsState(workingMap, checkedMap);
return null;
}, const []);
}, []);
return ContentDialog(
constraints:
@ -66,17 +66,17 @@ class HostsBoosterDialogUI extends HookConsumerWidget {
onPressed:
workingText.value.isEmpty ? Navigator.of(context).pop : null),
const SizedBox(width: 12),
const Text("Hosts 加速"),
Text(S.current.tools_hosts_info_hosts_acceleration),
const Spacer(),
Button(
onPressed: () => _openHostsFile(context),
child: const Padding(
padding: EdgeInsets.all(3),
child: Padding(
padding: const EdgeInsets.all(3),
child: Row(
children: [
Icon(FluentIcons.open_file),
SizedBox(width: 6),
Text("打开 Hosts 文件"),
const Icon(FluentIcons.open_file),
const SizedBox(width: 6),
Text(S.current.tools_hosts_info_open_hosts_file),
],
),
))
@ -88,15 +88,15 @@ class HostsBoosterDialogUI extends HookConsumerWidget {
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 12),
const Row(
Row(
children: [
SizedBox(width: 12),
Text("状态"),
SizedBox(width: 38),
Text("站点"),
Spacer(),
Text("是否启用"),
SizedBox(width: 12),
const SizedBox(width: 12),
Text(S.current.tools_hosts_info_status),
const SizedBox(width: 38),
Text(S.current.tools_hosts_info_site),
const Spacer(),
Text(S.current.tools_hosts_info_enable),
const SizedBox(width: 12),
],
),
const SizedBox(height: 12),
@ -163,10 +163,10 @@ class HostsBoosterDialogUI extends HookConsumerWidget {
padding: const EdgeInsets.all(12),
child: FilledButton(
onPressed: () => doHost(context),
child: const Padding(
child: Padding(
padding:
EdgeInsets.only(top: 3, bottom: 3, left: 12, right: 12),
child: Text("一键加速"),
const EdgeInsets.only(top: 3, bottom: 3, left: 12, right: 12),
child: Text(S.current.tools_hosts_action_one_click_acceleration),
),
),
),
@ -177,7 +177,7 @@ class HostsBoosterDialogUI extends HookConsumerWidget {
}
Future<void> _openHostsFile(BuildContext context) async {
// 使 Hosts
// 使${S.current.tools_hosts_info_open_hosts_file}
Process.run(SystemHelper.powershellPath, [
"-Command",
"Start-Process notepad.exe -Verb runAs -ArgumentList ${SystemHelper.getHostsFilePath()}"

View File

@ -55,14 +55,14 @@ class ToolsUI extends HookConsumerWidget {
),
const SizedBox(height: 12),
if (state.items.isEmpty)
const Expanded(
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ProgressRing(),
SizedBox(height: 12),
Text("正在扫描..."),
const ProgressRing(),
const SizedBox(height: 12),
Text(S.current.tools_info_scanning),
],
),
),
@ -170,13 +170,13 @@ class ToolsUI extends HookConsumerWidget {
decoration: BoxDecoration(
color: Colors.black.withAlpha(150),
),
child: const Center(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ProgressRing(),
SizedBox(height: 12),
Text("正在处理..."),
const ProgressRing(),
const SizedBox(height: 12),
Text(S.current.doctor_info_processing),
],
),
),
@ -190,7 +190,7 @@ class ToolsUI extends HookConsumerWidget {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text("游戏安装位置: "),
Text(S.current.tools_info_game_install_location),
const SizedBox(width: 6),
Expanded(
child: SizedBox(
@ -228,7 +228,7 @@ class ToolsUI extends HookConsumerWidget {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text("RSI启动器位置"),
Text(S.current.tools_info_rsi_launcher_location),
const SizedBox(width: 6),
Expanded(
child: SizedBox(

View File

@ -41,7 +41,7 @@ class ToolsItemData {
@freezed
class ToolsUIState with _$ToolsUIState {
const factory ToolsUIState({
factory ToolsUIState({
@Default(false) bool working,
@Default("") String scInstalledPath,
@Default("") String rsiLauncherInstalledPath,
@ -56,7 +56,7 @@ class ToolsUIState with _$ToolsUIState {
class ToolsUIModel extends _$ToolsUIModel {
@override
ToolsUIState build() {
state = const ToolsUIState();
state = ToolsUIState();
return state;
}
@ -71,35 +71,35 @@ class ToolsUIModel extends _$ToolsUIModel {
items = [
ToolsItemData(
"systemnfo",
"查看系统信息",
S.current.tools_action_view_system_info,
"查看系统关键信息,用于快速问诊 \n\n耗时操作,请耐心等待。",
const Icon(FluentIcons.system, size: 24),
onTap: () => _showSystemInfo(context),
),
ToolsItemData(
"p4k_downloader",
"P4K 分流下载 / 修复",
S.current.tools_action_p4k_download_repair,
"使用星际公民中文百科提供的分流下载服务,可用于下载或修复 p4k。 \n资源有限,请勿滥用。",
const Icon(FontAwesomeIcons.download, size: 24),
onTap: () => _downloadP4k(context),
),
ToolsItemData(
"hosts_booster",
"Hosts 加速(实验性)",
S.current.tools_action_hosts_acceleration_experimental,
"将 IP 信息写入 Hosts 文件,解决部分地区的 DNS 污染导致无法登录官网等问题。\n该功能正在进行第一阶段测试,遇到问题请及时反馈。",
const Icon(FluentIcons.virtual_network, size: 24),
onTap: () => _doHostsBooster(context),
),
ToolsItemData(
"reinstall_eac",
"重装 EasyAntiCheat 反作弊",
"若您遇到 EAC 错误,且自动修复无效,请尝试使用此功能重装 EAC。",
S.current.tools_action_reinstall_easyanticheat,
S.current.tools_action_info_reinstall_eac,
const Icon(FluentIcons.game, size: 24),
onTap: () => _reinstallEAC(context),
),
ToolsItemData(
"rsilauncher_admin_mode",
"RSI Launcher 管理员模式",
S.current.tools_action_rsi_launcher_admin_mode,
"以管理员身份运行RSI启动器可能会解决一些问题。\n\n若设置了能效核心屏蔽参数,也会在此应用。",
const Icon(FluentIcons.admin, size: 24),
onTap: () => _adminRSILauncher(context),
@ -136,7 +136,7 @@ class ToolsUIModel extends _$ToolsUIModel {
return [
ToolsItemData(
"rsilauncher_log_fix",
"RSI Launcher Log 修复",
S.current.tools_action_rsi_launcher_log_fix,
"在某些情况下 RSI启动器 的 log 文件会损坏,导致无法完成问题扫描,使用此工具清理损坏的 log 文件。\n\n当前日志文件大小:${(logPathLen.toStringAsFixed(4))} MB",
const Icon(FontAwesomeIcons.bookBible, size: 24),
onTap: () => _rsiLogFix(context),
@ -150,7 +150,7 @@ class ToolsUIModel extends _$ToolsUIModel {
if (nvmePatchStatus)
ToolsItemData(
"remove_nvme_settings",
"移除 nvme 注册表补丁",
S.current.tools_action_remove_nvme_registry_patch,
"若您使用 nvme 补丁出现问题,请运行此工具。(可能导致游戏 安装/更新 不可用。)\n\n当前补丁状态:${(nvmePatchStatus) ? "已安装" : "未安装"}",
const Icon(FluentIcons.hard_drive, size: 24),
onTap: nvmePatchStatus
@ -159,7 +159,7 @@ class ToolsUIModel extends _$ToolsUIModel {
await SystemHelper.doRemoveNvmePath();
state = state.copyWith(working: false);
if (!context.mounted) return;
showToast(context, "已移除,重启电脑生效!");
showToast(context, S.current.tools_action_info_removed_restart_effective);
loadToolsCard(context, skipPathScan: true);
}
: null,
@ -167,8 +167,8 @@ class ToolsUIModel extends _$ToolsUIModel {
if (!nvmePatchStatus)
ToolsItemData(
"add_nvme_settings",
"写入 nvme 注册表补丁",
"手动写入NVM补丁该功能仅在您知道自己在作什么的情况下使用",
S.current.tools_action_write_nvme_registry_patch,
S.current.tools_action_info_manual_nvme_patch,
const Icon(FontAwesomeIcons.cashRegister, size: 24),
onTap: () async {
state = state.copyWith(working: true);
@ -176,7 +176,7 @@ class ToolsUIModel extends _$ToolsUIModel {
if (r == "") {
if (!context.mounted) return;
showToast(context,
"修复成功,请尝试重启电脑后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
S.current.tools_action_info_fix_success_restart);
} else {
if (!context.mounted) return;
showToast(context, "修复失败,$r");
@ -192,7 +192,7 @@ class ToolsUIModel extends _$ToolsUIModel {
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
return ToolsItemData(
"clean_shaders",
"清理着色器缓存",
S.current.tools_action_clear_shader_cache,
"若游戏画面出现异常或版本更新后可使用本工具清理过期的着色器当大于500M时建议清理 \n\n缓存大小:${((await SystemHelper.getDirLen(gameShaderCachePath ?? "", skipPath: [
"$gameShaderCachePath\\Crashes"
])) / 1024 / 1024).toStringAsFixed(4)} MB",
@ -207,10 +207,10 @@ class ToolsUIModel extends _$ToolsUIModel {
return ToolsItemData(
"photography_mode",
isEnable ? "关闭摄影模式" : "开启摄影模式",
isEnable ? "关闭摄影模式" : S.current.tools_action_open_photography_mode,
isEnable
? "还原镜头摇晃效果。\n\n@拉邦那 Lapernum 提供参数信息。"
: "一键关闭游戏内镜头晃动以便于摄影操作。\n\n @拉邦那 Lapernum 提供参数信息。",
: "一键${S.current.action_close}游戏内镜头晃动以便于摄影操作。\n\n @拉邦那 Lapernum 提供参数信息。",
const Icon(FontAwesomeIcons.camera, size: 24),
onTap: () => _onChangePhotographyMode(context, isEnable),
);
@ -260,18 +260,18 @@ class ToolsUIModel extends _$ToolsUIModel {
if (rsiLauncherInstalledPath == "") {
if (!context.mounted) return;
showToast(context, "未找到 RSI 启动器,请尝试重新安装,或在设置中手动添加。");
showToast(context, S.current.tools_action_info_rsi_launcher_not_found);
}
if (scInstalledPath == "") {
if (!context.mounted) return;
showToast(context, "未找到星际公民游戏安装位置,请至少完成一次游戏启动操作 或在设置中手动添加。");
showToast(context, S.current.tools_action_info_star_citizen_not_found);
}
}
/// EAC
Future<void> _reinstallEAC(BuildContext context) async {
if (state.scInstalledPath.isEmpty) {
showToast(context, "该功能需要一个有效的游戏安装目录");
showToast(context, S.current.tools_action_info_valid_game_directory_needed);
return;
}
state = state.copyWith(working: true);
@ -302,7 +302,7 @@ class ToolsUIModel extends _$ToolsUIModel {
}
if (!context.mounted) return;
showToast(context,
"已为您移除 EAC 文件,接下来将为您打开 RSI 启动器,请您前往 SETTINGS -> VERIFY 重装 EAC。");
S.current.tools_action_info_eac_file_removed);
_adminRSILauncher(context);
} catch (e) {
showToast(context, "出现错误:$e");
@ -322,7 +322,7 @@ class ToolsUIModel extends _$ToolsUIModel {
/// RSI
Future _adminRSILauncher(BuildContext context) async {
if (state.rsiLauncherInstalledPath == "") {
showToast(context, "未找到 RSI 启动器目录,请您尝试手动操作。");
showToast(context, S.current.tools_action_info_rsi_launcher_directory_not_found);
}
SystemHelper.checkAndLaunchRSILauncher(state.rsiLauncherInstalledPath);
}
@ -333,14 +333,14 @@ class ToolsUIModel extends _$ToolsUIModel {
if (!await File(path!).exists()) {
if (!context.mounted) return;
showToast(
context, "日志文件不存在,请尝试进行一次游戏启动或游戏安装,并退出启动器,若无法解决问题,请尝试将启动器更新至最新版本!");
context, S.current.tools_action_info_log_file_not_exist);
return;
}
try {
SystemHelper.killRSILauncher();
await File(path).delete(recursive: true);
if (!context.mounted) return;
showToast(context, "清理完毕,请完成一次安装 / 游戏启动 操作。");
showToast(context, S.current.tools_action_info_cleanup_complete);
SystemHelper.checkAndLaunchRSILauncher(state.rsiLauncherInstalledPath);
} catch (_) {
if (!context.mounted) return;
@ -362,16 +362,16 @@ class ToolsUIModel extends _$ToolsUIModel {
showDialog<String>(
context: context,
builder: (context) => ContentDialog(
title: const Text('系统信息'),
title: Text(S.current.tools_action_info_system_info_title),
content: Text(systemInfo),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .65,
),
actions: [
FilledButton(
child: const Padding(
padding: EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
child: Text('关闭'),
child: Padding(
padding: const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
child: Text(S.current.action_close),
),
onPressed: () => Navigator.pop(context),
),
@ -404,7 +404,7 @@ class ToolsUIModel extends _$ToolsUIModel {
if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) {
if (!context.mounted) return;
showToast(context, "RSI启动器正在运行请先关闭启动器再使用此功能",
showToast(context, S.current.tools_action_info_rsi_launcher_running_warning,
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .35));
return;
@ -413,7 +413,7 @@ class ToolsUIModel extends _$ToolsUIModel {
if (!context.mounted) return;
await showToast(
context,
"P4k 是星际公民的核心游戏文件,高达 100GB+盒子提供的离线下载是为了帮助一些p4k文件下载超级慢的用户 或用于修复官方启动器无法修复的 p4k 文件。"
"${S.current.tools_action_info_p4k_file_description_part1}"
"\n\n接下来会弹窗询问您保存位置(可以选择星际公民文件夹也可以选择别处),下载完成后请确保 P4K 文件夹位于 LIVE 文件夹内,之后使用星际公民启动器校验更新即可。");
try {
@ -431,7 +431,7 @@ class ToolsUIModel extends _$ToolsUIModel {
final t = HomeDownloaderUIModel.getTaskTypeAndName(value);
if (t.key == "torrent" && t.value.contains("Data.p4k")) {
if (!context.mounted) return;
showToast(context, "已经有一个p4k下载任务正在进行中请前往下载管理器查看");
showToast(context, S.current.tools_action_info_p4k_download_in_progress);
state = state.copyWith(working: false);
return;
}
@ -447,7 +447,7 @@ class ToolsUIModel extends _$ToolsUIModel {
if (torrentUrl == "") {
state = state.copyWith(working: false);
if (!context.mounted) return;
showToast(context, "功能维护中,请稍后重试!");
showToast(context, S.current.tools_action_info_function_under_maintenance);
return;
}
@ -496,7 +496,7 @@ class ToolsUIModel extends _$ToolsUIModel {
Future<bool> _checkPhotographyStatus(BuildContext context,
{bool? setMode}) async {
final scInstalledPath = state.scInstalledPath;
const keys = ["AudioShakeStrength", "CameraSpringMovement", "ShakeScale"];
final keys = ["AudioShakeStrength", "CameraSpringMovement", "ShakeScale"];
final attributesFile = File(
"$scInstalledPath\\USER\\Client\\0\\Profiles\\default\\attributes.xml");
if (setMode == null) {
@ -522,7 +522,7 @@ class ToolsUIModel extends _$ToolsUIModel {
} else {
if (!await attributesFile.exists()) {
if (!context.mounted) return false;
showToast(context, "配置文件不存在,请尝试运行一次游戏");
showToast(context, S.current.tools_action_info_config_file_not_exist);
return false;
}
final xmlFile = XmlDocument.parse(await attributesFile.readAsString());

View File

@ -173,7 +173,7 @@ class __$$ToolsUIStateImplCopyWithImpl<$Res>
/// @nodoc
class _$ToolsUIStateImpl implements _ToolsUIState {
const _$ToolsUIStateImpl(
_$ToolsUIStateImpl(
{this.working = false,
this.scInstalledPath = "",
this.rsiLauncherInstalledPath = "",
@ -270,7 +270,7 @@ class _$ToolsUIStateImpl implements _ToolsUIState {
}
abstract class _ToolsUIState implements ToolsUIState {
const factory _ToolsUIState(
factory _ToolsUIState(
{final bool working,
final String scInstalledPath,
final String rsiLauncherInstalledPath,

View File

@ -6,7 +6,7 @@ part of 'tools_ui_model.dart';
// RiverpodGenerator
// **************************************************************************
String _$toolsUIModelHash() => r'4fb78bfe350d792cfdadd3314f4763097ea1b279';
String _$toolsUIModelHash() => r'5568cfd422f98a1aff9b8cb9d522c84565fcc289';
/// See also [ToolsUIModel].
@ProviderFor(ToolsUIModel)

View File

@ -101,7 +101,10 @@ class WebViewModel {
if (url.startsWith(org) ||
url.startsWith(citizens) ||
url.startsWith(organization)) {
replaceWords.add({"word": 'members', "replacement": '名成员'});
replaceWords.add({
"word": 'members',
"replacement": S.current.webview_localization_name_member
});
replaceWords.addAll(_getLocalizationResource("orgs"));
}
@ -111,9 +114,21 @@ class WebViewModel {
if (url.startsWith(referral)) {
replaceWords.addAll([
{"word": 'Total recruits: ', "replacement": '总邀请数:'},
{"word": 'Prospects ', "replacement": '未完成的邀请'},
{"word": 'Recruits', "replacement": '已完成的邀请'},
{
"word": 'Total recruits: ',
"replacement":
S.current.webview_localization_total_invitations
},
{
"word": 'Prospects ',
"replacement":
S.current.webview_localization_unfinished_invitations
},
{
"word": 'Recruits',
"replacement":
S.current.webview_localization_finished_invitations
},
]);
}
@ -304,7 +319,9 @@ class WebViewModel {
// send toast
webview.evaluateJavaScript("SCTShowToast(\"请完成 Windows Hello 验证以填充密码\")");
// decrypt
if (await localAuth.authenticate(localizedReason: "请输入设备PIN以自动登录RSI账户") !=
if (await localAuth.authenticate(
localizedReason:
S.current.webview_localization_enter_device_pin) !=
true) return;
final kv = Win32Credentials.read("SCToolbox_RSI_Account_secret");
if (kv == null || kv.key != email) return;

View File

@ -41,9 +41,17 @@ class AutoL10nTools {
// read all dart File
final dir = Directory('lib/ui');
for (var entity in dir.listSync(recursive: true)) {
if (entity is File && entity.path.endsWith('.dart')) {
if (entity is File &&
entity.path.endsWith('.dart') &&
!(entity.path.endsWith(".g.dart") &&
entity.path.endsWith(".freezed.dart"))) {
print('Processing ${entity.path}...');
_replaceDartFile(entity, jsonMap);
// sort map with value length
final newMap = Map.fromEntries(
jsonMap.entries.toList()
..sort((a, b) => (b.value as String).length.compareTo((a.value as String).length)),
);
_replaceDartFile(entity, newMap);
}
}
}
@ -56,34 +64,51 @@ class AutoL10nTools {
}
void _replaceDartFile(File entity, jsonMap) {
final parseResult = parseFile(
path: entity.path, featureSet: FeatureSet.latestLanguageVersion());
final unit = parseResult.unit;
final visitor = ReplaceAstVisitor(jsonMap);
unit.accept(visitor);
final output = visitor.buffer.toString();
entity.writeAsStringSync(output);
for (var key in jsonMap.keys) {
if (key == "@@locale") continue;
final mapValue = jsonMap[key] as String;
if (mapValue.contains("{{") && mapValue.contains("}}")) {
print("skipping args value === $mapValue");
continue;
}
// 使 CheckContainsVisitor.visitStringLiteral , true false
final parseResult = parseFile(
path: entity.path, featureSet: FeatureSet.latestLanguageVersion());
final unit = parseResult.unit;
final visitor = CheckContainsVisitor(mapValue);
unit.accept(visitor);
if (visitor.hasValue) {
// replaceDartFile with line
final lines = entity.readAsLinesSync();
final newLines = <String>[];
for (var line in lines) {
if (line.contains(mapValue) && !line.contains("\$")) {
line = line.replaceAll(mapValue, "\${S.current.$key}");
}
newLines.add(line);
}
entity.writeAsStringSync(newLines.join("\n"));
}
}
}
}
class ReplaceAstVisitor extends GeneralizingAstVisitor {
final Map<String, String> jsonMap;
final buffer = StringBuffer();
class CheckContainsVisitor extends GeneralizingAstVisitor {
final String mapValue;
ReplaceAstVisitor(this.jsonMap);
CheckContainsVisitor(this.mapValue);
bool hasValue = false;
@override
visitSimpleStringLiteral(SimpleStringLiteral node) {
final value = node.value;
if (jsonMap.containsValue(value)) {
final key = jsonMap.keys.firstWhere((k) => jsonMap[k] == value);
buffer.write('S.current.$key');
} else {
buffer.write(value);
visitStringLiteral(StringLiteral node) {
final value = node.stringValue ?? "";
if (value == mapValue) {
print('Found->visitStringLiteral: $value');
hasValue = true;
}
return super.visitSimpleStringLiteral(node);
return super.visitStringLiteral(node);
}
}
class MyAstVisitor extends GeneralizingAstVisitor {

View File

@ -4,7 +4,9 @@ void main(List<String> args) {
switch (args.elementAtOrNull(0)) {
case "gen":
return AutoL10nTools().genL10nFiles();
case "replace":
return AutoL10nTools().replaceL10nFiles();
default:
throw Exception("cmd not found");
throw Exception("cmd not found");
}
}

View File

@ -30,6 +30,8 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
flutter_riverpod: ^2.4.10
riverpod_annotation: ^2.3.4
flutter_hooks: ^0.20.5