feat: allow custom translations without rebuilding

This commit is contained in:
guenhter 2026-08-07 09:17:06 +02:00
parent 8faaf2cd1e
commit b9bf8fcc80
2 changed files with 41 additions and 4 deletions

View file

@ -1,5 +1,9 @@
// i18next-parser.config.ts
import { getLanguages } from './src/lib/i18n/index.ts';
const getLanguages = async () => {
const languages = (await import('./src/lib/i18n/locales/languages.json')).default;
return languages;
};
const getLangCodes = async () => {
const languages = await getLanguages();

View file

@ -3,6 +3,7 @@ import resourcesToBackend from 'i18next-resources-to-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import type { i18n as i18nType } from 'i18next';
import { writable } from 'svelte/store';
import { WEBUI_BASE_URL } from '$lib/constants';
const createI18nStore = (i18n: i18nType) => {
const i18nWritable = writable(i18n);
@ -40,15 +41,47 @@ const createIsLoadingStore = (i18n: i18nType) => {
return isLoading;
};
const loadBundledResource = async (
language: string,
namespace: string
): Promise<Record<string, string>> => {
const mod = await import(`./locales/${language}/${namespace}.json`);
return (mod.default ?? mod) as Record<string, string>;
};
const loadRuntimeOverrides = async (
language: string,
namespace: string
): Promise<Record<string, string>> => {
try {
const res = await fetch(`${WEBUI_BASE_URL}/static/locales/${language}/${namespace}.json`, {
credentials: 'same-origin'
});
if (!res.ok) {
return {};
}
const data = await res.json();
if (data && typeof data === 'object' && !Array.isArray(data)) {
return data as Record<string, string>;
}
} catch {
// Missing override file or network error — keep bundled translations
}
return {};
};
const loadResource = async (language: string, namespace: string) => {
const bundled = await loadBundledResource(language, namespace);
const overrides = await loadRuntimeOverrides(language, namespace);
return { ...bundled, ...overrides };
};
export const initI18n = (defaultLocale?: string | undefined) => {
const detectionOrder = defaultLocale
? ['querystring', 'localStorage']
: ['querystring', 'localStorage', 'navigator'];
const fallbackDefaultLocale = defaultLocale ? [defaultLocale] : ['en-US'];
const loadResource = (language: string, namespace: string) =>
import(`./locales/${language}/${namespace}.json`);
i18next
.use(resourcesToBackend(loadResource))
.use(LanguageDetector)