Support for file prefix in locales.files config
Describe the feature
Currently, the files property in the locales configuration only supports an array of file paths. I would like to propose supporting a prefix option for each file to namespace its translations when loaded.
i18n: {
locales: [
{
code: "en",
name: "English",
files: [
{ path: "en/common.json", prefix: "common" },
{ path: "en/checkout.json", prefix: "checkout" },
{ path: "en/order.json", prefix: "order" }
]
}
]
}
With this configuration, the keys inside en/checkout.json would be accessible via t('checkout.key'), while en/common.json would be t('common.key'), and so on.
Additional information
- [ ] Would you be willing to help implement this feature?
- [ ] Could this feature be implemented as a module?
Final checks
- [x] Read the contribution guide (The contribution guideline of nuxt-modules/i18n is compliant with Nuxt too).
- [x] Check existing discussions and issues.
The files entry is about support multiple country languages where you can share a lot of messages from base language.
You can use specific keys inside the json file, check elk.zone file for example es.json, es-ES.json and es-419.json in the locales folder: https://github.com/elk-zone/elk/tree/main/locales
You can use multiple files using a custom key or keys, the way you use those keys won't change.
@userquin
I have the following localization directory structure:
client/locales
├── en/
│ ├── activity.json
│ ├── cart.json
│ ├── checkout.json
│ └── ... (other page/module-specific files)
├── en.ts
Each language folder (like en/) contains JSON files that are split by page or module.
What I want is lazy loading of translations based on both language and page/module, rather than loading all translations for a language at once. This would improve performance by only loading the translation files needed for the current page.
The big problem with your approach is using multiple files in the same page, what should i18n load?
What you're describing is localize pages using multiple i18n feature files... In the worst case, you will end up loading all the files.
I am working on optimizations in v10 that aim to achieve the same thing but without the need for splitting translation files per page yourself by loading/stripping messages based on translation keys used during SSR
So far there is no localization file subloading when creating a deeper architecture, so we do the following example: create locales/en/product/some.json
and in order for us to address the path product.some.accountInfo.email, we need the following architecture inside the some.json file:
{
"product" : {
"some" : {
"accountInfo" : {
"email" : "some"
}
}
}
}
here's how I fixed it.
I am working on optimizations in v10 that aim to achieve the same thing but without the need for splitting translation files per page yourself by loading/stripping messages based on translation keys used during SSR
So, this feature will just tree-shake messages per page? nice ❤
@RGon-c Now I only use the config with the following like, but not use files config:
defaultLocale: 'en',
detectBrowserLanguage: {
useCookie: true,
fallbackLocale: 'en',
cookieKey: 'i18n_redirected'
},
strategy: 'prefix_except_default',
vueI18n: path.resolve(__dirname, './client/locales/i18n.config.ts'),
client/locales/en.ts
// Import JSON files
const modules = import.meta.glob('./en/*.json', { eager: true });
// Specify a more concrete JSON format type
const translations: Record<string, any> = {};
// Process each JSON module
Object.entries(modules).forEach(([path, module]) => {
const moduleName = path.replace('./en/', '').replace('.json', '');
translations[moduleName] = module;
});
export default translations;```
@BobbieGoede In 9.x I can use
// Import JSON files
const modules = import.meta.glob('./en/*.json', { eager: true });
// Specify a more concrete JSON format type
const translations: Record<string, any> = {};
// Process each JSON module
Object.entries(modules).forEach(([path, module]) => {
const moduleName = path.replace('./en/', '').replace('.json', '');
translations[moduleName] = module;
});
export default translations;
but In 10.x version caught error
ERROR [uncaughtException] globalThis._importMeta_.glob is not a function
at .nuxt/dev/index.mjs:112516:43
at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
at async loadESM (node:internal/process/esm_loader:34:7)
at async handleMainPromise (node:internal/modules/run_main:113:12)
ℹ Error: Cannot access 'renderer$1' before initialization
⁃ (.nuxt/dev/index.mjs:111715:65)
111710 ┃ components: {}
111711 ┃ };
111712 ┃ return ctx;
111713 ┃ }
111714 ┃
❯ 111715 ┃ const _lazy_aocgtT = () => Promise.resolve().then(function () { return renderer$1; });
111716 ┃
111717 ┃ const handlers = [
111718 ┃ { route: '/__nuxt_error', handler: _lazy_aocgtT, lazy: true, middleware: false, method: undefined },
111719 ┃ { route: '', handler: _x4Lo86, lazy: false, middleware: false, method: undefined },
111720 ┃ { route: '/_i18n/:locale/messages.json', handler: _v_3KeN, lazy: false, middleware: false, method: undefined },
⁃ at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
⁃ (async file://node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2003:19)
⁃ at async Object.callAsync (node_modules/.pnpm/[email protected]/node_modules/unctx/dist/index.mjs:72:16)
⁃ at async Server.toNodeHandle (node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2295:7)
@icai Could you open a new issue with a minimal reproduction? It sounds like a regression and it should be addressed.
@BobbieGoede In 9.x I can use
// Import JSON files const modules = import.meta.glob('./en/*.json', { eager: true });
// Specify a more concrete JSON format type const translations: Record<string, any> = {};
// Process each JSON module Object.entries(modules).forEach(([path, module]) => { const moduleName = path.replace('./en/', '').replace('.json', ''); translations[moduleName] = module; });
export default translations; but In 10.x version caught error
ERROR [uncaughtException] globalThis._importMeta_.glob is not a function at .nuxt/dev/index.mjs:112516:43 at ModuleJob.run (node:internal/modules/esm/module_job:218:25) at async ModuleLoader.import (node:internal/modules/esm/loader:329:24) at async loadESM (node:internal/process/esm_loader:34:7) at async handleMainPromise (node:internal/modules/run_main:113:12)ℹ Error: Cannot access 'renderer$1' before initialization ⁃ (.nuxt/dev/index.mjs:111715:65) 111710 ┃ components: {} 111711 ┃ }; 111712 ┃ return ctx; 111713 ┃ } 111714 ┃ ❯ 111715 ┃ const _lazy_aocgtT = () => Promise.resolve().then(function () { return renderer$1; }); 111716 ┃ 111717 ┃ const handlers = [ 111718 ┃ { route: '/__nuxt_error', handler: _lazy_aocgtT, lazy: true, middleware: false, method: undefined }, 111719 ┃ { route: '', handler: _x4Lo86, lazy: false, middleware: false, method: undefined }, 111720 ┃ { route: '/_i18n/:locale/messages.json', handler: _v_3KeN, lazy: false, middleware: false, method: undefined }, ⁃ at process.processTicksAndRejections (node:internal/process/task_queues:95:5) ⁃ (async file://node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2003:19) ⁃ at async Object.callAsync (node_modules/.pnpm/[email protected]/node_modules/unctx/dist/index.mjs:72:16) ⁃ at async Server.toNodeHandle (node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2295:7)
I encountered the same issue: the code works in v9, and in v10 it works during development, but when I build and preview the project using the preview command, I get the same error.
Here is my code:
// en-US.ts
export default defineI18nLocale(() => {
const importAllTranslations = import.meta.glob(
['./en-US/*.ts'],
{ eager: true, import: 'default' }
);
const combinedTranslations = Object.values(importAllTranslations).reduce(
(acc: any, cur: any) => Object.assign(acc, cur),
{},
);
return combinedTranslations;
});
import.meta.glob does not work with nitro (v2) https://github.com/nitrojs/nitro/issues/1671, this limitation was less noticeable in nuxt-i18n v9 since messages did not necessarily load from the nitro side, which does happen by default in v10.