feat: 安装自定义汉化文件

This commit is contained in:
2024-05-05 20:58:58 +08:00
parent 90f254b1d8
commit 2c744cc5bd
17 changed files with 393 additions and 242 deletions

View File

@ -12,6 +12,8 @@ import 'package:starcitizen_doctor/ui/home/localization/advanced_localization_ui
import 'package:starcitizen_doctor/widgets/widgets.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'localization_form_file_dialog_ui.dart';
class AdvancedLocalizationUI extends HookConsumerWidget {
const AdvancedLocalizationUI({super.key});
@ -20,6 +22,17 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
final state = ref.watch(advancedLocalizationUIModelProvider);
final model = ref.read(advancedLocalizationUIModelProvider.notifier);
final homeUIState = ref.watch(homeUIModelProvider);
onSwitchFile() async {
final sb = await showDialog(
context: context,
builder: (BuildContext context) => const LocalizationFromFileDialogUI(),
);
if (sb is StringBuffer) {
model.setCustomizeGlobalIni(sb.toString());
}
}
return makeDefaultPage(
title: S.current.home_localization_advanced_title(
homeUIState.scInstalledPath ?? "-"),
@ -41,10 +54,34 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
children: [
const SizedBox(width: 12),
Expanded(
child: Text(S.current
.home_localization_advanced_msg_version(
state.apiLocalizationData?.versionName ??
"-"))),
child: Row(
children: [
Text(
S.current.home_localization_advanced_msg_version(
state.apiLocalizationData?.versionName ?? "-"),
),
const SizedBox(width: 12),
Button(
onPressed: onSwitchFile,
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 3),
child: Icon(FluentIcons.switch_widget),
)),
if (state.customizeGlobalIni != null) ...[
const SizedBox(width: 12),
Button(
onPressed: () {
model.setCustomizeGlobalIni(null);
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 3),
child: Icon(FluentIcons.delete),
)),
]
],
)),
Text(S.current.home_localization_advanced_title_msg(
state.serverGlobalIniLines, state.p4kGlobalIniLines)),
const SizedBox(width: 32),

View File

