179 lines
5.2 KiB
JavaScript
179 lines
5.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Merges lib/l10n/app_{en,te}.arb with app_{en,te}_more.arb (in-memory) and
|
|
* writes lib/l10n/app_localizations.dart, app_localizations_en.dart, app_localizations_te.dart.
|
|
*
|
|
* Run from package root: node tool/sync_l10n.mjs
|
|
*/
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const root = path.join(__dirname, "..");
|
|
const l10nDir = path.join(root, "lib", "l10n");
|
|
|
|
function loadArb(name) {
|
|
const p = path.join(l10nDir, name);
|
|
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
}
|
|
|
|
function merge(base, extra) {
|
|
return { ...base, ...extra };
|
|
}
|
|
|
|
function messageKeys(arb) {
|
|
return Object.keys(arb).filter((k) => !k.startsWith("@") && k !== "@@locale");
|
|
}
|
|
|
|
function placeholdersFor(arb, key) {
|
|
const meta = arb[`@${key}`];
|
|
if (!meta || !meta.placeholders) return null;
|
|
return Object.keys(meta.placeholders);
|
|
}
|
|
|
|
function dartEscapeSingle(s) {
|
|
return s
|
|
.replace(/\\/g, "\\\\")
|
|
.replace(/\r\n/g, "\n")
|
|
.replace(/\n/g, "\\n")
|
|
.replace(/'/g, "\\'");
|
|
}
|
|
|
|
/** Build Dart string literal expr from ICU-ish template (e.g. "Hi, {name}!"). */
|
|
function dartReturnExpr(template, params) {
|
|
let d = template;
|
|
for (const p of params) {
|
|
d = d.split(`{${p}}`).join(`$${p}`);
|
|
}
|
|
return `'${dartEscapeSingle(d)}'`;
|
|
}
|
|
|
|
function writeLocalizations(en, te) {
|
|
const keys = [...new Set([...messageKeys(en), ...messageKeys(te)])].sort();
|
|
|
|
const abstractDecls = [];
|
|
const enImpl = [];
|
|
const teImpl = [];
|
|
|
|
for (const key of keys) {
|
|
const ph = placeholdersFor(en, key) ?? placeholdersFor(te, key);
|
|
if (ph && ph.length) {
|
|
const args = ph.map((p) => `String ${p}`).join(", ");
|
|
abstractDecls.push(` String ${key}(${args});`);
|
|
const tplEn = en[key];
|
|
const tplTe = te[key] ?? tplEn;
|
|
enImpl.push(` @override\n String ${key}(${args}) {\n return ${dartReturnExpr(tplEn, ph)};\n }`);
|
|
teImpl.push(` @override\n String ${key}(${args}) {\n return ${dartReturnExpr(tplTe, ph)};\n }`);
|
|
} else {
|
|
abstractDecls.push(` String get ${key};`);
|
|
const vEn = en[key] ?? "";
|
|
const vTe = te[key] ?? vEn;
|
|
enImpl.push(` @override\n String get ${key} => '${dartEscapeSingle(vEn)}';`);
|
|
teImpl.push(` @override\n String get ${key} => '${dartEscapeSingle(vTe)}';`);
|
|
}
|
|
}
|
|
|
|
const header = `// GENERATED FILE — do not edit by hand.
|
|
// Source: lib/l10n/app_en.arb + app_en_more.arb (merged). Regenerate: node tool/sync_l10n.mjs
|
|
|
|
`;
|
|
|
|
const main = `${header}import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
import 'app_localizations_en.dart';
|
|
import 'app_localizations_te.dart';
|
|
|
|
abstract class AppLocalizations {
|
|
AppLocalizations(String locale) : localeName = locale;
|
|
|
|
final String localeName;
|
|
|
|
static AppLocalizations? of(BuildContext context) {
|
|
return Localizations.of<AppLocalizations>(context, AppLocalizations);
|
|
}
|
|
|
|
static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
|
|
|
|
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
|
<LocalizationsDelegate<dynamic>>[
|
|
delegate,
|
|
GlobalMaterialLocalizations.delegate,
|
|
GlobalCupertinoLocalizations.delegate,
|
|
GlobalWidgetsLocalizations.delegate,
|
|
];
|
|
|
|
static const List<Locale> supportedLocales = <Locale>[
|
|
Locale('en'),
|
|
Locale('te'),
|
|
];
|
|
|
|
${abstractDecls.join("\n\n")}
|
|
}
|
|
|
|
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
|
const _AppLocalizationsDelegate();
|
|
|
|
@override
|
|
Future<AppLocalizations> load(Locale locale) {
|
|
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
|
}
|
|
|
|
@override
|
|
bool isSupported(Locale locale) =>
|
|
<String>['en', 'te'].contains(locale.languageCode);
|
|
|
|
@override
|
|
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
|
}
|
|
|
|
AppLocalizations lookupAppLocalizations(Locale locale) {
|
|
switch (locale.languageCode) {
|
|
case 'en':
|
|
return AppLocalizationsEn();
|
|
case 'te':
|
|
return AppLocalizationsTe();
|
|
}
|
|
throw FlutterError(
|
|
'AppLocalizations.delegate failed to load unsupported locale "\$locale".',
|
|
);
|
|
}
|
|
`;
|
|
|
|
const enFile = `${header}import 'app_localizations.dart';
|
|
|
|
class AppLocalizationsEn extends AppLocalizations {
|
|
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
|
|
|
${enImpl.join("\n\n")}
|
|
}
|
|
`;
|
|
|
|
const teFile = `${header}import 'app_localizations.dart';
|
|
|
|
class AppLocalizationsTe extends AppLocalizations {
|
|
AppLocalizationsTe([String locale = 'te']) : super(locale);
|
|
|
|
${teImpl.join("\n\n")}
|
|
}
|
|
`;
|
|
|
|
fs.writeFileSync(path.join(l10nDir, "app_localizations.dart"), main);
|
|
fs.writeFileSync(path.join(l10nDir, "app_localizations_en.dart"), enFile);
|
|
fs.writeFileSync(path.join(l10nDir, "app_localizations_te.dart"), teFile);
|
|
}
|
|
|
|
const baseEn = loadArb("app_en.arb");
|
|
const baseTe = loadArb("app_te.arb");
|
|
const moreEn = loadArb("app_en_more.arb");
|
|
const moreTe = loadArb("app_te_more.arb");
|
|
|
|
const en = merge(baseEn, moreEn);
|
|
const te = merge(baseTe, moreTe);
|
|
|
|
writeLocalizations(en, te);
|
|
console.log("Wrote app_localizations*.dart from merged ARBs.");
|