Introduce i18n-bundles to remove circular import

There was a circular import i18n.ts <-> i18n-translate.ts
This commit is contained in:
Henrik Giesel 2021-10-03 20:07:28 +02:00
parent 30000ce36e
commit 402b56e5b1
3 changed files with 51 additions and 41 deletions

View file

@ -17,9 +17,7 @@ class Variable(TypedDict):
def methods() -> str: def methods() -> str:
out = [ out = [ 'import { translate } from "./i18n-bundles";' ]
'import { translate } from "./i18n";',
]
for module in modules: for module in modules:
for translation in module["translations"]: for translation in module["translations"]:
key = stringcase.camelcase(translation["key"].replace("-", "_")) key = stringcase.camelcase(translation["key"].replace("-", "_"))

40
ts/lib/i18n-bundles.ts Normal file
View file

@ -0,0 +1,40 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { FluentNumber } from "@fluent/bundle";
import type { FluentBundle } from "@fluent/bundle";
type RecordVal = number | string | FluentNumber;
let bundles: FluentBundle[] = [];
export function setBundles(...newBundles: FluentBundle[]): void {
bundles.splice(0, bundles.length, ...newBundles);
}
export function firstLanguage(): string {
return bundles[0].locales[0];
}
function formatNumbers(args: Record<string, RecordVal>): void {
for (const key of Object.keys(args)) {
if (typeof args[key] === "number") {
args[key] = new FluentNumber(args[key] as number, {
maximumFractionDigits: 2,
});
}
}
}
export function translate(key: string, args?: Record<string, RecordVal>): string {
if (args) {
formatNumbers(args);
}
for (const bundle of bundles) {
const msg = bundle.getMessage(key);
if (msg && msg.value) {
return bundle.formatPattern(msg.value, args);
}
}
return `missing key: ${key}`;
}

View file

@ -1,43 +1,14 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
// An i18n singleton and setupI18n is re-exported via the generated i18n.ts file,
// so you should not need to access this file directly.
import "intl-pluralrules"; import "intl-pluralrules";
import { FluentBundle, FluentResource, FluentNumber } from "@fluent/bundle"; import { FluentBundle, FluentResource } from "@fluent/bundle";
import { firstLanguage, setBundles } from "./i18n-bundles";
import type { ModuleName } from "./i18n-modules"; import type { ModuleName } from "./i18n-modules";
type RecordVal = number | string | FluentNumber;
function formatNumbers(args: Record<string, RecordVal>): void {
for (const key of Object.keys(args)) {
if (typeof args[key] === "number") {
args[key] = new FluentNumber(args[key] as number, {
maximumFractionDigits: 2,
});
}
}
}
let bundles: FluentBundle[] = [];
export function translate(key: string, args?: Record<string, RecordVal>): string {
if (args) {
formatNumbers(args);
}
for (const bundle of bundles) {
const msg = bundle.getMessage(key);
if (msg && msg.value) {
return bundle.formatPattern(msg.value, args);
}
}
return `missing key: ${key}`;
}
export function supportsVerticalText(): boolean { export function supportsVerticalText(): boolean {
const firstLang = bundles[0].locales[0]; const firstLang = firstLanguage();
return ( return (
firstLang.startsWith("ja") || firstLang.startsWith("ja") ||
firstLang.startsWith("zh") || firstLang.startsWith("zh") ||
@ -46,7 +17,7 @@ export function supportsVerticalText(): boolean {
} }
export function direction(): string { export function direction(): string {
const firstLang = bundles[0].locales[0]; const firstLang = firstLanguage();
if ( if (
firstLang.startsWith("ar") || firstLang.startsWith("ar") ||
firstLang.startsWith("he") || firstLang.startsWith("he") ||
@ -59,7 +30,7 @@ export function direction(): string {
} }
export function weekdayLabel(n: number): string { export function weekdayLabel(n: number): string {
const firstLang = bundles[0].locales[0]; const firstLang = firstLanguage();
const now = new Date(); const now = new Date();
const daysFromToday = -now.getDay() + n; const daysFromToday = -now.getDay() + n;
const desiredDay = new Date(now.getTime() + daysFromToday * 86_400_000); const desiredDay = new Date(now.getTime() + daysFromToday * 86_400_000);
@ -95,17 +66,18 @@ export async function setupI18n(args: { modules: ModuleName[] }): Promise<void>
} }
const json = await resp.json(); const json = await resp.json();
bundles = []; const newBundles: FluentBundle[] = [];
for (const i in json.resources) { for (const i in json.resources) {
const text = json.resources[i]; const text = json.resources[i];
const lang = json.langs[i]; const lang = json.langs[i];
const bundle = new FluentBundle([lang, "en-US"]); const bundle = new FluentBundle([lang, "en-US"]);
const resource = new FluentResource(text); const resource = new FluentResource(text);
bundle.addResource(resource); bundle.addResource(resource);
bundles.push(bundle); newBundles.push(bundle);
} }
langs = json.langs; setBundles(...newBundles);
langs.splice(0, langs.length, ...json.langs);
} }
export { ModuleName } from "./i18n-modules"; export { ModuleName } from "./i18n-modules";