Skip to content

Commit d248362

Browse files
committed
Add i18n vendor overlays and dynamic bundling
- Add opt-in vendor overlays under src/env/locales/<env> (and optional variant), merged on top of base locales at runtime. - Auto-discover and bundle all base locale JSON files in src/locales/. - Example: move dump type labels under pageDumps.dumpTypes; read vendor-only dump labels from overlays. - Docs: update i18n guidelines and env README (formatting fixes). - Tests: add focused unit tests for overlays and locale aliases. Tested: - Unit: i18n.locale-alias.spec.js, i18n.vendor.spec.js (passing) - Manual: Verified dynamic locale discovery and overlay merge in UI Change-Id: I8eae2bfec0e9622bafdafac3168dbf96650e8ae8 Signed-off-by: jason westover <[email protected]>
1 parent 0c7f684 commit d248362

File tree

15 files changed

+383
-31
lines changed

15 files changed

+383
-31
lines changed

docs/guide/guidelines/internationalization.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,61 @@ this.$bvModal
7575
autoFocusButton: 'ok',
7676
})
7777
```
78+
79+
## Vendor overlays (environment-specific translations)
80+
81+
To keep the base translation files vendor-neutral, vendor-specific strings live
82+
under `src/env/locales/<envName>/`.
83+
84+
- Place shared vendor strings in the vendor root folder (e.g.,
85+
`src/env/locales/vendor/`)
86+
- Place project-specific overrides in the variant folder when needed (e.g.,
87+
`src/env/locales/vendor-variant/`)
88+
- Merge order at runtime:
89+
1. Base locales from `src/locales/` (auto-discovered)
90+
2. Vendor root overlays (e.g., `src/env/locales/vendor/`)
91+
3. Variant overlays (e.g., `src/env/locales/vendor-variant/`)
92+
- Variant keys overwrite vendor root keys on conflict
93+
94+
Notes:
95+
96+
- All JSON files under `src/locales/` are bundled automatically.
97+
- All JSON files under `src/env/locales/` that match the active environment are
98+
also bundled.
99+
- If multiple vendor projects share strings, prefer the vendor root folder so
100+
variants don’t duplicate content.
101+
102+
Example: moving vendor-only dump type labels
103+
104+
```json
105+
// src/locales/en-US.json (base)
106+
{
107+
"pageDumps": {
108+
"dumpTypes": {}
109+
}
110+
}
111+
112+
// src/env/locales/nvidia/en-US.json (overlay)
113+
{
114+
"pageDumps": {
115+
"dumpTypes": {
116+
"hmcDump": "HMC dump",
117+
"bmcDump": "BMC dump",
118+
"systemBmcDump": "System [BMC] dump (disruptive)",
119+
"systemHgxDump": "System [HGX] dump (disruptive)"
120+
}
121+
}
122+
}
123+
```
124+
125+
### Locale codes
126+
127+
We support aliasing short codes to our canonical locales:
128+
129+
- `en``en-US`
130+
- `ru``ru-RU`
131+
- `zh``zh-CN`
132+
- `ka``ka-GE`
133+
134+
If a short code is stored (e.g., in localStorage), it will be normalized at app
135+
startup.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"eslint-plugin-prettier": "5.1.3",
6161
"eslint-plugin-vue": "9.2.0",
6262
"eslint-scope": "7.1.1",
63+
"file-loader": "6.2.0",
6364
"lint-staged": "13.0.3",
6465
"postcss-loader": "8.1.1",
6566
"prettier": "3.2.5",

src/env/locales/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Vendor locale overlays
2+
3+
This directory contains environment/vendor-specific translation bundles that are
4+
merged on top of the base, vendor-neutral locales in `src/locales/`.
5+
6+
## Structure
7+
8+
```text
9+
src/env/locales/
10+
<vendor>/
11+
en-US.json
12+
ka-GE.json
13+
ru-RU.json
14+
zh-CN.json
15+
<vendor-variant>/
16+
en-US.json # optional, only when variant overrides vendor root
17+
ka-GE.json # optional
18+
ru-RU.json # optional
19+
zh-CN.json # optional
20+
```
21+
22+
Examples:
23+
24+
- Shared vendor folder: `src/env/locales/nvidia/`
25+
- Variant folder: `src/env/locales/nvidia-gb/`
26+
27+
## Merge order at runtime
28+
29+
1. Base locales from `src/locales/` (auto-discovered)
30+
2. Vendor root overlays (e.g., `src/env/locales/nvidia/`)
31+
3. Variant overlays (e.g., `src/env/locales/nvidia-gb/`)
32+
33+
Variant keys overwrite vendor root keys on conflict.
34+
35+
## Guidelines
36+
37+
- Keep `src/locales/` vendor‑neutral. Put vendor‑specific strings here.
38+
- Prefer the vendor root folder when multiple projects share strings to avoid
39+
duplication.
40+
- Only add a variant folder if it truly needs to override vendor root strings.
41+
- File names must match locale codes (e.g., `en-US.json`, `ru-RU.json`,
42+
`zh-CN.json`, `ka-GE.json`).
43+
- Use 4‑space indentation; alphabetize object keys for readability.
44+
- JSON must be valid (no trailing commas or comments).
45+
46+
## Environment selection
47+
48+
The active environment is selected by `VUE_APP_ENV_NAME` (e.g., `nvidia`,
49+
`nvidia-gb`). See the `.env.*` files at the repo root (e.g., `.env.nvidia-gb`).
50+
51+
## Bundling
52+
53+
- All JSON files under `src/locales/` are bundled automatically.
54+
- Matching overlays under `src/env/locales/<env>` are also bundled and merged at
55+
app start.
56+
57+
## Testing
58+
59+
Focused unit tests exist for overlays and fallback:
60+
61+
- `npm run test:unit -- i18n.vendor.spec.js`
62+
- `npm run test:unit -- i18n.locale-alias.spec.js`
63+
64+
These verify vendor root → variant merge behavior and locale alias handling.

src/env/locales/nvidia/en-US.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"pageDumps": {
3+
"dumpTypes": {
4+
"bmcDump": "BMC dump",
5+
"hmcDump": "HMC dump",
6+
"systemBmcDump": "System [BMC] dump (disruptive)",
7+
"systemHgxDump": "System [HGX] dump (disruptive)",
8+
"systemDump": "System [BMC] dump (disruptive)"
9+
}
10+
}
11+
}

src/env/locales/nvidia/ka-GE.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"pageDumps": {
3+
"dumpTypes": {
4+
"bmcDump": "BMC-ის დამპი",
5+
"hmcDump": "HMC დამპი",
6+
"systemBmcDump": "სისტემის [BMC] დამპი (დროებით წყვეტს სისტემის მუშაობას)",
7+
"systemHgxDump": "სისტემის [HGX] დამპი (დროებით წყვეტს სისტემის მუშაობას)",
8+
"systemDump": "სისტემის [BMC] დამპი (დროებით წყვეტს სისტემის მუშაობას)"
9+
}
10+
}
11+
}

src/env/locales/nvidia/ru-RU.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"pageDumps": {
3+
"dumpTypes": {
4+
"bmcDump": "BMC дамп",
5+
"hmcDump": "HMC дамп",
6+
"systemBmcDump": "Системный [BMC] дамп (разрушительный)",
7+
"systemHgxDump": "Системный [HGX] дамп (разрушительный)",
8+
"systemDump": "Системный [BMC] дамп (разрушительный)"
9+
}
10+
}
11+
}

src/env/locales/nvidia/zh-CN.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"pageDumps": {
3+
"dumpTypes": {
4+
"bmcDump": "BMC 转储",
5+
"hmcDump": "HMC 转储",
6+
"systemBmcDump": "系统 [BMC] 转储(破坏性)",
7+
"systemHgxDump": "系统 [HGX] 转储(破坏性)",
8+
"systemDump": "系统 [BMC] 转储(破坏性)"
9+
}
10+
}
11+
}

src/i18n.js

Lines changed: 88 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,94 @@
11
import { createI18n } from 'vue-i18n';
2+
import { deepMerge } from './utilities/objectUtils';
23

3-
import en_us from './locales/en-US.json';
4-
import ru_ru from './locales/ru-RU.json';
5-
import ka_ge from './locales/ka-GE.json';
6-
7-
function loadLocaleMessages() {
8-
const messages = {
9-
'en-US': en_us,
10-
'ka-GE': ka_ge,
11-
'ru-RU': ru_ru,
12-
};
4+
export function loadBaseLocaleMessages() {
5+
const context = require.context(
6+
'./locales',
7+
true,
8+
/[A-Za-z0-9-_,\s]+\.json$/i,
9+
);
10+
const messages = {};
11+
context.keys().forEach((key) => {
12+
const match = key.match(/([A-Za-z0-9-_]+)\.json$/i);
13+
if (!match) return;
14+
const locale = match[1];
15+
const mod = context(key);
16+
messages[locale] = mod && mod.default ? mod.default : mod;
17+
});
1318
return messages;
1419
}
1520

16-
const i18n = createI18n({
17-
// Get default locale from local storage
18-
locale: window.localStorage.getItem('storedLanguage'),
19-
// Locales that don't exist will fallback to English
20-
fallbackLocale: 'en-US',
21-
// Falling back to fallbackLocale generates two console warnings
22-
// Silent fallback suppresses console warnings when using fallback
23-
silentFallbackWarn: true,
24-
messages: loadLocaleMessages(),
25-
globalInjection: false,
26-
legacy: false,
27-
});
21+
export function loadEnvLocaleMessages(envName) {
22+
if (!envName) return {};
23+
const envMessages = {};
24+
const envLocales = require.context(
25+
'./env/locales',
26+
true,
27+
/[A-Za-z0-9-_,\s]+\.json$/i,
28+
);
29+
const vendorRoot = String(envName).split('-')[0];
30+
const candidates =
31+
vendorRoot && vendorRoot !== envName ? [vendorRoot, envName] : [envName];
32+
candidates.forEach((candidate) => {
33+
envLocales.keys().forEach((key) => {
34+
if (!key.includes(`/${candidate}/`)) return;
35+
const localeMatch = key.match(/([A-Za-z0-9-_]+)\.json$/i);
36+
if (!localeMatch) return;
37+
const locale = localeMatch[1];
38+
const mod = envLocales(key);
39+
const bundle = mod && mod.default ? mod.default : mod;
40+
envMessages[locale] = deepMerge(envMessages[locale] || {}, bundle);
41+
});
42+
});
43+
return envMessages;
44+
}
45+
46+
export function createI18nInstance(
47+
envName,
48+
locale,
49+
loadEnv = loadEnvLocaleMessages,
50+
loadBase = loadBaseLocaleMessages,
51+
) {
52+
const base = loadBase();
53+
const env = loadEnv(envName);
54+
const messages = { ...base };
55+
Object.keys(env).forEach((loc) => {
56+
messages[loc] = deepMerge(base[loc] || {}, env[loc]);
57+
});
58+
59+
const addAlias = (alias, target) => {
60+
if (!messages[alias] && messages[target])
61+
messages[alias] = messages[target];
62+
};
63+
addAlias('en', 'en-US');
64+
addAlias('ru', 'ru-RU');
65+
addAlias('zh', 'zh-CN');
66+
addAlias('ka', 'ka-GE');
67+
68+
const normalize = (val) => {
69+
if (!val) return undefined;
70+
const s = String(val);
71+
if (s === 'en') return 'en-US';
72+
if (s === 'ru') return 'ru-RU';
73+
if (s === 'zh') return 'zh-CN';
74+
if (s === 'ka') return 'ka-GE';
75+
return s;
76+
};
77+
78+
return createI18n({
79+
locale: normalize(locale),
80+
// Locales that don't exist will fallback to English
81+
fallbackLocale: 'en-US',
82+
// Falling back to fallbackLocale generates two console warnings
83+
// Silent fallback suppresses console warnings when using fallback
84+
silentFallbackWarn: true,
85+
messages,
86+
globalInjection: false,
87+
legacy: false,
88+
});
89+
}
2890

29-
export default i18n;
91+
const envName = process.env.VUE_APP_ENV_NAME;
92+
// Get default locale from local storage
93+
const stored = window.localStorage.getItem('storedLanguage');
94+
export default createI18nInstance(envName, stored);

src/locales/en-US.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,13 @@
220220
"pageDumps": {
221221
"dumpsAvailableOnBmc": "Dumps available on BMC",
222222
"initiateDump": "Initiate dump",
223-
"form": {
223+
"dumpTypes": {
224224
"bmcDump": "BMC dump",
225+
"systemDump": "System dump (disruptive)"
226+
},
227+
"form": {
225228
"initiateDump": "Initiate dump",
226229
"selectDumpType": "Select dump type",
227-
"systemDump": "System dump (disruptive)",
228230
"systemDumpInfo": "System dumps will be offloaded to the operating system and will not appear in the table below."
229231
},
230232
"modal": {

src/locales/ka-GE.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,13 @@
220220
"pageDumps": {
221221
"dumpsAvailableOnBmc": "BMC-ზე ხელმისაწვდომი დამპები",
222222
"initiateDump": "დამპის შექმნა",
223-
"form": {
223+
"dumpTypes": {
224224
"bmcDump": "BMC-ის დამპი",
225+
"systemDump": "სისტემის დამპი (დროებით წყვეტს სისტემის მუშაობას)"
226+
},
227+
"form": {
225228
"initiateDump": "დამპის შექმნა",
226229
"selectDumpType": "აირჩით დამპის ტიპი",
227-
"systemDump": "სისტემის დამპი (დროებით წყვეტს სისტემის მუშაობას)",
228230
"systemDumpInfo": "სისტემის დამპები გადაეცემა ოპერაციულ სისტემას და ქვემოთ ცხრილში არ გამოჩნდება"
229231
},
230232
"modal": {

0 commit comments

Comments
 (0)