Internationalization V2
Overview
Internationalization is powered by @nuxtjs/i18n with an opt-in design:
- Disabled — The layer only lists
@nuxtjs/i18nas a dependency. The site runs single-language, UI strings come from the built-in translations, and routes are not localized. - Enabled — Once you add
@nuxtjs/i18ntomodulesand configurei18n.localesinnuxt.config.ts, localized routing, per-locale content collections, the language switcher, and multilingual SEO activate automatically.
Either way, pages access the current locale, the translation function, and localized paths through a single entry point, useMovkI18n(), with consistent behavior.
Single-language UI
When @nuxtjs/i18n is not enabled, set the UI language via app.config.ts. It decides which language the built-in UI strings (TOC title, edit link, AI assistant, etc.) use.
export default defineAppConfig({
i18n: {
locale: 'en' // UI language, defaults to 'zh-CN'
}
})
Built-in language packs currently provide zh-CN and en. In this mode URLs carry no locale prefix and the content directory stays single-structured.
Multilingual site
1. Install the dependency
Multilingual requires installing @nuxtjs/i18n manually (the layer does not pull it in):
npm install @nuxtjs/i18n
pnpm add @nuxtjs/i18n
yarn add @nuxtjs/i18n
bun add @nuxtjs/i18n
2. Configure nuxt.config.ts
Add @nuxtjs/i18n to modules and declare defaultLocale and locales:
export default defineNuxtConfig({
extends: ['@movk/nuxt-docs'],
modules: ['@nuxtjs/i18n'],
i18n: {
defaultLocale: 'zh-CN',
locales: [
{ code: 'zh-CN', name: '简体中文', file: 'zh-CN.json' },
{ code: 'en', name: 'English', file: 'en.json' }
]
}
})
prefix_except_default: the default locale has no prefix (/docs), while others are prefixed (/en/docs). You do not need to set strategy manually.3. Organize content directories
Default-locale content stays in place at the content/ root; other locales live under content/{locale}/:
content/
├── index.md # Default locale (zh-CN) home
├── docs/ # Default locale docs → /docs/*
│ └── 1.getting-started/
│ └── 1.index.md
└── en/ # English content
├── index.md # English home → /en
└── docs/ # English docs → /en/docs/*
└── 1.getting-started/
└── 1.index.md
The layer generates content collections per locale automatically: the default locale uses docs / landing, while others use docs_{code} / landing_{code} (with - replaced by _ in code, e.g. docs_zh_CN).
Translation files
UI strings (navigation, buttons, AI assistant, etc.) come from translation files. The built-in zh-CN and en live inside the package at i18n/locales/zh-CN.json and i18n/locales/en.json.
Adding a new language requires meeting two conditions, otherwise the locale is filtered out at build time with a warning:
- A matching translation file
{code}.jsonexists; - A matching content directory
content/{code}/exists (except the default locale, whose content lives at thecontent/root).
UI string resolution order is app.config.ts override ?? translation file. So the string fields in app.config.ts default to empty and are provided by the current locale's translation file; to override a single entry, set it explicitly in app.config.ts.
Language switcher
Once multilingual is enabled, the site header shows a language switcher (LanguageSwitcher) that renders the current locale as a flag emoji, lists all valid locales, and navigates to the equivalent path in the chosen language.
Multilingual SEO
When i18n is enabled and multiple valid locales exist, the layer injects:
<link rel="alternate" hreflang="…">for each locale plus anx-defaultfallback;og:localefor the current locale;<html lang/dir>driven by@nuxt/ui/locale.
Browser-language redirection is controlled by @nuxtjs/i18n's detectBrowserLanguage; to pin the home page language, disable it in the i18n config.
Unified entry point
Pages, components, and composables access i18n through useMovkI18n(), with consistent behavior whether or not i18n is enabled:
| Field | Description |
|---|---|
isEnabled | Whether @nuxtjs/i18n is enabled |
locale | Current locale code (reactive) |
defaultLocale | Default locale code |
locales | List of valid locales |
t(key, params?) | Translation function with named params |
localePath(path) | Convert to the current locale's localized path |
switchLocalePath(code) | Get the path to switch to a given locale |
docsRoot | Docs root path (/docs or /{locale}/docs) |
docsCollection | Current locale's docs collection name |
landingCollection | Current locale's landing collection name |
useMovkI18n().t is the mode-agnostic entry that also works when i18n is disabled; its key is a plain string with no type completion. When i18n is enabled, the layer turns on @nuxtjs/i18n typed messages, so consumer code that needs key completion should use the native const { t } = useI18n(), which automatically recognizes both the layer's built-in keys and the consumer's custom keys.Template
For a ready-to-use multilingual site, use the official i18n template:
npx nuxi init -t gh:mhaibaraai/movk-nuxt-docs/templates/i18n my-docs
The template ships with zh-CN (default) and en, the matching content directories, and a preconfigured @nuxtjs/i18n setup.
Project Structure
Take a closer look at the directory structure of a Movk Nuxt Docs project and the responsibilities of each file, including the content/ directory, public/ static assets, nuxt.config.ts module configuration, and app.config.ts runtime UI configuration, with typical configuration examples.
Troubleshooting
Resolve common issues you may run into when using Movk Nuxt Docs, including Google Fonts access, removing the pnpm shamefully-hoist setting, installing Tailwind CSS as a peer dependency, approving build scripts, and Vercel deployment OOM (out-of-memory) errors, with detailed solutions.