Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: read vue-i18n options from config path #1948

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 18 additions & 27 deletions docs/content/1.getting-started/2.basic-usage.md
Expand Up @@ -8,34 +8,37 @@ The basics to get started with the Nuxt i18n module is to translate with Vue I18

The basic to get started with **Nuxt i18n module** is to **translate with Vue I18n via the `vueI18n` option**

So, let's get started configuring the following `nuxt.config`:
So, let's get started by adding the module to `nuxt.config`:

```ts {}[nuxt.config.ts]
export default defineNuxtConfig({
modules: [
'@nuxtjs/i18n'
],
i18n: {
// add `vueI18n` option to `@nuxtjs/i18n` module options
vueI18n: {
legacy: false,
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
},
fr: {
welcome: 'Bienvenue'
}
}
vueI18n: { configPath: './i18n.config.ts' } // if you are using custom path
}
})
```

```ts {}[i18n.config.ts]
export default defineI18nConfig({
legacy: false,
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
},
fr: {
welcome: 'Bienvenue'
}
}
})
```

The `vueI18n` option is the same as `createI18n` function option of Vue I18n. `vueI18n` option is passed to the `createI18n` function via the nuxt plugin of this module internally.
The `defineI18nConfig` function is the same as `createI18n` function option of Vue I18n. The configuration is passed to the `createI18n` function via the nuxt plugin (runtime) of this module internally.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should name defineIVue18nConfig, not defineI18nConfig, because that is coniguration forvueI18n option. And some people might misunderstand it as a nuxt i18n setting.


For more details about the `vueI18n` option, see the [Vue I18n documentation](https://vue-i18n.intlify.dev/api/general.html#createi18n).
For more details about configuration, see the [Vue I18n documentation](https://vue-i18n.intlify.dev/api/general.html#createi18n).

Now, put (or edit) the following the page component in `pages` directory of you project:

Expand Down Expand Up @@ -103,18 +106,6 @@ For localizing links, you can use the code provided from the `locales` option as
i18n: {
+ locales: ['en', 'fr'], // used in URL path prefix
+ defaultLocale: 'en', // default locale of your project for Nuxt pages and routings
vueI18n: {
legacy: false,
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
},
fr: {
welcome: 'Bienvenue'
}
}
}
}
})
```
Expand Down
35 changes: 14 additions & 21 deletions docs/content/2.guide/11.locale-fallback.md
Expand Up @@ -7,27 +7,20 @@ How a fallback gets selected when a translation is missing.
**Nuxt i18n module** takes advantage of **Vue I18n** ability to handle localization fallback. It is possible to define a single fallback locale, an array of locales,
or a decision map for more specific needs.

```js [nuxt.config.js]
export default defineNuxtConfig({
// ...

i18n: {
vueI18n: {
fallbackLocale: 'en',
// or
fallbackLocale: ['en', 'fr'],
// or
fallbackLocale: {
'de-CH': ['fr', 'it'],
'zh-Hant': ['zh-Hans'],
'es-CL': ['es-AR'],
'es': ['en-GB'],
'pt': ['es-AR'],
'default': ['en', 'da']
}
}
},

```js [i18n.config.ts]
export default defineI18nConfig({
fallbackLocale: 'en',
// or
fallbackLocale: ['en', 'fr'],
// or
fallbackLocale: {
'de-CH': ['fr', 'it'],
'zh-Hant': ['zh-Hans'],
'es-CL': ['es-AR'],
'es': ['en-GB'],
'pt': ['es-AR'],
'default': ['en', 'da']
}
// ...
})
```
Expand Down
10 changes: 10 additions & 0 deletions docs/content/2.guide/15.migrating.md
Expand Up @@ -4,6 +4,10 @@ Follow this guide to upgrade from one major version to the other.

---

::alert{type="warning"}
Here, deprecated = removed
::

## Upgrading from `nuxtjs/i18n` v7.x

### Change the route key rules in `pages` option
Expand Down Expand Up @@ -177,3 +181,9 @@ The following APIs are no longer available:
- `localeRoute()`
- `localeLocation()`
- `switchLocalePath()`

### Deprecated `vueI18n` option to not accept `createI18n` options

This is to ensure stability and distinction between compile / build-time and runtime since `vue-i18n` is used in runtime.

You can continue defining `vue-i18n` options in `i18n.config.ts`. Refer to [Vue i18n](/options/vue-i18n) section and [basic usage](/getting-started/basic-usage/#translate-with-vue-i18n) for an example.
4 changes: 0 additions & 4 deletions docs/content/2.guide/8.lazy-load-translations.md
Expand Up @@ -164,10 +164,6 @@ export default defineNuxtConfig({
lazy: true,
langDir: 'lang',
defaultLocale: 'en',
vueI18n: {
// If fallback is needed, you need to define
fallbackLocale: 'en',
}
},

// ...
Expand Down
49 changes: 30 additions & 19 deletions docs/content/3.options/1.vue-i18n.md
Expand Up @@ -6,26 +6,37 @@ Related Vue I18n options.

## `vueI18n`

- type: `object` or `string`
- default: `{}`

Configuration for the `vue-i18n` library that is used internally by this module. See full documentation at https://vue-i18n.intlify.dev/api/general.html#createi18n

::alert{type="info"}

It's also supported to set this property to a path to a local configuration file. The file needs to export a function or plain object. If a function, it will be passed a Nuxt Context as a parameter. It's necessary to use that approach when overriding more complex types (like functions) that can't be stringified correctly.

```ts {}[~/vue-i18n.options.ts]
import type { I18nOptions } from 'vue-i18n'
import type { NuxtApp } from 'nuxt/dist/app/index'
- type: `object`
- default: `undefined`

Build-time configuration for the `vue-i18n` options that is used internally by this module. See full documentation at https://vue-i18n.intlify.dev/api/general.html#createi18n

Configuration for `createI18n` can be passed using a configuration file. By default, the module will scan for a `i18n.config.{js,mjs,ts}` if nothing is specified.

```ts {}[nuxt.config.ts]
export default defineNuxtConfig({
modules: [
'@nuxtjs/i18n'
],
i18n: {
vueI18n: { configPath: './nuxt-i18n.js' } // custom path example
Copy link
Collaborator

@kazupon kazupon Mar 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vueI18n option may no longer be optional. It's recommended to autoload i18n.config.ts if we have a config that conforms to the convention. If we have a nonconventional config file name, we can specify it in the vueI18n option!

Copy link
Collaborator

@kazupon kazupon Mar 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, sorry, Never mind this comment.
I was misreading. πŸ˜…

}
})
```

export default function (nuxt: NuxtApp) {
return {
modifiers: {
snakeCase: (str) => str.split(' ').join('-')
```ts {}[nuxt-i18n.js]
export default defineI18nConfig({
legacy: false,
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
},
fr: {
welcome: 'Bienvenue'
}
} as I18nOptions
}
}
})
```

::
Expand All @@ -34,6 +45,6 @@ export default function (nuxt: NuxtApp) {

The `messages` option cannot be included in an object and exported with function due to limitations in Vue I18n v9 handling of locale messages.

As the workaround, you can use [lazy-load transtions](/guide/lazy-load-translations) in Nuxt i18n module. locale messages handled with lazy-load transtions will be loaded as locale messges inside Vue i18n.
As the workaround, you can use [lazy-load transtions](/guide/lazy-load-translations) in Nuxt i18n module. locale messages handled with lazy-load transtions will be loaded as locale messages inside Vue i18n.

::
14 changes: 14 additions & 0 deletions docs/content/4.API/1.composables.md
Expand Up @@ -220,3 +220,17 @@ A function that is the dynamic locale messages loading, that has the following p

- when you switch the locale with `setLocale`.
- when the locale is switched with `<NuxtLink>`. for example, the route path resolved by `useSwitchLocalePath` or `$switchLocalePath`.

## `defineI18nConfig`

The `defineI18nConfig` defines the configuration to be passed to the `vue-i18n` plugin in runtime.

You can use at JavaScript and TypeScript extension formats.

### Parameters

#### `config`

**Type**: `I18nOptions`

The object with configuration definition.
18 changes: 18 additions & 0 deletions playground/i18n.config.ts
@@ -0,0 +1,18 @@
// import { defineI18nConfig, type I18nOptions } from '@nuxtjs/i18n'

// export default <I18nOptions>{
export default defineI18nConfig({
legacy: false,
locale: 'en',
fallbackLocale: 'fr'
// messages: {
// ja: {
// hello: 'こんにけは!'
// }
// }
// fallbackLocale: {
// en: ['ja', 'fr', 'en-US'],
// ja: ['en', 'fr', 'ja-JP'],
// fr: ['en', 'ja', 'fr-FR']
// }
})
19 changes: 2 additions & 17 deletions playground/nuxt.config.ts
Expand Up @@ -77,29 +77,14 @@ export default defineNuxtConfig({
},
// differentDomains: true,
// skipSettingLocaleOnNavigate: true,
detectBrowserLanguage: false,
detectBrowserLanguage: false
// detectBrowserLanguage: {
// useCookie: true,
// // alwaysRedirect: true
// // cookieKey: 'i18n_redirected',
// // // cookieKey: 'my_custom_cookie_name',
// // redirectOn: 'root'
// },
// vueI18n: './vue-i18n.options.ts'
vueI18n: {
legacy: false,
locale: 'en',
fallbackLocale: 'fr'
// messages: {
// ja: {
// hello: 'こんにけは!'
// }
// }
// fallbackLocale: {
// en: ['ja', 'fr', 'en-US'],
// ja: ['en', 'fr', 'ja-JP'],
// fr: ['en', 'ja', 'fr-FR']
// }
}
// vueI18n: { configFile: './i18n.config.ts' }
}
})
46 changes: 6 additions & 40 deletions src/gen.ts
Expand Up @@ -43,7 +43,8 @@ export function generateLoaderOptions(
dev: boolean
ssg: boolean
ssr: boolean
} = { dev: true, ssg: false, ssr: true }
} = { dev: true, ssg: false, ssr: true },
vueI18nPath: string | false = false
) {
const generatedImports = new Map<string, string>()
const importMapper = new Map<string, string>()
Expand Down Expand Up @@ -83,7 +84,7 @@ export function generateLoaderOptions(
return gen
}

let genCode = ''
let genCode = vueI18nPath ? `${genImport(vueI18nPath, 'vueI18nOptions')}\n` : ''
const localeInfo = options.localeInfo || []
const syncLocaleFiles = new Set<LocaleInfo>()
const asyncLocaleFiles = new Set<LocaleInfo>()
Expand Down Expand Up @@ -135,25 +136,9 @@ export function generateLoaderOptions(
genCode += `${Object.entries(options).map(([rootKey, rootValue]) => {
if (rootKey === 'nuxtI18nOptions') {
let genCodes = `export const resolveNuxtI18nOptions = async (context) => {\n`
genCodes += ` const ${rootKey} = Object({})\n`
for (const [key, value] of Object.entries(rootValue)) {
if (key === 'vueI18n') {
const optionLoaderVariable = `${key}OptionsLoader`
genCodes += ` const ${optionLoaderVariable} = ${isObject(value)
? `async (context) => ${generateVueI18nOptions(value, misc.dev)}\n`
: isString(value)
? `async (context) => import(${toCode(value)}).then(r => (r.default || r)(context))\n`
: `async (context) => ${toCode({})}\n`
}`
genCodes += ` ${rootKey}.${key} = await ${optionLoaderVariable}(context)\n`
if (isString(value)) {
const parsedLoaderPath = parsePath(value)
const loaderFilename = `${parsedLoaderPath.name}${parsedLoaderPath.ext}`
genCodes += ` if (${rootKey}.${key}.messages) { console.warn("[${NUXT_I18N_MODULE_ID}]: Cannot include 'messages' option in '${loaderFilename}'. Please use Lazy-load translations."); ${rootKey}.${key}.messages = {}; }\n`
}
} else {
genCodes += ` ${rootKey}.${key} = ${toCode(key === 'locales' ? stripPathFromLocales(value) : value)}\n`
}
genCodes += ` const ${rootKey} = ${JSON.stringify(rootValue)}\n`
if (vueI18nPath) {
genCodes += `${rootKey}.vueI18n = vueI18nOptions\n`
}
genCodes += ` return nuxtI18nOptions\n`
genCodes += `}\n`
Expand Down Expand Up @@ -300,25 +285,6 @@ function resolveLocaleRelativePath(relativeBase: string, langDir: string, file:
return normalize(`${relativeBase}/${langDir}/${file}`)
}

function generateVueI18nOptions(options: Record<string, any>, dev: boolean): string {
let genCode = 'Object({'
for (const [key, value] of Object.entries(options)) {
if (key === 'messages') {
genCode += `${JSON.stringify(key)}: Object({`
for (const [locale, localeMessages] of Object.entries(value)) {
genCode += `${JSON.stringify(locale)}:${
generateJSON(JSON.stringify(localeMessages), { type: 'bare', env: dev ? 'development' : 'production' }).code
},`
}
genCode += '}),'
} else {
genCode += `${JSON.stringify(key)}:${toCode(value)},`
}
}
genCode += '})'
return genCode
}

function generateAdditionalMessages(value: Record<string, any>, dev: boolean): string {
let genCode = 'Object({'
for (const [locale, messages] of Object.entries(value)) {
Expand Down