feat: 多语言提取器

This commit is contained in:
xkeyC 2024-03-14 21:49:21 +08:00
parent 8426570ced
commit a84852d4f0
16 changed files with 432 additions and 17 deletions

1
.gitignore vendored
View File

@ -45,3 +45,4 @@ app.*.map.json
/pubspec.lock /pubspec.lock
/rust/target/ /rust/target/
/rust/Cargo.lock /rust/Cargo.lock
/lib/generated/l10_temp.json

View File

@ -0,0 +1,67 @@
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that looks up messages for specific locales by
// delegating to the appropriate library.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:implementation_imports, file_names, unnecessary_new
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
// ignore_for_file:argument_type_not_assignable, invalid_assignment
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
// ignore_for_file:comment_references
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
import 'package:intl/src/intl_helpers.dart';
import 'messages_en.dart' as messages_en;
import 'messages_zh_CN.dart' as messages_zh_cn;
typedef Future<dynamic> LibraryLoader();
Map<String, LibraryLoader> _deferredLibraries = {
'en': () => new SynchronousFuture(null),
'zh_CN': () => new SynchronousFuture(null),
};
MessageLookupByLibrary? _findExact(String localeName) {
switch (localeName) {
case 'en':
return messages_en.messages;
case 'zh_CN':
return messages_zh_cn.messages;
default:
return null;
}
}
/// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) {
var availableLocale = Intl.verifiedLocale(
localeName, (locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null);
if (availableLocale == null) {
return new SynchronousFuture(false);
}
var lib = _deferredLibraries[availableLocale];
lib == null ? new SynchronousFuture(false) : lib();
initializeInternalMessageLookup(() => new CompositeMessageLookup());
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
return new SynchronousFuture(true);
}
bool _messagesExistFor(String locale) {
try {
return _findExact(locale) != null;
} catch (e) {
return false;
}
}
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
var actualLocale =
Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
if (actualLocale == null) return null;
return _findExact(actualLocale);
}

View File

@ -0,0 +1,25 @@
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a en locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = new MessageLookup();
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'en';
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{};
}

View File

@ -0,0 +1,25 @@
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a zh_CN locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = new MessageLookup();
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'zh_CN';
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{};
}

79
lib/generated/l10n.dart Normal file
View File

@ -0,0 +1,79 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'intl/messages_all.dart';
// **************************************************************************
// Generator: Flutter Intl IDE plugin
// Made by Localizely
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
class S {
S();
static S? _current;
static S get current {
assert(_current != null,
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.');
return _current!;
}
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
static Future<S> load(Locale locale) {
final name = (locale.countryCode?.isEmpty ?? false)
? locale.languageCode
: locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
final instance = S();
S._current = instance;
return instance;
});
}
static S of(BuildContext context) {
final instance = S.maybeOf(context);
assert(instance != null,
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
return instance!;
}
static S? maybeOf(BuildContext context) {
return Localizations.of<S>(context, S);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
const AppLocalizationDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
Locale.fromSubtags(languageCode: 'en'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),
];
}
@override
bool isSupported(Locale locale) => _isSupported(locale);
@override
Future<S> load(Locale locale) => S.load(locale);
@override
bool shouldReload(AppLocalizationDelegate old) => false;
bool _isSupported(Locale locale) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode) {
return true;
}
}
return false;
}
}

3
lib/l10n/intl_en.arb Normal file
View File

@ -0,0 +1,3 @@
{
"@@locale": "en"
}

3
lib/l10n/intl_zh_CN.arb Normal file
View File

@ -0,0 +1,3 @@
{
"@@locale": "zh_CN"
}

View File

