feat(electrobun): 10 locales + system language auto-detection

This commit is contained in:
Hibryda 2026-03-22 10:42:25 +01:00
parent aae86a4001
commit 7a8df2c216

View file

@ -21,9 +21,16 @@ export interface LocaleMeta {
} }
export const AVAILABLE_LOCALES: LocaleMeta[] = [ export const AVAILABLE_LOCALES: LocaleMeta[] = [
{ tag: 'en', label: 'English', nativeLabel: 'English', dir: 'ltr' }, { tag: 'en', label: 'English', nativeLabel: 'English', dir: 'ltr' },
{ tag: 'pl', label: 'Polish', nativeLabel: 'Polski', dir: 'ltr' }, { tag: 'de', label: 'German', nativeLabel: 'Deutsch', dir: 'ltr' },
{ tag: 'ar', label: 'Arabic', nativeLabel: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629', dir: 'rtl' }, { tag: 'es', label: 'Spanish', nativeLabel: 'Español', dir: 'ltr' },
{ tag: 'fr', label: 'French', nativeLabel: 'Français', dir: 'ltr' },
{ tag: 'ja', label: 'Japanese', nativeLabel: '日本語', dir: 'ltr' },
{ tag: 'pl', label: 'Polish', nativeLabel: 'Polski', dir: 'ltr' },
{ tag: 'uk', label: 'Ukrainian', nativeLabel: 'Українська', dir: 'ltr' },
{ tag: 'zh', label: 'Chinese', nativeLabel: '中文', dir: 'ltr' },
{ tag: 'ar', label: 'Arabic', nativeLabel: 'العربية', dir: 'rtl' },
{ tag: 'he', label: 'Hebrew', nativeLabel: 'עברית', dir: 'rtl' },
]; ];
// ── Reactive state ─────────────────────────────────────────────────────────── // ── Reactive state ───────────────────────────────────────────────────────────
@ -44,8 +51,15 @@ let _intl: IntlShape<string> = createIntl({ locale: 'en', messages: {} }, cache)
const loaders: Record<string, () => Promise<Messages>> = { const loaders: Record<string, () => Promise<Messages>> = {
en: () => import('../../locales/en.json').then(m => m.default as Messages), en: () => import('../../locales/en.json').then(m => m.default as Messages),
de: () => import('../../locales/de.json').then(m => m.default as Messages),
es: () => import('../../locales/es.json').then(m => m.default as Messages),
fr: () => import('../../locales/fr.json').then(m => m.default as Messages),
ja: () => import('../../locales/ja.json').then(m => m.default as Messages),
pl: () => import('../../locales/pl.json').then(m => m.default as Messages), pl: () => import('../../locales/pl.json').then(m => m.default as Messages),
uk: () => import('../../locales/uk.json').then(m => m.default as Messages),
zh: () => import('../../locales/zh.json').then(m => m.default as Messages),
ar: () => import('../../locales/ar.json').then(m => m.default as Messages), ar: () => import('../../locales/ar.json').then(m => m.default as Messages),
he: () => import('../../locales/he.json').then(m => m.default as Messages),
}; };
// ── Public API ─────────────────────────────────────────────────────────────── // ── Public API ───────────────────────────────────────────────────────────────
@ -140,8 +154,9 @@ export async function initI18n(): Promise<void> {
// Load PluralRules polyfill for WebKitGTK (needed for Arabic 6-form plurals) // Load PluralRules polyfill for WebKitGTK (needed for Arabic 6-form plurals)
await import('@formatjs/intl-pluralrules/polyfill-force.js').catch(() => {}); await import('@formatjs/intl-pluralrules/polyfill-force.js').catch(() => {});
let savedLocale = 'en'; let savedLocale: string | null = null;
// 1. Try saved preference from settings
try { try {
const result = await appRpc?.request['settings.get']({ key: 'locale' }); const result = await appRpc?.request['settings.get']({ key: 'locale' });
if (result?.value && loaders[result.value]) { if (result?.value && loaders[result.value]) {
@ -149,5 +164,16 @@ export async function initI18n(): Promise<void> {
} }
} catch { /* use default */ } } catch { /* use default */ }
// 2. If no saved preference, detect system language
if (!savedLocale) {
const systemLang = navigator.language ?? navigator.languages?.[0] ?? 'en';
const langBase = systemLang.split('-')[0].toLowerCase(); // 'en-US' → 'en'
if (loaders[langBase]) {
savedLocale = langBase;
} else {
savedLocale = 'en'; // fallback
}
}
await setLocale(savedLocale); await setLocale(savedLocale);
} }