@ -25,6 +25,7 @@ class AdvancedLocalizationUIState with _$AdvancedLocalizationUIState {
Map<String, AppAdvancedLocalizationClassKeysData>? classMap,
String? p4kGlobalIni,
String? serverGlobalIni,
String? customizeGlobalIni,
ScLocalizationData? apiLocalizationData,
@Default(0) int p4kGlobalIniLines,
@Default(0) int serverGlobalIniLines,
@ -81,6 +82,13 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel {
classMap: m);
}
void setCustomizeGlobalIni(String? data) async {
state = state.copyWith(customizeGlobalIni: data);
final localizationUIState = ref.read(localizationUIModelProvider);
final localizationUIModel = ref.read(localizationUIModelProvider.notifier);
await _init(localizationUIState, localizationUIModel);
}
static Map<String, AppAdvancedLocalizationClassKeysData> _doClassIni(
(
AppAdvancedLocalizationData ald,
@ -180,21 +188,30 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel {
state = state.copyWith(
workingText: S.current
.home_localization_advanced_msg_reading_server_localization_text);
final apiLocalizationData =
localizationUIState.apiLocalizationData?.values.firstOrNull;
if (apiLocalizationData == null) return ("", "");
final file = File(
"${localizationUIModel.getDownloadDir().absolute.path}\\${apiLocalizationData.versionName}.sclang");
if (!await file.exists()) {
await localizationUIModel.downloadLocalizationFile(
file, apiLocalizationData);
if (state.customizeGlobalIni != null) {
final apiLocalizationData = ScLocalizationData(
versionName: S.current.localization_info_custom_files,
info: "Customize");
state = state.copyWith(apiLocalizationData: apiLocalizationData);
return (p4kGlobalIni, state.customizeGlobalIni!);
} else {
final apiLocalizationData =
localizationUIState.apiLocalizationData?.values.firstOrNull;
if (apiLocalizationData == null) return ("", "");
final file = File(
"${localizationUIModel.getDownloadDir().absolute.path}\\${apiLocalizationData.versionName}.sclang");
if (!await file.exists()) {
await localizationUIModel.downloadLocalizationFile(
file, apiLocalizationData);
}
state = state.copyWith(apiLocalizationData: apiLocalizationData);
final serverGlobalIni =
(await compute(LocalizationUIModel.readArchive, file.absolute.path))
.toString();
dPrint("read serverGlobalIni => ${serverGlobalIni.length}");
return (p4kGlobalIni, serverGlobalIni);
}
state = state.copyWith(apiLocalizationData: apiLocalizationData);
final serverGlobalIni =
(await compute(LocalizationUIModel.readArchive, file.absolute.path))
.toString();
dPrint("read serverGlobalIni => ${serverGlobalIni.length}");
return (p4kGlobalIni, serverGlobalIni);
}
Future<String> readEnglishInI(String gameDir) async {

View File

@ -21,6 +21,7 @@ mixin _$AdvancedLocalizationUIState {
throw _privateConstructorUsedError;
String? get p4kGlobalIni => throw _privateConstructorUsedError;
String? get serverGlobalIni => throw _privateConstructorUsedError;
String? get customizeGlobalIni => throw _privateConstructorUsedError;
ScLocalizationData? get apiLocalizationData =>
throw _privateConstructorUsedError;
int get p4kGlobalIniLines => throw _privateConstructorUsedError;
@ -44,6 +45,7 @@ abstract class $AdvancedLocalizationUIStateCopyWith<$Res> {
Map<String, AppAdvancedLocalizationClassKeysData>? classMap,
String? p4kGlobalIni,
String? serverGlobalIni,
String? customizeGlobalIni,
ScLocalizationData? apiLocalizationData,
int p4kGlobalIniLines,
int serverGlobalIniLines});
@ -67,6 +69,7 @@ class _$AdvancedLocalizationUIStateCopyWithImpl<$Res,
Object? classMap = freezed,
Object? p4kGlobalIni = freezed,
Object? serverGlobalIni = freezed,
Object? customizeGlobalIni = freezed,
Object? apiLocalizationData = freezed,
Object? p4kGlobalIniLines = null,
Object? serverGlobalIniLines = null,
@ -88,6 +91,10 @@ class _$AdvancedLocalizationUIStateCopyWithImpl<$Res,
? _value.serverGlobalIni
: serverGlobalIni // ignore: cast_nullable_to_non_nullable
as String?,
customizeGlobalIni: freezed == customizeGlobalIni
? _value.customizeGlobalIni
: customizeGlobalIni // ignore: cast_nullable_to_non_nullable
as String?,
apiLocalizationData: freezed == apiLocalizationData
? _value.apiLocalizationData
: apiLocalizationData // ignore: cast_nullable_to_non_nullable
@ -118,6 +125,7 @@ abstract class _$$AdvancedLocalizationUIStateImplCopyWith<$Res>
Map<String, AppAdvancedLocalizationClassKeysData>? classMap,
String? p4kGlobalIni,
String? serverGlobalIni,
String? customizeGlobalIni,
ScLocalizationData? apiLocalizationData,
int p4kGlobalIniLines,
int serverGlobalIniLines});
@ -140,6 +148,7 @@ class __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res>
Object? classMap = freezed,
Object? p4kGlobalIni = freezed,
Object? serverGlobalIni = freezed,
Object? customizeGlobalIni = freezed,
Object? apiLocalizationData = freezed,
Object? p4kGlobalIniLines = null,
Object? serverGlobalIniLines = null,
@ -161,6 +170,10 @@ class __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res>
? _value.serverGlobalIni
: serverGlobalIni // ignore: cast_nullable_to_non_nullable
as String?,
customizeGlobalIni: freezed == customizeGlobalIni
? _value.customizeGlobalIni
: customizeGlobalIni // ignore: cast_nullable_to_non_nullable
as String?,
apiLocalizationData: freezed == apiLocalizationData
? _value.apiLocalizationData
: apiLocalizationData // ignore: cast_nullable_to_non_nullable
@ -187,6 +200,7 @@ class _$AdvancedLocalizationUIStateImpl
final Map<String, AppAdvancedLocalizationClassKeysData>? classMap,
this.p4kGlobalIni,
this.serverGlobalIni,
this.customizeGlobalIni,
this.apiLocalizationData,
this.p4kGlobalIniLines = 0,
this.serverGlobalIniLines = 0})
@ -210,6 +224,8 @@ class _$AdvancedLocalizationUIStateImpl
@override
final String? serverGlobalIni;
@override
final String? customizeGlobalIni;
@override
final ScLocalizationData? apiLocalizationData;
@override
@JsonKey()
@ -220,7 +236,7 @@ class _$AdvancedLocalizationUIStateImpl
@override
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni, apiLocalizationData: $apiLocalizationData, p4kGlobalIniLines: $p4kGlobalIniLines, serverGlobalIniLines: $serverGlobalIniLines)';
return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni, customizeGlobalIni: $customizeGlobalIni, apiLocalizationData: $apiLocalizationData, p4kGlobalIniLines: $p4kGlobalIniLines, serverGlobalIniLines: $serverGlobalIniLines)';
}
@override
@ -232,6 +248,7 @@ class _$AdvancedLocalizationUIStateImpl
..add(DiagnosticsProperty('classMap', classMap))
..add(DiagnosticsProperty('p4kGlobalIni', p4kGlobalIni))
..add(DiagnosticsProperty('serverGlobalIni', serverGlobalIni))
..add(DiagnosticsProperty('customizeGlobalIni', customizeGlobalIni))
..add(DiagnosticsProperty('apiLocalizationData', apiLocalizationData))
..add(DiagnosticsProperty('p4kGlobalIniLines', p4kGlobalIniLines))
..add(DiagnosticsProperty('serverGlobalIniLines', serverGlobalIniLines));
@ -249,6 +266,8 @@ class _$AdvancedLocalizationUIStateImpl
other.p4kGlobalIni == p4kGlobalIni) &&
(identical(other.serverGlobalIni, serverGlobalIni) ||
other.serverGlobalIni == serverGlobalIni) &&
(identical(other.customizeGlobalIni, customizeGlobalIni) ||
other.customizeGlobalIni == customizeGlobalIni) &&
(identical(other.apiLocalizationData, apiLocalizationData) ||
other.apiLocalizationData == apiLocalizationData) &&
(identical(other.p4kGlobalIniLines, p4kGlobalIniLines) ||
@ -264,6 +283,7 @@ class _$AdvancedLocalizationUIStateImpl
const DeepCollectionEquality().hash(_classMap),
p4kGlobalIni,
serverGlobalIni,
customizeGlobalIni,
apiLocalizationData,
p4kGlobalIniLines,
serverGlobalIniLines);
@ -283,6 +303,7 @@ abstract class _AdvancedLocalizationUIState
final Map<String, AppAdvancedLocalizationClassKeysData>? classMap,
final String? p4kGlobalIni,
final String? serverGlobalIni,
final String? customizeGlobalIni,
final ScLocalizationData? apiLocalizationData,
final int p4kGlobalIniLines,
final int serverGlobalIniLines}) = _$AdvancedLocalizationUIStateImpl;
@ -296,6 +317,8 @@ abstract class _AdvancedLocalizationUIState
@override
String? get serverGlobalIni;
@override
String? get customizeGlobalIni;
@override
ScLocalizationData? get apiLocalizationData;
@override
int get p4kGlobalIniLines;

