feat: make i18n work (thanks serstars)
This commit is contained in:
parent
43a4a5b665
commit
cb6a242fe8
1 changed files with 129 additions and 36 deletions
|
|
@ -1,10 +1,13 @@
|
||||||
|
import { logger } from "@lib/utils/logger";
|
||||||
import { FluxDispatcher } from "@metro/common";
|
import { FluxDispatcher } from "@metro/common";
|
||||||
import { findByNameLazy } from "@metro/wrappers";
|
import { findByNameLazy, findByStoreName } from "@metro/wrappers";
|
||||||
import { PrimitiveType } from "intl-messageformat";
|
import { PrimitiveType } from "intl-messageformat";
|
||||||
|
|
||||||
import langDefault from "./default.json";
|
import langDefault from "./default.json";
|
||||||
|
|
||||||
const IntlMessageFormat = findByNameLazy("MessageFormat") as typeof import("intl-messageformat").default;
|
const IntlMessageFormat = findByNameLazy(
|
||||||
|
"MessageFormat",
|
||||||
|
) as typeof import("intl-messageformat").default;
|
||||||
|
|
||||||
type I18nKey = keyof typeof langDefault;
|
type I18nKey = keyof typeof langDefault;
|
||||||
|
|
||||||
|
|
@ -14,54 +17,144 @@ let _lastSetLocale: string | null = null;
|
||||||
const _loadedLocale = new Set<string>();
|
const _loadedLocale = new Set<string>();
|
||||||
const _loadedStrings = {} as Record<string, typeof langDefault>;
|
const _loadedStrings = {} as Record<string, typeof langDefault>;
|
||||||
|
|
||||||
export const Strings = new Proxy({}, {
|
export const Strings = new Proxy(
|
||||||
get: (_t, prop: keyof typeof langDefault) => {
|
{},
|
||||||
if (_currentLocale && _loadedStrings[_currentLocale]?.[prop]) {
|
{
|
||||||
return _loadedStrings[_currentLocale]?.[prop];
|
get: (_t, prop: keyof typeof langDefault) => {
|
||||||
|
if (_currentLocale && _loadedStrings[_currentLocale]?.[prop]) {
|
||||||
|
return _loadedStrings[_currentLocale]?.[prop];
|
||||||
|
}
|
||||||
|
return langDefault[prop];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
) as Record<I18nKey, string>;
|
||||||
|
|
||||||
|
const languageMap: Record<string, string> = {
|
||||||
|
"ar-SA": "ar",
|
||||||
|
"bn-BD": "bn",
|
||||||
|
"ca-ES": "ca",
|
||||||
|
"de-DE": "de",
|
||||||
|
"es-ES": "es",
|
||||||
|
"es-419": "es",
|
||||||
|
"fa-IR": "fa",
|
||||||
|
"fi-FI": "fi",
|
||||||
|
"fr-FR": "fr",
|
||||||
|
"hi-IN": "hi",
|
||||||
|
"hr-HR": "hr",
|
||||||
|
"hu-HU": "hu",
|
||||||
|
"id-ID": "id",
|
||||||
|
"it-IT": "it",
|
||||||
|
"ja-JP": "ja",
|
||||||
|
"pl-PL": "pl",
|
||||||
|
"pt-BR": "pt_BR",
|
||||||
|
"ru-RU": "ru",
|
||||||
|
"sk-SK": "sk",
|
||||||
|
"sv-SE": "sv",
|
||||||
|
"tr-TR": "tr",
|
||||||
|
"vi-VN": "vi",
|
||||||
|
};
|
||||||
|
|
||||||
|
function fetchLocale(locale: string) {
|
||||||
|
const resolvedLocale = (_lastSetLocale = languageMap[locale] ?? locale);
|
||||||
|
|
||||||
|
logger.log("[i18n] fetchLocale called:", locale, "->", resolvedLocale);
|
||||||
|
|
||||||
|
if (!_loadedLocale.has(resolvedLocale)) {
|
||||||
|
_loadedLocale.add(resolvedLocale);
|
||||||
|
|
||||||
|
if (resolvedLocale.toLowerCase().startsWith("en")) {
|
||||||
|
// use local default.json for english
|
||||||
|
logger.log("[i18n] Using local default.json for English locale");
|
||||||
|
_loadedStrings[resolvedLocale] = langDefault;
|
||||||
|
_currentLocale = resolvedLocale;
|
||||||
|
} else {
|
||||||
|
fetch(`https://codeberg.org/cocobo1/kettu-i18n/raw/branch/main/base/${resolvedLocale}.json`)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(strings => {
|
||||||
|
logger.log("[i18n] Loaded strings for:", resolvedLocale);
|
||||||
|
_loadedStrings[resolvedLocale] = strings;
|
||||||
|
_currentLocale = resolvedLocale;
|
||||||
|
})
|
||||||
|
.catch(e =>
|
||||||
|
logger.error(`[i18n] Error fetching strings for ${resolvedLocale}: ${e}`),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return langDefault[prop];
|
} else {
|
||||||
|
_currentLocale = resolvedLocale;
|
||||||
}
|
}
|
||||||
}) as Record<I18nKey, string>;
|
}
|
||||||
|
|
||||||
export function initFetchI18nStrings() {
|
export function initFetchI18nStrings() {
|
||||||
const cb = ({ locale }: { locale: string; }) => {
|
let attempts = 0;
|
||||||
const languageMap = {
|
const checkAndFetch = () => {
|
||||||
"es-ES": "es",
|
attempts++;
|
||||||
"es-419": "es_419",
|
|
||||||
"zh-TW": "zh-Hant",
|
|
||||||
"zh-CN": "zh-Hans",
|
|
||||||
"pt-PT": "pt",
|
|
||||||
"pt-BR": "pt_BR",
|
|
||||||
"sv-SE": "sv"
|
|
||||||
} as Record<string, string>;
|
|
||||||
|
|
||||||
const resolvedLocale = _lastSetLocale = languageMap[locale] ?? locale;
|
try {
|
||||||
|
const LocaleStore = findByStoreName("LocaleStore");
|
||||||
|
logger.log("[i18n] Attempt", attempts, "- LocaleStore:", !!LocaleStore);
|
||||||
|
|
||||||
if (resolvedLocale.startsWith("en-")) {
|
if (!LocaleStore) {
|
||||||
_currentLocale = null;
|
logger.log("[i18n] LocaleStore not found yet");
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LocaleStore?._isInitialized !== true) {
|
||||||
|
logger.log("[i18n] LocaleStore not initialized yet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locale = LocaleStore.locale;
|
||||||
|
|
||||||
|
if (locale) {
|
||||||
|
logger.log("[i18n] Using LocaleStore:", locale);
|
||||||
|
fetchLocale(locale);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
logger.log("[i18n] Error:", e);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if (!_loadedLocale.has(resolvedLocale)) {
|
const tryTimes = () => {
|
||||||
_loadedLocale.add(resolvedLocale);
|
if (checkAndFetch()) return;
|
||||||
|
if (attempts < 15) {
|
||||||
fetch(`https://codeberg.org/cocobo1/kettu-i18n/src/branch/main/base/${resolvedLocale}.json`)
|
setTimeout(tryTimes, 500);
|
||||||
.then(r => r.json())
|
|
||||||
.then(strings => _loadedStrings[resolvedLocale] = strings)
|
|
||||||
.then(() => resolvedLocale === _lastSetLocale && (_currentLocale = resolvedLocale))
|
|
||||||
.catch(e => console.error(`An error occured while fetching strings for ${resolvedLocale}: ${e}`));
|
|
||||||
} else {
|
|
||||||
_currentLocale = resolvedLocale;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FluxDispatcher.subscribe("I18N_LOAD_SUCCESS", cb);
|
tryTimes();
|
||||||
return () => FluxDispatcher.unsubscribe("I18N_LOAD_SUCCESS", cb);
|
|
||||||
|
const cb = (e: any) => {
|
||||||
|
if (e?.settings?.changes?.loading) {
|
||||||
|
logger.log("[i18n] Settings loading, skipping...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locale = e?.settings?.changes?.protoToSave?.localization?.locale?.value;
|
||||||
|
logger.log("[i18n] Locale changed:", locale);
|
||||||
|
if (locale) {
|
||||||
|
logger.log("[i18n] Found locale in event:", locale);
|
||||||
|
fetchLocale(locale);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FluxDispatcher.subscribe("USER_SETTINGS_PROTO_UPDATE_EDIT_INFO", cb);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
FluxDispatcher.unsubscribe("USER_SETTINGS_PROTO_UPDATE_EDIT_INFO", cb);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type FormatStringRet<T> = T extends PrimitiveType ? string : string | T | (string | T)[];
|
type FormatStringRet<T> = T extends PrimitiveType
|
||||||
|
? string
|
||||||
|
: string | T | (string | T)[];
|
||||||
|
|
||||||
export function formatString<T = void>(key: I18nKey, val: Record<string, T>): FormatStringRet<T> {
|
export function formatString<T = void>(
|
||||||
|
key: I18nKey,
|
||||||
|
val: Record<string, T>,
|
||||||
|
): FormatStringRet<T> {
|
||||||
const str = Strings[key];
|
const str = Strings[key];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return new IntlMessageFormat(str).format(val);
|
return new IntlMessageFormat(str).format(val);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue