Skip to content

Commit

Permalink
feat: configurable directory restructure (#3054)
Browse files Browse the repository at this point in the history
* feat: i18n dir resolve

* feat: i18n dir resolve

* test: add restructure test

* refactor: file resolution

* fix: layer option validation

* docs: describe breaking folder structure change

* fix: layer validation

* chore: update lockfile

* test: change fixture structure

* feat: `restructureDir` option

* docs: describe restructure and compatible option
  • Loading branch information
BobbieGoede committed Aug 14, 2024
1 parent 9c90089 commit c906a8d
Show file tree
Hide file tree
Showing 34 changed files with 3,252 additions and 588 deletions.
11 changes: 11 additions & 0 deletions docs/content/docs/3.options/10.misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,14 @@ Don't enable this option in production. It's not optimized for it.
- default: `false`

Set the plugin as `parallel`. See [nuxt plugin loading strategy](https://nuxt.com/docs/guide/directory-structure/plugins#loading-strategy).

## `restructureDir`

- type: `string | undefined`
- default: `undefined`

Can be used to configure the directory used to resolve i18n files, this will be set to `i18n` by default in the v9 release.

::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
Setting this value will also change the default value for `langDir`, which is `locales` if unset, this is the new default value in v9.
::
117 changes: 117 additions & 0 deletions docs/content/docs/5.v9/2.guide/14.layers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
title: Layers
description: Using layers to extends projects with Nuxt i18n.
---

Nuxt i18n module supports layers and will automatically combine i18n configuration of all extended layers. [Read more about layers here](https://nuxt.com/docs/getting-started/layers)

## Merging strategy

As described in the [Nuxt layer authoring guide](https://nuxt.com/docs/guide/going-further/layers#multi-layer-support-for-nuxt-modules)

> - Earlier items in the `_layers` array have higher priority and override later ones
> - The user's project is the first item in the `_layers` array
Mixing locale configuration such as lazy loading objects and strings may not work as expected, Nuxt i18n will attempt to merge layers as best it can. Consistency of i18n configuration between layers will be most effective.

## Pages & Routing

Pages in the `pages` directory from extended layers will automatically be merged and have i18n support as if they were part of your project.

Page routes defined in `i18n.pages` in each layer configuration will be merged as well.

## Locales

A project extending a layer set up with the Nuxt i18n module needs no additional set up as shown in this example:

::code-group

```ts [nuxt.config.ts]
export default defineNuxtConfig({
extends: ['my-layer']
})
```

```ts [my-layer/nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
lazy: true,
locales: [
{ code: 'en', file: 'en.json' },
{ code: 'nl', file: 'nl.json' }
]
}
})
```

::

The project is able to use i18n functionality and the configured locales would be loaded provided by the extended layer.

### Merging locales

Locales provided by a project will be merged with those provided by extended layers, this can be done as follows:

::code-group

```ts {} [nuxt.config.ts]
export default defineNuxtConfig({
extends: ['my-layer'],
i18n: {
locales: [{ code: 'en', file: 'en.json' }]
}
})
```

```ts [my-layer/nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
lazy: true,
locales: [
{ code: 'en', file: 'en.json' },
{ code: 'nl', file: 'nl.json' }
]
}
})
```

::

::callout{icon="i-heroicons-light-bulb"}
Note how some options such as `lazy` are inherited, while options such as `locales` need to be set for every layer (project included) providing locale files.
::

This example would result in the project supporting two locales (`en`, `nl`) and would add the additional messages added for the `en` locale.

::code-group

```ts [project/i18n/locales/en.json]
{
"title": "foo"
}
```

```ts [project/my-layer/i18n/locales/en.json]
{
"title": "layer title",
"description": "bar"
}
```

::

The above will result in the following

```jsonc
{
// earlier layers take priority
"title": "foo",
"description": "bar"
}
```

## VueI18n options

Options defined in VueI18n configuration files within layers are merged and override each other according to their layers priority.
85 changes: 85 additions & 0 deletions docs/content/docs/5.v9/2.guide/15.server-side-translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: Server-Side Translations
description: Translate on the server-side and return it as a response.
---

You can do the translation on the server-side and return it as a response. The locale messages defined in nuxt i18n module options are integrated, so all you need to do is configure the locale detector.

::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
**This feature is experimental,** that is supported from v8 RC8.
::

## Define locale detector

For server-side translation, you need to define a locale detector.

Nuxt i18n exports the `defineI18nLocaleDetector` composable function to define it.

The following is an example of how to define a detector that detects locale using query, cookie, and header:

```ts [i18n/localeDetector.ts]
// Detect based on query, cookie, header
export default defineI18nLocaleDetector((event, config) => {
// try to get locale from query
const query = tryQueryLocale(event, { lang: '' }) // disable locale default value with `lang` option
if (query) {
return query.toString()
}

// try to get locale from cookie
const cookie = tryCookieLocale(event, { lang: '', name: 'i18n_locale' }) // disable locale default value with `lang` option
if (cookie) {
return cookie.toString()
}

// try to get locale from header (`accept-header`)
const header = tryHeaderLocale(event, { lang: '' }) // disable locale default value with `lang` option
if (header) {
return header.toString()
}

// If the locale cannot be resolved up to this point, it is resolved with the value `defaultLocale` of the locale config passed to the function
return config.defaultLocale
})
```

The locale detector function is used to detect the locale on the server-side. It's called per request on the server.

When you define the locale detector, you need to pass the path to the locale detector to the `experimental.localeDetector` option.

The following is an example of a locale detector configuration defined directly in the Nuxt application:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
experimental: {
localeDetector: 'localeDetector.ts'
}
}
})
```

For details on the locale detector function defined by `defineI18nLocaleDetector`, see [here](/docs/api#definei18nlocaledetector).

## `useTranslation` on eventHandler

To translate on the server-side , you need to call `useTranslation`.

Example:

```ts
// you need to define `async` event handler
export default defineEventHandler(async event => {
// call `useTranslation`, so it return the translation function
const t = await useTranslation(event)
return {
// call translation function with key of locale messages,
// and translation function has some overload
hello: t('hello')
}
})
```

::callout{icon="i-heroicons-light-bulb"}
For the key of the translation function, you can specify the locale messages set in the nuxt-i18n options inside the nuxt.config, or the locale loaded in the i18n.config messages.
::
49 changes: 49 additions & 0 deletions docs/content/docs/5.v9/2.guide/18.breaking-changes-in-v9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: Breaking Chainges in v9
description: Follow this guide to upgrade from one major version to the other.
---

::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
`nuxtjs/i18n` v9 is still an alpha version
::

## Upgrade to Vue I18n v10

Vue I18n has been upgraded from v9 to v10. Vue I18n v10 has no major feature additions, but there are some disruptive changes, such as dropping some features that were deprecated in v9 and integrating the API `$tc` into `$t`, which can be used in the Legacy API style

Check the documentation [here](https://vue-i18n.intlify.dev/guide/migration/breaking10.html#change-t-and-t-overloaded-signature-for-legacy-api-mode) for more information.


## Drop `jit` option

JIT compilation is now the default in Vue I18n v10.

https://vue-i18n.intlify.dev/guide/migration/breaking10.html#default-enable-for-jit-compilation

Accordingly, the `jit` option in Nuxt I18n v8 is no longer needed, so this option has been removed.

## Directory restructure and `langDir` default value

We now use a default directory structure that is consistent with [directory structure changes in Nuxt 4](https://nuxt.com/docs/getting-started/upgrade#new-directory-structure).

What changed
* `langDir` now defaults to `locales`.
* All i18n files are resolved relative to `<rootDir>/i18n`, this can be configured with the `restructureDir` option.

Here is an example of a project structure after this change:

```sh
app/
server/
i18n/
locales/
en.json
ja.json
i18n.config.ts
localeDetector.ts
nuxt.config.ts
```

Reasons for change
1. Context - i18n files are used both server-side and client-side, using a dedicated `i18n/` folder in the root directory outside `app/` and `server/` makes more sense.
2. Clean - less clutter/fragmentation of i18n files, and should make resolving and loading files easier for us.
Loading

0 comments on commit c906a8d

Please sign in to comment.