From 01d1caa21bbc0f4e188eba9a63c72c18a9620448 Mon Sep 17 00:00:00 2001 From: Dorian Date: Sat, 14 Mar 2026 05:41:33 +0000 Subject: [PATCH] feat: add language selector and lazy-load i18n infrastructure Updated i18n.ts with SUPPORTED_LOCALES, setLocale() lazy loading, localStorage persistence. Added language selector in Settings.vue. Co-Authored-By: Claude Opus 4.6 (1M context) --- neode-ui/src/i18n.ts | 37 +++++++++++++++++++++++++++++++-- neode-ui/src/views/Settings.vue | 25 +++++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/neode-ui/src/i18n.ts b/neode-ui/src/i18n.ts index 16dd50b2..fcd23793 100644 --- a/neode-ui/src/i18n.ts +++ b/neode-ui/src/i18n.ts @@ -3,13 +3,46 @@ import en from './locales/en.json' export type MessageSchema = typeof en -const i18n = createI18n<[MessageSchema], 'en'>({ +export const SUPPORTED_LOCALES = [ + { code: 'en', name: 'English', flag: '🇬🇧' }, + { code: 'es', name: 'Español', flag: '🇪🇸' }, +] as const + +export type SupportedLocale = typeof SUPPORTED_LOCALES[number]['code'] + +const savedLocale = (typeof localStorage !== 'undefined' + ? localStorage.getItem('neode_locale') + : null) as SupportedLocale | null + +const i18n = createI18n({ legacy: false, - locale: 'en', + locale: savedLocale || 'en', fallbackLocale: 'en', messages: { en, }, }) +/** Lazy-load a locale's messages and switch to it. */ +export async function setLocale(locale: SupportedLocale) { + if (locale === 'en') { + i18n.global.locale = 'en' as never + localStorage.setItem('neode_locale', 'en') + return + } + + if (!(i18n.global.availableLocales as string[]).includes(locale)) { + const messages = await import(`./locales/${locale}.json`) + ;(i18n.global as ReturnType['global']).setLocaleMessage(locale, messages.default) + } + + i18n.global.locale = locale as never + localStorage.setItem('neode_locale', locale) +} + +// Load saved locale on startup +if (savedLocale && savedLocale !== 'en') { + setLocale(savedLocale) +} + export default i18n diff --git a/neode-ui/src/views/Settings.vue b/neode-ui/src/views/Settings.vue index 3fb811d0..b7c9878f 100644 --- a/neode-ui/src/views/Settings.vue +++ b/neode-ui/src/views/Settings.vue @@ -453,6 +453,23 @@ + +
+

Language

+

Choose your preferred language

+
+ +
+
+

{{ t('settings.claudeAuth') }}

@@ -818,6 +835,7 @@ import { computed, ref, onMounted, nextTick } from 'vue' import { useRouter } from 'vue-router' import { useI18n } from 'vue-i18n' +import { SUPPORTED_LOCALES, setLocale, type SupportedLocale } from '@/i18n' import { useAppStore } from '../stores/app' import { useUIModeStore } from '@/stores/uiMode' import { useAIPermissionsStore, AI_PERMISSION_CATEGORIES } from '@/stores/aiPermissions' @@ -827,8 +845,13 @@ import { useModalKeyboard } from '@/composables/useModalKeyboard' import type { UIMode } from '@/types/api' const router = useRouter() -const { t } = useI18n() +const { t, locale } = useI18n() const store = useAppStore() +const supportedLocales = SUPPORTED_LOCALES +const currentLocale = computed(() => locale.value) +async function changeLocale(code: string) { + await setLocale(code as SupportedLocale) +} const uiMode = useUIModeStore() const aiPermissions = useAIPermissionsStore() const aiCategoryGroups = computed(() => {