@ -46,21 +46,6 @@ class HomeGameLoginDialogUI extends HookConsumerWidget {
], ],
), ),
), ),
] else if (loginState.loginStatus == 1) ...[
Text(
"请输入RSI账户 [${loginState.nickname}] 的邮箱,以保存登录状态(输入错误会导致无法进入游戏!)"),
const SizedBox(height: 12),
TextFormBox(
// controller: model.emailCtrl,
),
const SizedBox(height: 6),
Text(
"*该操作同一账号只需执行一次,输入错误请在盒子设置中清理,切换账号请在汉化浏览器中操作。",
style: TextStyle(
fontSize: 13,
color: Colors.white.withOpacity(.6),
),
)
] else if (loginState.loginStatus == 2 || ] else if (loginState.loginStatus == 2 ||
loginState.loginStatus == 3) ...[ loginState.loginStatus == 3) ...[
Center( Center(

7
packages/sct_dev_tools/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View File

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View File

@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,117 @@
import 'dart:convert';
import 'dart:io';
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:uuid/v4.dart';
final stringResult = <String>[];
class AutoL10nTools {
void genL10nFiles() {
final dir = Directory('lib/ui');
for (var entity in dir.listSync(recursive: true)) {
if (entity is File && entity.path.endsWith('.dart')) {
print('Processing ${entity.path}...');
_processDartFile(entity);
}
}
if (stringResult.isNotEmpty) {
final outputMap = <String, String>{};
for (var value in stringResult) {
if (outputMap.containsValue(value)) {
continue;
}
final key = UuidV4().generate();
outputMap[key] = value;
}
// output to json
final j = json.encode(outputMap);
File("./lib/generated/l10_temp.json").writeAsStringSync(j);
print(
"output to json file (length: ${outputMap.length}): ./lib/generated/l10n_temp.json");
}
// output to json
}
void _processDartFile(File file) {
final parseResult = parseFile(
path: file.path, featureSet: FeatureSet.latestLanguageVersion());
final unit = parseResult.unit;
unit.accept(MyAstVisitor());
}
}
class MyAstVisitor extends GeneralizingAstVisitor {
@override
visitStringLiteral(StringLiteral node) {
final value = node.stringValue ?? "";
if (containsChinese(value)) {
print('Found->visitStringLiteral: $value');
addStringResult(value);
}
return super.visitStringLiteral(node);
}
@override
visitAdjacentStrings(AdjacentStrings node) {
int interpolationIndex = 0;
var result = '';
for (var string in node.strings) {
if (string is SimpleStringLiteral) {
result += string.value;
} else if (string is StringInterpolation) {
for (var element in string.elements) {
if (element is InterpolationString) {
result += element.value;
} else if (element is InterpolationExpression) {
result += '{{${interpolationIndex++}}}';
}
}
}
}
if (containsChinese(result)) {
print('Found->visitAdjacentStrings: $result');
addStringResult(result);
}
return super.visitAdjacentStrings(node);
}
@override
visitStringInterpolation(StringInterpolation node) {
int interpolationIndex = 0;
var result = '';
for (var element in node.elements) {
if (element is InterpolationString) {
result += element.value;
} else if (element is InterpolationExpression) {
result += '{{${interpolationIndex++}}}';
}
}
if (containsChinese(result)) {
print('Found->visitStringInterpolation: $result');
addStringResult(result);
}
return super.visitStringInterpolation(node);
}
@override
visitInterpolationExpression(InterpolationExpression node) {
int interpolationIndex = 0;
final value = '{{${interpolationIndex++}}}';
if (containsChinese(value)) {
print('Found->visitInterpolationExpression: $value');
addStringResult(value);
}
return super.visitInterpolationExpression(node);
}
bool containsChinese(String input) {
return RegExp(r'[\u4e00-\u9fa5]').hasMatch(input);
}
addStringResult(String value) {
stringResult.add(value);
}
}

View File

@ -0,0 +1,10 @@
import 'auto_l10n.dart';
void main(List<String> args) {
switch (args.elementAtOrNull(0)) {
case "gen":
return AutoL10nTools().genL10nFiles();
default:
throw Exception("cmd not found");
}
}

View File

@ -0,0 +1,17 @@
name: sct_dev_tools
description: A starting point for Dart libraries or applications.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
environment:
sdk: ^3.3.1
# Add regular dependencies here.
dependencies:
# path: ^1.8.0
analyzer: ^6.4.1
uuid: ^4.3.3
dev_dependencies:
lints: ^3.0.0
test: ^1.24.0

View File

@ -79,7 +79,7 @@ dependencies:
path: rust_builder path: rust_builder
aria2: aria2:
git: https://github.com/xkeyC/dart_aria2_rpc.git git: https://github.com/xkeyC/dart_aria2_rpc.git
# path: ../../xkeyC/dart_aria2_rpc # path: ../../xkeyC/dart_aria2_rpc
intl: ^0.18.0 intl: ^0.18.0
synchronized: ^3.1.0+1 synchronized: ^3.1.0+1
dependency_overrides: dependency_overrides:
@ -103,6 +103,8 @@ dev_dependencies:
custom_lint: ^0.6.2 custom_lint: ^0.6.2
riverpod_lint: ^2.3.9 riverpod_lint: ^2.3.9
ffigen: ^11.0.0 ffigen: ^11.0.0
sct_dev_tools:
path: ./packages/sct_dev_tools
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
@ -158,4 +160,6 @@ msix_config:
capabilities: internetClient,allowElevation capabilities: internetClient,allowElevation
languages: zh-cn languages: zh-cn
windows_build_args: --dart-define="MSE=true" windows_build_args: --dart-define="MSE=true"
store: true store: true
flutter_intl:
enabled: true