View File

@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart';
// **************************************************************************
String _$advancedLocalizationUIModelHash() =>
r'9fbbeca3af90f992717710633bcf2cd0e1cb06eb';
r'9acfe6624ebce488a8fd0ed13b84d8b4faab058c';
/// See also [AdvancedLocalizationUIModel].
@ProviderFor(AdvancedLocalizationUIModel)

View File

@ -8,6 +8,7 @@ import 'package:starcitizen_doctor/data/sc_localization_data.dart';
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
import 'package:starcitizen_doctor/widgets/widgets.dart';
import 'localization_form_file_dialog_ui.dart';
import 'localization_ui_model.dart';
class LocalizationDialogUI extends HookConsumerWidget {
@ -483,6 +484,17 @@ class LocalizationDialogUI extends HookConsumerWidget {
case "advanced":
context.push("/index/advanced_localization");
break;
case "custom_files":
final sb = await showDialog(
context: context,
builder: (BuildContext context) =>
const LocalizationFromFileDialogUI(),
);
if (sb is StringBuffer) {
await model.installFormString(
sb, S.current.localization_info_custom_files);
}
break;
}
}
: null,

View File

@ -0,0 +1,130 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:re_editor/re_editor.dart';
import 'package:starcitizen_doctor/widgets/widgets.dart';
class LocalizationFromFileDialogUI extends HookConsumerWidget {
const LocalizationFromFileDialogUI({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedStringBuffer = useState<StringBuffer?>(null);
final isLoading = useState(false);
void onSelectFile() async {
final result = await FilePicker.platform.pickFiles(
dialogTitle: "请选择 ini 文件",
type: FileType.custom,
allowedExtensions: ["ini"],
allowMultiple: false,
lockParentWindow: true);
if (result == null || result.files.firstOrNull == null) return;
isLoading.value = true;
final file = result.files.first;
final buffer = StringBuffer();
final content = await File(file.path!).readAsString();
for (final line in content.split("\n")) {
if (line.startsWith("_starcitizen_doctor_")) continue;
buffer.writeln(line);
}
selectedStringBuffer.value = buffer;
isLoading.value = false;
}
useEffect(() {
addPostFrameCallback(() => onSelectFile());
return null;
}, const []);
return ContentDialog(
constraints: BoxConstraints(
maxWidth: selectedStringBuffer.value == null
? 420
: MediaQuery.of(context).size.width * .75,
maxHeight: MediaQuery.of(context).size.height * .8,
),
title: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
FluentIcons.back,
size: 22,
),
onPressed: () => context.pop()),
const SizedBox(width: 12),
const Text("请选择自定义汉化文件"),
const Spacer(),
if (selectedStringBuffer.value != null)
FilledButton(
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
child: Text("确认"),
),
onPressed: () {
Navigator.pop(context, selectedStringBuffer.value);
})
],
),
content: AnimatedSize(
duration: const Duration(milliseconds: 130),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (selectedStringBuffer.value == null)
Center(
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(.05),
borderRadius: BorderRadius.circular(7),
),
padding: const EdgeInsets.all(12),
child: IconButton(
icon: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(
FluentIcons.file_code,
size: 32,
color: Colors.white.withOpacity(.6),
),
const SizedBox(height: 12),
const Text("点击选择 ini 文件")
],
),
),
onPressed: onSelectFile,
),
),
)
else if (isLoading.value) ...[
makeLoading(context),
] else ...[
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(.05),
borderRadius: BorderRadius.circular(7),
),
padding: const EdgeInsets.all(6),
child: CodeEditor(
controller: CodeLineEditingController.fromText(
selectedStringBuffer.value.toString()),
readOnly: true,
),
),
),
],
],
),
),
);
}
}

