Internationalization (i18n)
MegaShop wants to enter the Japanese and global markets, but all its pages are in Chinese only. Alice can't see prices in yen in Japan, and Japanese users can't find products by their Japanese names. Charlie needs i18n to enable MegaShop to support switching between Chinese, English, and Japanese, with prices automatically formatted according to the currency.
1. What You'll Learn
- @nuxtjs/i18n module configuration: language list, default language, SEO URL strategy
- Translation File Management: Lazy-loaded translations + JSON format
- Runtime API: useI18n() / t() / localePath() / switchLocalePath()
- SEO and i18n: hreflang Tags + Alternative Language URLs + Multilingual Sitemaps
- MegaShop: Chinese/English/Japanese + USD/JPY/CNY multi-currency support
2. A True Story of an Architect
(1) Pain Point: Single-Language Limitations on Globalization
MegaShop's website is only in Chinese, so Japanese users can't understand it, and U.S. users can't find products by their English names. All prices are displayed in RMB, so when Alice in the U.S. sees "¥2,999," she doesn't know if it's in yen or RMB.
(2) The @nuxtjs/i18n Solution
The Nuxt i18n module automatically enables multilingual support for every page, and its URL strategy is SEO-friendly:
/en/products/123 → English page, USD price
/ja/products/123 → Japanese page, JPY price
/zh/products/123 → Chinese page, CNY price
(3) Benefits: Global Market Coverage
Three months later, traffic from Japan increased by 200% and traffic from the U.S. increased by 150%; the hreflang tags enabled Google to correctly index each language version.
3. i18n Language Processing Workflow
(1) Language Detection → Loading → Rendering Workflow
flowchart TB
A[User Request] --> B{Detect Language}
B -->|Cookie| C[Use saved locale]
B -->|Browser Header| D[Match Accept-Language]
B -->|Default| E[Use defaultLocale: en]
C --> F[Load translation file]
D --> F
E --> F
F --> G[Set URL prefix]
G --> H[Render page with locale]
H --> I[Generate hreflang tags]
```
---
## 4. @nuxtjs/i18n Configuration
### (1) Installation and Basic Configuration
```bash
npm install @nuxtjs/i18n
(1) ▶ Example: i18n Module Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
locales: [
{ code: 'en', name: 'English', file: 'en.json', currency: 'USD' },
{ code: 'zh', name: 'Chinese', file: 'zh.json', currency: 'CNY' },
{ code: 'ja', name: 'Japanese', file: 'ja.json', currency: 'JPY' }
],
defaultLocale: 'en',
lazy: true,
langDir: 'locales/',
strategy: 'prefix_except_default', // / for en, /zh/..., /ja/...
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n-locale',
redirectOn: 'root'
}
}
})
Output:
// Execution Successful
(2) Comparison of URL Policies
| Strategy | English URL | Chinese URL | SEO Effectiveness |
|---|---|---|---|
| prefix_except_default | /products | /zh/products | ✅ Recommended |
| prefix | /en/products | /zh/products | ✅ Match |
| prefix_and_default | /en/products + /products | /zh/products | ⚠️ Duplicate |
| no_prefix | /products | /products | ❌ Cannot distinguish |
4. Translation File Management
(1) ▶ Example: Translation File Structure
locales/
├── en.json # English
├── zh.json # Chinese
└── ja.json # Japanese
Output:
Execution Successful
(2) ▶ Example: English Translation Document
{
"common": {
"home": "Home",
"products": "Products",
"cart": "Cart",
"search": "Search",
"signIn": "Sign In",
"signOut": "Sign Out"
},
"product": {
"addToCart": "Add to Cart",
"outOfStock": "Out of Stock",
"reviews": "{count} reviews",
"price": "Price: {amount} {currency}",
"freeShipping": "Free shipping on orders over {amount} USD"
},
"cart": {
"empty": "Your cart is empty",
"total": "Total: {amount}",
"checkout": "Proceed to Checkout",
"items": "{count} items"
}
}
Output:
{
"common": {
"home": "Home",
"products": "Products",
"cart": "Cart",
"search": "Search",
"signIn": "Sign In",
"signOut": "Sign Out"
},
"product": {
"addToCart": "Add to Cart",
"outOfStock": "Out of Stock",
"reviews": "{count} reviews",
"price": "Price: {amount} {currency}",
"freeShipping": "Free shipping on orders over {amount} USD"
},
"cart": {
"empty": "Your cart is empty",
"total": "Total: {amount}",
"checkout": "Proceed to Ch
(3) ▶ Example: Chinese Translation File
{
"common": {
"home": "Home Page",
"products": "Products",
"cart": "Shopping Cart",
"search": "Search",
"signIn": "Sign In",
"signOut": "Sign Out"
},
"product": {
"addToCart": "Add to Cart",
"outOfStock": "Out of stock",
"reviews": "{count} reviews",
"price": "Price: {amount} {currency}",
"freeShipping": "Free shipping on orders of {amount} yuan or more"
},
"cart": {
"empty": "Your cart is empty",
"total": "Total: {amount}",
"checkout": "Proceed to Checkout",
"items": "{count} items"
}
}
Output:
{
"common": {
"home": "Home Page",
"products": "Products",
"cart": "Shopping Cart",
"search": "Search",
"signIn": "Sign In",
"signOut": "Sign Out"
},
"product": {
"addToCart": "Add to Cart",
"outOfStock": "Out of stock",
"reviews": "{count} reviews",
"price": "Price: {amount} {currency}",
"freeShipping": "Free shipping on orders of {amount} yuan or more"
},
"cart": {
"empty": "Your cart is empty",
"total": "Total: {amount}",
"checkout": "Proceed to Checkout",
"items": "{count} items"
}
}
5. Runtime API
(1) ▶ Example: Using useI18n in a component
<template>
<div>
<h1>{{ t('common.products') }}</h1>
<button>{{ t('product.addToCart') }}</button>
<p>{{ t('product.reviews', { count: 1200 }) }}</p>
<p>{{ t('product.price', { amount: formattedPrice, currency: currentCurrency }) }}</p>
</div>
</template>
<script setup lang="ts">
const { t, locale, locales } = useI18n()
// Get current currency based on locale
const currentCurrency = computed(() => {
const current = locales.value.find(l => l.code === locale.value)
return current?.currency || 'USD'
})
const formattedPrice = computed(() => {
return new Intl.NumberFormat(locale.value, {
style: 'currency',
currency: currentCurrency.value
}).format(299.99)
})
</script>
Output:
// Execution Successful
(2) ▶ Example: Language Switch Navigation
<!-- components/LanguageSwitcher.vue -->
<template>
<div class="language-switcher">
<NuxtLink
v-for="loc in availableLocales"
:key="loc.code"
:to="switchLocalePath(loc.code)"
:class="{ active: loc.code === locale }"
>
{{ loc.name }}
</NuxtLink>
</div>
</template>
<script setup lang="ts">
const { locale, locales, t } = useI18n()
const switchLocalePath = useSwitchLocalePath()
const availableLocales = computed(() =>
(locales.value as any[]).filter(l => l.code !== locale.value)
)
</script>
Output:
// Execution Successful
(3) ▶ Example: localePath Route Translation
<template>
<nav>
<NuxtLink :to="localePath('/')">{{ t('common.home') }}</NuxtLink>
<NuxtLink :to="localePath('/products')">{{ t('common.products') }}</NuxtLink>
<NuxtLink :to="localePath('/cart')">{{ t('common.cart') }}</NuxtLink>
</nav>
</template>
<script setup lang="ts">
const localePath = useLocalePath()
</script>
Output:
// Execution Successful
6. SEO and i18n
(1) Automatic Generation of hreflang Tags
@nuxtjs/i18n automatically generates an hreflang tag for each page:
<!-- Auto-generated by i18n module -->
<link rel="alternate" hreflang="en" href="https://megashop.com/products/123" />
<link rel="alternate" hreflang="zh" href="https://megashop.com/zh/products/123" />
<link rel="alternate" hreflang="ja" href="https://megashop.com/ja/products/123" />
<link rel="alternate" hreflang="x-default" href="https://megashop.com/products/123" />
(1) ▶ Example: Multilingual Sitemap
// nuxt.config.ts - i18n + sitemap integration
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n', '@nuxtjs/sitemap'],
i18n: {
locales: [
{ code: 'en', name: 'English', file: 'en.json' },
{ code: 'zh', name: 'Chinese', file: 'zh.json' },
{ code: 'ja', name: 'Japanese', file: 'ja.json' }
],
defaultLocale: 'en'
},
sitemap: {
hostname: 'https://megashop.com',
i18n: {
locales: ['en', 'zh', 'ja'],
defaultLocale: 'en'
}
}
})
Output:
// Execution Successful
(2) i18n SEO Checklist
| Check Item | Requirement | Verification Method |
|---|---|---|
| hreflang tag | Cross-linking between language versions | View source code |
| canonical URL | Link to the current language version | View source code |
| x-default | Links to the default language | View source code |
| URLs containing language prefixes | /zh/ /ja/ | Browser address bar |
| Translation Completion Rate | > 95% | Automated Check |
7. Comprehensive Example: MegaShop—Multilingual and Multi-Currency
// composables/useLocalizedPrice.ts
export function useLocalizedPrice() {
const { locale, locales } = useI18n()
const currentCurrency = computed(() => {
const current = (locales.value as any[]).find(l => l.code === locale.value)
return current?.currency || 'USD'
})
function formatPrice(amount: number): string {
return new Intl.NumberFormat(locale.value, {
style: 'currency',
currency: currentCurrency.value
}).format(amount)
}
// Convert USD base price to local currency (simplified)
const rates: Record<string, number> = { USD: 1, CNY: 7.25, JPY: 149.5 }
function convertFromUSD(usdAmount: number): number {
const rate = rates[currentCurrency.value] || 1
return usdAmount * rate
}
function formatFromUSD(usdAmount: number): string {
return formatPrice(convertFromUSD(usdAmount))
}
return { currentCurrency, formatPrice, convertFromUSD, formatFromUSD }
}
<!-- components/ProductCard.vue -->
<template>
<div class="product-card">
<img :src="product.image" :alt="product.name" />
<h3>{{ product.name }}</h3>
<p>{{ formatFromUSD(product.price) }}</p>
<button>{{ t('product.addToCart') }}</button>
<p>{{ t('product.reviews', { count: product.reviewCount }) }}</p>
</div>
</template>
<script setup lang="ts">
const { t } = useI18n()
const { formatFromUSD } = useLocalizedPrice()
defineProps<{ product: any }>()
</script>
❓ FAQ
lazy: true and lazy: false?lazy: true loads translation files on demand (only when switching languages), reducing the size of the above-the-fold content. lazy: false loads all languages at once; switching is instantaneous, but the above-the-fold content loads more slowly. We recommend lazy: true.Intl.NumberFormat for formatting, and implement currency conversion in a Composable. MegaShop's useLocalizedPrice encapsulates both.ProductTranslation table); translation files only handle UI text. The API returns the corresponding translation based on the current language.📖 Summary
- The @nuxtjs/i18n module supports lazy loading, SEO URL strategies, and browser language detection
- Translation files use JSON format and support interpolation variables {count}/{amount}
- useI18n().t() translates text, localePath() generates localized routes, and switchLocalePath() switches languages
- Automatic generation of hreflang tags, multilingual sitemap support, and correct indexing of each language version by Google
- Multi-currency support using Intl.NumberFormat + Composable; product data is stored in the database in multiple languages
📝 Exercises
- Basic Exercise (Difficulty: ⭐): Install @nuxtjs/i18n, configure support for Chinese and English, and implement basic UI translation
- Advanced Exercise (Difficulty: ⭐⭐): Add Japanese language support + language switcher + localePath route translation, and verify the hreflang tag
- Challenge (Difficulty: ⭐⭐⭐): Implement the
useLocalizedPriceComposable to support automatic conversion and formatting for three currencies—USD, CNY, and JPY—and display prices in the corresponding currency on the product page based on the language.
---|



