|
1 | | -import arCommands from '@frontend-locales/ar/commands.json' with { type: 'json' } |
2 | | -import ar from '@frontend-locales/ar/main.json' with { type: 'json' } |
3 | | -import arNodes from '@frontend-locales/ar/nodeDefs.json' with { type: 'json' } |
4 | | -import arSettings from '@frontend-locales/ar/settings.json' with { type: 'json' } |
| 1 | +// Import only English locale eagerly as the default/fallback |
| 2 | +// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*), |
| 3 | +// but these are properly configured in tsconfig.json and resolved by Vite at build time. |
| 4 | +// eslint-disable-next-line import-x/no-unresolved |
5 | 5 | import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' } |
| 6 | +// eslint-disable-next-line import-x/no-unresolved |
6 | 7 | import en from '@frontend-locales/en/main.json' with { type: 'json' } |
| 8 | +// eslint-disable-next-line import-x/no-unresolved |
7 | 9 | import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' } |
| 10 | +// eslint-disable-next-line import-x/no-unresolved |
8 | 11 | import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' } |
9 | | -import esCommands from '@frontend-locales/es/commands.json' with { type: 'json' } |
10 | | -import es from '@frontend-locales/es/main.json' with { type: 'json' } |
11 | | -import esNodes from '@frontend-locales/es/nodeDefs.json' with { type: 'json' } |
12 | | -import esSettings from '@frontend-locales/es/settings.json' with { type: 'json' } |
13 | | -import frCommands from '@frontend-locales/fr/commands.json' with { type: 'json' } |
14 | | -import fr from '@frontend-locales/fr/main.json' with { type: 'json' } |
15 | | -import frNodes from '@frontend-locales/fr/nodeDefs.json' with { type: 'json' } |
16 | | -import frSettings from '@frontend-locales/fr/settings.json' with { type: 'json' } |
17 | | -import jaCommands from '@frontend-locales/ja/commands.json' with { type: 'json' } |
18 | | -import ja from '@frontend-locales/ja/main.json' with { type: 'json' } |
19 | | -import jaNodes from '@frontend-locales/ja/nodeDefs.json' with { type: 'json' } |
20 | | -import jaSettings from '@frontend-locales/ja/settings.json' with { type: 'json' } |
21 | | -import koCommands from '@frontend-locales/ko/commands.json' with { type: 'json' } |
22 | | -import ko from '@frontend-locales/ko/main.json' with { type: 'json' } |
23 | | -import koNodes from '@frontend-locales/ko/nodeDefs.json' with { type: 'json' } |
24 | | -import koSettings from '@frontend-locales/ko/settings.json' with { type: 'json' } |
25 | | -import ruCommands from '@frontend-locales/ru/commands.json' with { type: 'json' } |
26 | | -import ru from '@frontend-locales/ru/main.json' with { type: 'json' } |
27 | | -import ruNodes from '@frontend-locales/ru/nodeDefs.json' with { type: 'json' } |
28 | | -import ruSettings from '@frontend-locales/ru/settings.json' with { type: 'json' } |
29 | | -import trCommands from '@frontend-locales/tr/commands.json' with { type: 'json' } |
30 | | -import tr from '@frontend-locales/tr/main.json' with { type: 'json' } |
31 | | -import trNodes from '@frontend-locales/tr/nodeDefs.json' with { type: 'json' } |
32 | | -import trSettings from '@frontend-locales/tr/settings.json' with { type: 'json' } |
33 | | -import zhTWCommands from '@frontend-locales/zh-TW/commands.json' with { type: 'json' } |
34 | | -import zhTW from '@frontend-locales/zh-TW/main.json' with { type: 'json' } |
35 | | -import zhTWNodes from '@frontend-locales/zh-TW/nodeDefs.json' with { type: 'json' } |
36 | | -import zhTWSettings from '@frontend-locales/zh-TW/settings.json' with { type: 'json' } |
37 | | -import zhCommands from '@frontend-locales/zh/commands.json' with { type: 'json' } |
38 | | -import zh from '@frontend-locales/zh/main.json' with { type: 'json' } |
39 | | -import zhNodes from '@frontend-locales/zh/nodeDefs.json' with { type: 'json' } |
40 | | -import zhSettings from '@frontend-locales/zh/settings.json' with { type: 'json' } |
41 | 12 | import { createI18n } from 'vue-i18n' |
42 | 13 |
|
43 | | -function buildLocale<M, N, C, S>(main: M, nodes: N, commands: C, settings: S) { |
| 14 | +function buildLocale< |
| 15 | + M extends Record<string, unknown>, |
| 16 | + N extends Record<string, unknown>, |
| 17 | + C extends Record<string, unknown>, |
| 18 | + S extends Record<string, unknown> |
| 19 | +>(main: M, nodes: N, commands: C, settings: S) { |
44 | 20 | return { |
45 | 21 | ...main, |
46 | 22 | nodeDefs: nodes, |
47 | 23 | commands: commands, |
48 | 24 | settings: settings |
| 25 | + } as M & { nodeDefs: N; commands: C; settings: S } |
| 26 | +} |
| 27 | + |
| 28 | +// Locale loader map - dynamically import locales only when needed |
| 29 | +// ESLint cannot statically resolve these dynamic imports, but they are valid at build time |
| 30 | +/* eslint-disable import-x/no-unresolved */ |
| 31 | +const localeLoaders: Record< |
| 32 | + string, |
| 33 | + () => Promise<{ default: Record<string, unknown> }> |
| 34 | +> = { |
| 35 | + ar: () => import('@frontend-locales/ar/main.json'), |
| 36 | + es: () => import('@frontend-locales/es/main.json'), |
| 37 | + fr: () => import('@frontend-locales/fr/main.json'), |
| 38 | + ja: () => import('@frontend-locales/ja/main.json'), |
| 39 | + ko: () => import('@frontend-locales/ko/main.json'), |
| 40 | + ru: () => import('@frontend-locales/ru/main.json'), |
| 41 | + tr: () => import('@frontend-locales/tr/main.json'), |
| 42 | + zh: () => import('@frontend-locales/zh/main.json'), |
| 43 | + 'zh-TW': () => import('@frontend-locales/zh-TW/main.json') |
| 44 | +} |
| 45 | + |
| 46 | +const nodeDefsLoaders: Record< |
| 47 | + string, |
| 48 | + () => Promise<{ default: Record<string, unknown> }> |
| 49 | +> = { |
| 50 | + ar: () => import('@frontend-locales/ar/nodeDefs.json'), |
| 51 | + es: () => import('@frontend-locales/es/nodeDefs.json'), |
| 52 | + fr: () => import('@frontend-locales/fr/nodeDefs.json'), |
| 53 | + ja: () => import('@frontend-locales/ja/nodeDefs.json'), |
| 54 | + ko: () => import('@frontend-locales/ko/nodeDefs.json'), |
| 55 | + ru: () => import('@frontend-locales/ru/nodeDefs.json'), |
| 56 | + tr: () => import('@frontend-locales/tr/nodeDefs.json'), |
| 57 | + zh: () => import('@frontend-locales/zh/nodeDefs.json'), |
| 58 | + 'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json') |
| 59 | +} |
| 60 | + |
| 61 | +const commandsLoaders: Record< |
| 62 | + string, |
| 63 | + () => Promise<{ default: Record<string, unknown> }> |
| 64 | +> = { |
| 65 | + ar: () => import('@frontend-locales/ar/commands.json'), |
| 66 | + es: () => import('@frontend-locales/es/commands.json'), |
| 67 | + fr: () => import('@frontend-locales/fr/commands.json'), |
| 68 | + ja: () => import('@frontend-locales/ja/commands.json'), |
| 69 | + ko: () => import('@frontend-locales/ko/commands.json'), |
| 70 | + ru: () => import('@frontend-locales/ru/commands.json'), |
| 71 | + tr: () => import('@frontend-locales/tr/commands.json'), |
| 72 | + zh: () => import('@frontend-locales/zh/commands.json'), |
| 73 | + 'zh-TW': () => import('@frontend-locales/zh-TW/commands.json') |
| 74 | +} |
| 75 | + |
| 76 | +const settingsLoaders: Record< |
| 77 | + string, |
| 78 | + () => Promise<{ default: Record<string, unknown> }> |
| 79 | +> = { |
| 80 | + ar: () => import('@frontend-locales/ar/settings.json'), |
| 81 | + es: () => import('@frontend-locales/es/settings.json'), |
| 82 | + fr: () => import('@frontend-locales/fr/settings.json'), |
| 83 | + ja: () => import('@frontend-locales/ja/settings.json'), |
| 84 | + ko: () => import('@frontend-locales/ko/settings.json'), |
| 85 | + ru: () => import('@frontend-locales/ru/settings.json'), |
| 86 | + tr: () => import('@frontend-locales/tr/settings.json'), |
| 87 | + zh: () => import('@frontend-locales/zh/settings.json'), |
| 88 | + 'zh-TW': () => import('@frontend-locales/zh-TW/settings.json') |
| 89 | +} |
| 90 | + |
| 91 | +// Track which locales have been loaded |
| 92 | +const loadedLocales = new Set<string>(['en']) |
| 93 | + |
| 94 | +// Track locales currently being loaded to prevent race conditions |
| 95 | +const loadingLocales = new Map<string, Promise<void>>() |
| 96 | + |
| 97 | +/** |
| 98 | + * Dynamically load a locale and its associated files (nodeDefs, commands, settings) |
| 99 | + */ |
| 100 | +export async function loadLocale(locale: string): Promise<void> { |
| 101 | + if (loadedLocales.has(locale)) { |
| 102 | + return |
| 103 | + } |
| 104 | + |
| 105 | + // If already loading, return the existing promise to prevent duplicate loads |
| 106 | + const existingLoad = loadingLocales.get(locale) |
| 107 | + if (existingLoad) { |
| 108 | + return existingLoad |
| 109 | + } |
| 110 | + |
| 111 | + const loader = localeLoaders[locale] |
| 112 | + const nodeDefsLoader = nodeDefsLoaders[locale] |
| 113 | + const commandsLoader = commandsLoaders[locale] |
| 114 | + const settingsLoader = settingsLoaders[locale] |
| 115 | + |
| 116 | + if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) { |
| 117 | + console.warn(`Locale "${locale}" is not supported`) |
| 118 | + return |
49 | 119 | } |
| 120 | + |
| 121 | + // Create and track the loading promise |
| 122 | + const loadPromise = (async () => { |
| 123 | + try { |
| 124 | + const [main, nodes, commands, settings] = await Promise.all([ |
| 125 | + loader(), |
| 126 | + nodeDefsLoader(), |
| 127 | + commandsLoader(), |
| 128 | + settingsLoader() |
| 129 | + ]) |
| 130 | + |
| 131 | + const messages = buildLocale( |
| 132 | + main.default, |
| 133 | + nodes.default, |
| 134 | + commands.default, |
| 135 | + settings.default |
| 136 | + ) |
| 137 | + |
| 138 | + i18n.global.setLocaleMessage(locale, messages as LocaleMessages) |
| 139 | + loadedLocales.add(locale) |
| 140 | + } catch (error) { |
| 141 | + console.error(`Failed to load locale "${locale}":`, error) |
| 142 | + throw error |
| 143 | + } finally { |
| 144 | + // Clean up the loading promise once complete |
| 145 | + loadingLocales.delete(locale) |
| 146 | + } |
| 147 | + })() |
| 148 | + |
| 149 | + loadingLocales.set(locale, loadPromise) |
| 150 | + return loadPromise |
50 | 151 | } |
51 | 152 |
|
| 153 | +// Only include English in the initial bundle |
52 | 154 | const messages = { |
53 | | - en: buildLocale(en, enNodes, enCommands, enSettings), |
54 | | - zh: buildLocale(zh, zhNodes, zhCommands, zhSettings), |
55 | | - 'zh-TW': buildLocale(zhTW, zhTWNodes, zhTWCommands, zhTWSettings), |
56 | | - ru: buildLocale(ru, ruNodes, ruCommands, ruSettings), |
57 | | - ja: buildLocale(ja, jaNodes, jaCommands, jaSettings), |
58 | | - ko: buildLocale(ko, koNodes, koCommands, koSettings), |
59 | | - fr: buildLocale(fr, frNodes, frCommands, frSettings), |
60 | | - es: buildLocale(es, esNodes, esCommands, esSettings), |
61 | | - ar: buildLocale(ar, arNodes, arCommands, arSettings), |
62 | | - tr: buildLocale(tr, trNodes, trCommands, trSettings) |
| 155 | + en: buildLocale(en, enNodes, enCommands, enSettings) |
63 | 156 | } |
64 | 157 |
|
| 158 | +// Type for locale messages - inferred from the English locale structure |
| 159 | +type LocaleMessages = typeof messages.en |
| 160 | + |
65 | 161 | export const i18n = createI18n({ |
66 | 162 | // Must set `false`, as Vue I18n Legacy API is for Vue 2 |
67 | 163 | legacy: false, |
|
0 commit comments