mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-30 15:15:24 +00:00
feat: load plugin frontends at runtime and remove plugin ids from the shell
This commit is contained in:
1 parent
49f0286a14
commit
e88d5a52e3
721 files changed
+54265
-44416
No files matched your search
+92
-37
@@ -6,46 +6,83 @@ import enTranslation from "../locales/en.json";
|
||||
|
||||
type LocaleModule = { default: ResourceKey };
|
||||
|
||||
const localeLoaders = {
|
||||
af: () => import("../locales/translated/af_ZA.json"),
|
||||
ar: () => import("../locales/translated/ar_SA.json"),
|
||||
bn: () => import("../locales/translated/bn_BD.json"),
|
||||
bg: () => import("../locales/translated/bg_BG.json"),
|
||||
ca: () => import("../locales/translated/ca_ES.json"),
|
||||
cs: () => import("../locales/translated/cs_CZ.json"),
|
||||
da: () => import("../locales/translated/da_DK.json"),
|
||||
de: () => import("../locales/translated/de_DE.json"),
|
||||
el: () => import("../locales/translated/el_GR.json"),
|
||||
"es-ES": () => import("../locales/translated/es_ES.json"),
|
||||
fi: () => import("../locales/translated/fi_FI.json"),
|
||||
fr: () => import("../locales/translated/fr_FR.json"),
|
||||
he: () => import("../locales/translated/he_IL.json"),
|
||||
hi: () => import("../locales/translated/hi_IN.json"),
|
||||
hu: () => import("../locales/translated/hu_HU.json"),
|
||||
id: () => import("../locales/translated/id_ID.json"),
|
||||
it: () => import("../locales/translated/it_IT.json"),
|
||||
ja: () => import("../locales/translated/ja_JP.json"),
|
||||
ko: () => import("../locales/translated/ko_KR.json"),
|
||||
nl: () => import("../locales/translated/nl_NL.json"),
|
||||
no: () => import("../locales/translated/no_NO.json"),
|
||||
pl: () => import("../locales/translated/pl_PL.json"),
|
||||
"pt-PT": () => import("../locales/translated/pt_PT.json"),
|
||||
"pt-BR": () => import("../locales/translated/pt_BR.json"),
|
||||
ro: () => import("../locales/translated/ro_RO.json"),
|
||||
ru: () => import("../locales/translated/ru_RU.json"),
|
||||
sr: () => import("../locales/translated/sr_SP.json"),
|
||||
"sv-SE": () => import("../locales/translated/sv_SE.json"),
|
||||
th: () => import("../locales/translated/th_TH.json"),
|
||||
tr: () => import("../locales/translated/tr_TR.json"),
|
||||
uk: () => import("../locales/translated/uk_UA.json"),
|
||||
vi: () => import("../locales/translated/vi_VN.json"),
|
||||
"zh-CN": () => import("../locales/translated/zh_CN.json"),
|
||||
"zh-TW": () => import("../locales/translated/zh_TW.json"),
|
||||
} satisfies Record<string, () => Promise<LocaleModule>>;
|
||||
/** i18n code -> translated file name, shared by core and plugin locales. */
|
||||
export const LOCALE_FILES = {
|
||||
af: "af_ZA",
|
||||
ar: "ar_SA",
|
||||
bn: "bn_BD",
|
||||
bg: "bg_BG",
|
||||
ca: "ca_ES",
|
||||
cs: "cs_CZ",
|
||||
da: "da_DK",
|
||||
de: "de_DE",
|
||||
el: "el_GR",
|
||||
"es-ES": "es_ES",
|
||||
fi: "fi_FI",
|
||||
fr: "fr_FR",
|
||||
he: "he_IL",
|
||||
hi: "hi_IN",
|
||||
hu: "hu_HU",
|
||||
id: "id_ID",
|
||||
it: "it_IT",
|
||||
ja: "ja_JP",
|
||||
ko: "ko_KR",
|
||||
nl: "nl_NL",
|
||||
no: "no_NO",
|
||||
pl: "pl_PL",
|
||||
"pt-PT": "pt_PT",
|
||||
"pt-BR": "pt_BR",
|
||||
ro: "ro_RO",
|
||||
ru: "ru_RU",
|
||||
sr: "sr_SP",
|
||||
"sv-SE": "sv_SE",
|
||||
th: "th_TH",
|
||||
tr: "tr_TR",
|
||||
uk: "uk_UA",
|
||||
vi: "vi_VN",
|
||||
"zh-CN": "zh_CN",
|
||||
"zh-TW": "zh_TW",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const translatedFiles = import.meta.glob<LocaleModule>(
|
||||
"../locales/translated/*.json",
|
||||
);
|
||||
|
||||
const localeLoaders: Record<string, () => Promise<LocaleModule>> =
|
||||
Object.fromEntries(
|
||||
Object.entries(LOCALE_FILES).map(([code, file]) => [
|
||||
code,
|
||||
() => {
|
||||
const load = translatedFiles[`../locales/translated/${file}.json`];
|
||||
return load ? load() : Promise.reject(new Error(`no ${file}`));
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
/**
|
||||
* Loads a plugin namespace. Set by the plugin loader; until then a plugin
|
||||
* namespace resolves to nothing and keys fall back to core.
|
||||
*/
|
||||
export type PluginLocaleResolver = (
|
||||
namespace: string,
|
||||
language: string,
|
||||
file: string,
|
||||
) => Promise<ResourceKey | null>;
|
||||
|
||||
let pluginLocaleResolver: PluginLocaleResolver | null = null;
|
||||
|
||||
export function setPluginLocaleResolver(
|
||||
resolver: PluginLocaleResolver | null,
|
||||
): void {
|
||||
pluginLocaleResolver = resolver;
|
||||
}
|
||||
|
||||
export const supportedLngs = ["en", ...Object.keys(localeLoaders)];
|
||||
const PENDING_LOGIN_LANGUAGE_KEY = "termix-pending-login-language";
|
||||
|
||||
/** Core strings. Each plugin gets a namespace named after its id. */
|
||||
export const CORE_NAMESPACE = "translation";
|
||||
|
||||
export function normalizeLanguageCode(language?: string | null): string {
|
||||
if (!language) return "en";
|
||||
|
||||
@@ -64,9 +101,24 @@ export function normalizeLanguageCode(language?: string | null): string {
|
||||
const localeBackend: BackendModule = {
|
||||
type: "backend",
|
||||
init: () => {},
|
||||
read: (language, _namespace, callback) => {
|
||||
read: (language, namespace, callback) => {
|
||||
const normalizedLanguage = normalizeLanguageCode(language);
|
||||
|
||||
if (namespace !== CORE_NAMESPACE) {
|
||||
const file =
|
||||
normalizedLanguage === "en"
|
||||
? "en"
|
||||
: (LOCALE_FILES as Record<string, string>)[normalizedLanguage];
|
||||
if (!pluginLocaleResolver || !file) {
|
||||
callback(null, {});
|
||||
return;
|
||||
}
|
||||
pluginLocaleResolver(namespace, normalizedLanguage, file)
|
||||
.then((resources) => callback(null, resources ?? {}))
|
||||
.catch(() => callback(null, {}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (normalizedLanguage === "en") {
|
||||
callback(null, enTranslation);
|
||||
return;
|
||||
@@ -91,6 +143,9 @@ i18n
|
||||
.init({
|
||||
supportedLngs,
|
||||
fallbackLng: "en",
|
||||
// A plugin's namespace falls back to core, so plugins keep using the
|
||||
// shared strings (common.*, hosts.*) without copying them.
|
||||
fallbackNS: CORE_NAMESPACE,
|
||||
debug: false,
|
||||
|
||||
detection: {
|
||||
|
||||
Reference in new issue
Block a user