@byjohann/nuxt-i18n
Nuxt module for internationalization with locale auto-imports & localized routing.
This module's intention is not to provide a full-blown solution for internationalization like @nuxtjs/i18n, but offer a lean, effective and lightweight set of tools to cover your needs without the bloat of a full-blown solution.
Key Features
Setup
pnpm add -D @byjohann/nuxt-i18n
npm i -D @byjohann/nuxt-i18n
Basic Usage
📖 Check out the playground
Add @byjohann/nuxt-i18n
to your nuxt.confg.ts
:
export default defineNuxtConfig({
modules: ['@byjohann/nuxt-i18n']
})
For the most basic setup, add the locales
and defaultLocales
module options with a set of translation messages
:
export default defineNuxtConfig({
modules: ['@byjohann/nuxt-i18n'],
i18n: {
locales: ['en', 'de'],
defaultLocale: 'en',
messages: {
en: { welcome: 'Welcome' },
de: { welcome: 'Willkommen' }
}
}
})
Use the globally available useI18n
composable in your component's setup
hook:
<script setup>
const { locale, t } = useI18n()
</script>
<template>
<div>Language: {{ locale }}</div>
<p>{{ t('welcome') }}</p>
</template>
Guide
Routing & Strategies
You can opt-in to override the Nuxt default routes with added locale prefixes to every URL by using one of the built-in routing strategies. By default, the generated routes stay untouched (no_prefix
strategy).
For example, if your app supports two languages: German and English as the default language, and you have the following pages in your project:
└── pages/
├── about/
│ └── index.vue
└── index.vue
This would result in the following routes being generated for the prefix_except_default
strategy:
🎄 Routes Tree
[
{
path: '/',
name: 'index___en',
},
{
path: '/de/',
name: 'index___de',
},
{
path: '/about',
name: 'about___en',
},
{
path: '/de/about',
name: 'about___de',
}
]
[!NOTE]
Routes for the English version don't have a prefix because it is the default language.
Available Strategies
There are 4 supported strategies in total that affect how the app's routes are generated.
no_prefix (default)
|
With this strategy, routes stay as they are generated by Nuxt. No locale prefix will be added. The locale can be changed without changing the URL.
|
prefix_except_default
|
Using this strategy, all of your routes will have a locale prefix added except for the default language.
|
prefix
|
With this strategy, all routes will have a locale prefix.
|
prefix_and_default
|
This strategy combines both previous strategies behaviours, meaning that you will get URLs with prefixes for every language, but URLs for the default language will also have a non-prefixed version. This could lead to duplicated content. You will have to handle, which URL is preferred when navigating in your app.
|
Configuration
A strategy may be set using the strategy
module option. Make sure that you have a defaultLocale
defined in any case.
export default defineNuxtConfig({
i18n: {
locales: ['en', 'de'],
defaultLocale: 'en',
strategy: 'prefix_except_default',
},
})
Custom Route Paths
In some cases, you might want to translate URLs in addition to having them prefixed with the locale code. For example, you might want to have a route like /about
in English and /ueber-uns
in German. You can achieve this by defining a custom path for the route in the nuxt.config.ts
file:
export default defineNuxtConfig({
i18n: {
locales: ['en', 'de', 'fr'],
defaultLocale: 'en',
pages: {
about: {
de: '/ueber-uns',
fr: '/a-propos'
}
}
}
})
[!NOTE]
Each key within the pages object should correspond to the relative file-based path (excluding the .vue
file extension) of the route within your pages
directory.
Customized route paths must start with a /
and not include the locale prefix.
Auto-Importing & Lazy-Loading Translations
For apps that contain a lot of translated content, it is preferable not to bundle all the messages in the main bundle, but rather lazy-load only the language that the users selected. By defining a directory where translation files are located, locale messages can be dynamically imported when the app loads or when the user switches to another language.
However, you can also benefit from the advantages of auto-import without enabling dynamic imports.
How to enable file-based translations with or without lazy-loading:
- Set the
langImports
option to true
. - Enable dynamic imports by setting the
lazy
option to true
. - Optionally, configure the
langDir
option to a directory that contains your translation files. Defaults to locales
. - Make sure the
locales
option covers possible languages.
[!NOTE]
Translation files must be called the same as their locale. Currently, JSON, JSON5 and YAML are supported.
Example files structure:
├── locales/
│ ├── en.json
│ ├── es.json5
│ ├── fr.yaml
└── nuxt.config.js
Configuration example:
export default defineNuxtConfig({
i18n: {
locales: ['en', 'es', 'fr'],
defaultLocale: 'en',
langImports: true,
lazy: true
}
})
[!NOTE]
If you prefer to import file-based translations but don't want to dynamically import them, omit the lazy
module option, as it defaults to false
.
[!WARNING]
The global route middleware to lazy-load translations when switching locales won't run when the no_prefix
strategy is chosen. Use the useLazyLocaleSwitch
composable for changing the language, it will load the corresponding translations beforehand.
Manual Translations
Instead of auto-importing (with or without lazy-loading), you can manually import your translations and merge them into the global locale messages object:
import en from './locales/en.json'
import de from './locales/de.json'
export default defineNuxtConfig({
i18n: {
locales: ['en', 'de'],
defaultLocale: 'en',
messages: {
en,
de,
},
},
})
The locale messages defined above will be passed as the messages
option when initializing @byjohann/vue-i18n
with createI18n()
.
API
Module Options
interface ModuleOptions {
locales?: string[]
defaultLocale?: string
langDir?: string
langImports?: boolean
lazy?: boolean
messages?: LocaleMessages
strategy?: Strategies
pages?: CustomRoutePages
routeOverrides?: Record<string, string>
logs?: boolean
}
Composables
useI18n
Gives access to the current i18n instance.
function useI18n(): UseI18n
interface UseI18n {
defaultLocale: string
locale: ComputedRef<string>
locales: readonly string[]
messages: LocaleMessages
t: (key: string, params?: Record<string, any>) => string
setLocale: (locale: string) => void
getLocale: () => string
}
useRouteLocale
Returns the current locale based on the route name. Preferred for strategies other than no_prefix
.
Type Declarations
function useRouteLocale(): string
useLocalizedPath
Returns a translated path for a given route. Preferred when working with all routing strategies except no_prefix
.
Type Declarations
function useLocalizedPath(
path: string,
locale: string,
): string
Example
const to = useLocalizedPath(useRoute().fullPath, 'de')
useRouter().push(to)
useLazyLocaleSwitch
Ensures to load the translation messages for the given locale before switching to it. Mostly needed for the no_prefix
strategy.
Type Declarations
function useLazyLocaleSwitch(locale: string): Promise<void>
Example
await useLazyLocaleSwitch('en')
💻 Development
- Clone this repository
- Enable Corepack using
corepack enable
- Install dependencies using
pnpm install
- Run
pnpm run dev:prepare
- Start development server using
pnpm run dev
Credits
License
MIT License © 2022-PRESENT Johann Schopplich
MIT License © 2022-2023 LeanERA GmbH