// 원격 배포 다국어 덮어쓰기 층 생성기 — 요구사항 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; File(outputPath).writeAsStringSync(generateRemoteL10n(arb)); stdout.writeln('wrote $outputPath'); } String generateRemoteL10n(Map 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 _remote;') ..writeln() ..writeln(' /// 자리표시자를 한 번에 치환한다 — 인자 값 안의 `{name}` 은 다시 바꾸지 않는다.') ..writeln(' String? _fill(String key, Map 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?; final names = ((meta?['placeholders'] as Map?) ?? 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(); }