feat(i18n): add multi-language support (ja/ko/vi/ru/zh-TW) with system auto-detection
- Extend AppLanguage to 7 locales: zh-CN, zh-TW, en, ja, ko, vi, ru - Auto-detect system language via navigator.languages; fallback to English - Add BCP47 locale mapping for date formatting and sorting - Create 50 translation JSON files (5 languages × 10 namespaces, 1137 keys each) - Update language selector with native language names - Extend locale key parity test to cover all 7 languages (70 assertions) - Add VNC/RDP desktop, serial terminal, AI agent, port forwarding, and SFTP features - Add credential encryption, host metrics, session backup crypto, and zmodem bridge - Update Rust backend: desktop services, host metrics, SSH/SFTP enhancements
This commit is contained in:
1 parent
fea6d8db04
commit
b0d0e3a5f3
301 files changed
+28002
-2705
No files matched your search
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { I18N_NAMESPACES } from '../../src/i18n'
|
||||
import { APP_LANGUAGES, type AppLanguage } from '../../src/i18n/detectLocale'
|
||||
|
||||
function flattenKeys(value: unknown, prefix = ''): string[] {
|
||||
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
||||
return prefix ? [prefix] : []
|
||||
}
|
||||
|
||||
const entries = Object.entries(value as Record<string, unknown>)
|
||||
if (entries.length === 0) return prefix ? [prefix] : []
|
||||
|
||||
return entries.flatMap(([key, nested]) => {
|
||||
const next = prefix ? `${prefix}.${key}` : key
|
||||
if (nested !== null && typeof nested === 'object' && !Array.isArray(nested)) {
|
||||
const childKeys = flattenKeys(nested, next)
|
||||
return childKeys.length > 0 ? childKeys : [next]
|
||||
}
|
||||
return [next]
|
||||
})
|
||||
}
|
||||
|
||||
async function loadLocale(namespace: string, language: AppLanguage) {
|
||||
const mod = await import(`../../src/i18n/locales/${language}/${namespace}.json`)
|
||||
return mod.default as Record<string, unknown>
|
||||
}
|
||||
|
||||
describe('i18n locale key parity', () => {
|
||||
for (const namespace of I18N_NAMESPACES) {
|
||||
for (const language of APP_LANGUAGES) {
|
||||
it(`${language} keys match en for ${namespace}`, async () => {
|
||||
const [locale, en] = await Promise.all([
|
||||
loadLocale(namespace, language),
|
||||
loadLocale(namespace, 'en'),
|
||||
])
|
||||
|
||||
const localeKeys = flattenKeys(locale).sort()
|
||||
const enKeys = flattenKeys(en).sort()
|
||||
|
||||
expect(localeKeys).toEqual(enKeys)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user