Files
fanuzu/app/tool/gen_l10n_remote.dart
T

71 lines
3.2 KiB
Dart

// 원격 배포 다국어 덮어쓰기 층 생성기 — 요구사항 FR-I18N-01 「언어 리소스를 원격 배포 방식으로 갱신」.
//
// `flutter gen-l10n` 이 만드는 AppLocalizations 는 문자열이 컴파일 상수라, 재배포 없이
// 문구를 고칠 길이 없다. 여기서 그 클래스를 감싸는 [RemoteAppLocalizations] 를 만든다.
// 게터마다 Firestore `i18n/{locale}` 문서의 같은 키를 먼저 보고, 없으면 번들 문자열을 쓴다.
//
// 실행: `dart run tool/gen_l10n_remote.dart` (app/ 에서). arb 키를 더하거나 바꾸면 다시 돌린다.
// 생성물이 arb 와 어긋나면 test/i18n_test.dart 가 실패한다.
import 'dart:convert';
import 'dart:io';
const templatePath = 'lib/l10n/app_ko.arb';
const outputPath = 'lib/l10n/remote_app_localizations.dart';
void main() {
final arb =
jsonDecode(File(templatePath).readAsStringSync()) as Map<String, dynamic>;
File(outputPath).writeAsStringSync(generateRemoteL10n(arb));
stdout.writeln('wrote $outputPath');
}
String generateRemoteL10n(Map<String, dynamic> arb) {
final out = StringBuffer()
..writeln('// GENERATED by tool/gen_l10n_remote.dart — 직접 고치지 말 것.')
..writeln("import 'app_localizations.dart';")
..writeln()
..writeln('/// 번들 문자열 위에 원격 문자열(`i18n/{locale}` 문서)을 덮는다.')
..writeln('/// 원격에 없는 키는 번들 → 번들에도 없으면 ko(template) 순으로 폴백한다.')
..writeln('class RemoteAppLocalizations extends AppLocalizations {')
..writeln(
' RemoteAppLocalizations(this._base, this._remote) : super(_base.localeName);',
)
..writeln()
..writeln(' final AppLocalizations _base;')
..writeln(' final Map<String, String> _remote;')
..writeln()
..writeln(' /// 자리표시자를 한 번에 치환한다 — 인자 값 안의 `{name}` 은 다시 바꾸지 않는다.')
..writeln(' String? _fill(String key, Map<String, String> args) =>')
..writeln(' _remote[key]?.replaceAllMapped(')
..writeln(" RegExp(r'\\{(\\w+)\\}'),")
..writeln(' (m) => args[m[1]] ?? m[0]!,')
..writeln(' );');
for (final key
in arb.keys.where((k) => !k.startsWith('@')).toList()..sort()) {
final message = arb[key] as String;
final meta = arb['@$key'] as Map<String, dynamic>?;
final names = ((meta?['placeholders'] as Map<String, dynamic>?) ?? const {})
.keys
.toList();
out.writeln();
out.writeln(' @override');
if (names.isEmpty) {
out.writeln(" String get $key => _remote['$key'] ?? _base.$key;");
continue;
}
final params = names.map((n) => 'String $n').join(', ');
final args = names.join(', ');
// ICU select·plural 은 원격 문자열로 해석하지 않는다 — 번들만 쓴다.
if (message.contains(', select,') || message.contains(', plural,')) {
out.writeln(' String $key($params) => _base.$key($args);');
continue;
}
final map = names.map((n) => "'$n': $n").join(', ');
out.writeln(
" String $key($params) => _fill('$key', {$map}) ?? _base.$key($args);",
);
}
out.writeln('}');
return out.toString();
}