View File

@ -34,7 +34,6 @@ class LocalizationUIState with _$LocalizationUIState {
MapEntry<bool, String>? patchStatus,
bool? isInstalledAdvanced,
List<String>? customizeList,
@Default(false) bool enableCustomize,
}) = _LocalizationUIState;
}
@ -208,31 +207,13 @@ class LocalizationUIModel extends _$LocalizationUIModel {
};
}
void toggleCustomize() {
state = state.copyWith(enableCustomize: !state.enableCustomize);
}
String getCustomizeFileName(String path) {
return path.split("\\").last;
}
VoidCallback? doLocalInstall(String filePath) {
if (state.workingVersion.isNotEmpty) return null;
return () async {
final f = File(filePath);
if (!await f.exists()) return;
state = state.copyWith(workingVersion: filePath);
final str = await f.readAsString();
await installFormString(
StringBuffer(str),
S.current
.localization_info_custom_file(getCustomizeFileName(filePath)));
state = state.copyWith(workingVersion: "");
};
}
installFormString(StringBuffer globalIni, String versionName,
{bool? advanced}) async {
dPrint("LocalizationUIModel -> installFormString $versionName");
final iniFile = File(
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini");
if (versionName.isNotEmpty) {

View File

@ -23,7 +23,6 @@ mixin _$LocalizationUIState {
MapEntry<bool, String>? get patchStatus => throw _privateConstructorUsedError;
bool? get isInstalledAdvanced => throw _privateConstructorUsedError;
List<String>? get customizeList => throw _privateConstructorUsedError;
bool get enableCustomize => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$LocalizationUIStateCopyWith<LocalizationUIState> get copyWith =>
@ -42,8 +41,7 @@ abstract class $LocalizationUIStateCopyWith<$Res> {
String workingVersion,
MapEntry<bool, String>? patchStatus,
bool? isInstalledAdvanced,
List<String>? customizeList,
bool enableCustomize});
List<String>? customizeList});
}
/// @nodoc
@ -65,7 +63,6 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState>
Object? patchStatus = freezed,
Object? isInstalledAdvanced = freezed,
Object? customizeList = freezed,
Object? enableCustomize = null,
}) {
return _then(_value.copyWith(
selectedLanguage: freezed == selectedLanguage
@ -92,10 +89,6 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState>
? _value.customizeList
: customizeList // ignore: cast_nullable_to_non_nullable
as List<String>?,
enableCustomize: null == enableCustomize
? _value.enableCustomize
: enableCustomize // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@ -114,8 +107,7 @@ abstract class _$$LocalizationUIStateImplCopyWith<$Res>
String workingVersion,
MapEntry<bool, String>? patchStatus,
bool? isInstalledAdvanced,
List<String>? customizeList,
bool enableCustomize});
List<String>? customizeList});
}
/// @nodoc
@ -135,7 +127,6 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res>
Object? patchStatus = freezed,
Object? isInstalledAdvanced = freezed,
Object? customizeList = freezed,
Object? enableCustomize = null,
}) {
return _then(_$LocalizationUIStateImpl(
selectedLanguage: freezed == selectedLanguage
@ -162,10 +153,6 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res>
? _value._customizeList
: customizeList // ignore: cast_nullable_to_non_nullable
as List<String>?,
enableCustomize: null == enableCustomize
? _value.enableCustomize
: enableCustomize // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@ -179,8 +166,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
this.workingVersion = "",
this.patchStatus,
this.isInstalledAdvanced,
final List<String>? customizeList,
this.enableCustomize = false})
final List<String>? customizeList})
: _apiLocalizationData = apiLocalizationData,
_customizeList = customizeList;
@ -214,13 +200,9 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
return EqualUnmodifiableListView(value);
}
@override
@JsonKey()
final bool enableCustomize;
@override
String toString() {
return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList, enableCustomize: $enableCustomize)';
return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)';
}
@override
@ -239,9 +221,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
(identical(other.isInstalledAdvanced, isInstalledAdvanced) ||
other.isInstalledAdvanced == isInstalledAdvanced) &&
const DeepCollectionEquality()
.equals(other._customizeList, _customizeList) &&
(identical(other.enableCustomize, enableCustomize) ||
other.enableCustomize == enableCustomize));
.equals(other._customizeList, _customizeList));
}
@override
@ -252,8 +232,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
workingVersion,
patchStatus,
isInstalledAdvanced,
const DeepCollectionEquality().hash(_customizeList),
enableCustomize);
const DeepCollectionEquality().hash(_customizeList));
@JsonKey(ignore: true)
@override
@ -270,8 +249,7 @@ abstract class _LocalizationUIState implements LocalizationUIState {
final String workingVersion,
final MapEntry<bool, String>? patchStatus,
final bool? isInstalledAdvanced,
final List<String>? customizeList,
final bool enableCustomize}) = _$LocalizationUIStateImpl;
final List<String>? customizeList}) = _$LocalizationUIStateImpl;
@override
String? get selectedLanguage;
@ -286,8 +264,6 @@ abstract class _LocalizationUIState implements LocalizationUIState {
@override
List<String>? get customizeList;
@override
bool get enableCustomize;
@override
@JsonKey(ignore: true)
_$$LocalizationUIStateImplCopyWith<_$LocalizationUIStateImpl> get copyWith =>
throw _privateConstructorUsedError;

View File

@ -7,7 +7,7 @@ part of 'localization_ui_model.dart';
// **************************************************************************
String _$localizationUIModelHash() =>
r'892a302b28ce4446cab7591f54008fbcc2e5eae0';
r'6aad19a3302d7b5691fc5f6f2cce26ccdf245662';
/// See also [LocalizationUIModel].
@ProviderFor(LocalizationUIModel)

View File

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