Internacionalização (i18n)
O MegaShop quer entrar no mercado japonês e global, mas todas as suas páginas estão apenas em chinês. Alice não consegue ver preços em ienes no Japão, e usuários japoneses não conseguem encontrar produtos por seus nomes em japonês. Charlie precisa de i18n para permitir que o MegaShop suporte alternância entre chinês, inglês e japonês, com preços automaticamente formatados de acordo com a moeda.
1. O Que Você Vai Aprender
- Configuração do módulo @nuxtjs/i18n: lista de idiomas, idioma padrão, estratégia de URL SEO
- Gerenciamento de Arquivos de Tradução: Traduções lazy loading + formato JSON
- API em Tempo de Execução: useI18n() / t() / localePath() / switchLocalePath()
- SEO e i18n: Tags hreflang + URLs de Idiomas Alternativos + Sitemaps Multilíngues
- MegaShop: Suporte Chinês/Inglês/Japonês + Multi-moeda USD/JPY/CNY
2. Uma História Real de um Arquiteto
(1) Ponto Crítico: Limitações de Idioma Único na Globalização
O site do MegaShop está apenas em chinês, então usuários japoneses não conseguem entender, e usuários americanos não conseguem encontrar produtos por seus nomes em inglês. Todos os preços são exibidos em RMB, então quando Alice nos EUA vê "¥2,999," ela não sabe se é em ienes ou RMB.
(2) Solução com @nuxtjs/i18n
O módulo i18n do Nuxt habilita automaticamente o suporte multilíngue para cada página, e sua estratégia de URL é amigável para SEO:
/en/products/123 → Página em inglês, preço em USD
/ja/products/123 → Página em japonês, preço em JPY
/zh/products/123 → Página em chinês, preço em CNY
(3) Benefícios: Cobertura de Mercado Global
Três meses depois, o tráfego do Japão aumentou 200% e o tráfego dos EUA aumentou 150%; as tags hreflang permitiram que o Google indexasse corretamente cada versão de idioma.
3. Fluxo de Processamento de Idiomas do i18n
(1) Fluxo de Detecção → Carregamento → Renderização de Idiomas
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. Configuração do @nuxtjs/i18n
### (1) Instalação e Configuração Básica
```bash
npm install @nuxtjs/i18n
(1) ▶Exemplo: Configuração do Módulo i18n
// 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', // / para en, /zh/..., /ja/...
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n-locale',
redirectOn: 'root'
}
}
})
Saída:
// Execução Bem-sucedida
(2) Comparação de Políticas de URL
| Estratégia | URL em Inglês | URL em Chinês | Eficácia SEO |
|---|---|---|---|
| prefix_except_default | /products | /zh/products | ✅ Recomendado |
| prefix | /en/products | /zh/products | ✅ Consistente |
| prefix_and_default | /en/products + /products | /zh/products | ⚠️ Duplicado |
| no_prefix | /products | /products | ❌ Não distingue |
4. Gerenciamento de Arquivos de Tradução
(1) ▶Exemplo: Estrutura de Arquivos de Tradução
locales/
├── en.json # Inglês
├── zh.json # Chinês
└── ja.json # Japonês
Saída:
Execução Bem-sucedida
(2) ▶Exemplo: Documento de Tradução em Inglês
{
"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"
}
}
Saída:
{
"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) ▶Exemplo: Arquivo de Tradução em Chinês
{
"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"
}
}
Saída:
{
"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. API em Tempo de Execução
(1) ▶Exemplo: Usando useI18n em um componente
<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()
// Obter moeda atual com base no 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>
Saída:
// Execução Bem-sucedida
(2) ▶Exemplo: Navegação de Troca de Idioma
<!-- 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>
Saída:
// Execução Bem-sucedida
(3) ▶Exemplo: Tradução de Rotas localePath
<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>
Saída:
// Execução Bem-sucedida
6. SEO e i18n
(1) Geração Automática de Tags hreflang
O @nuxtjs/i18n gera automaticamente uma tag hreflang para cada página:
<!-- Gerado automaticamente pelo módulo i18n -->
<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) ▶Exemplo: Sitemap Multilíngue
// nuxt.config.ts - Integração i18n + sitemap
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'
}
}
})
Saída:
// Execução Bem-sucedida
(2) Checklist de SEO i18n
| Item de Verificação | Requisito | Método de Verificação |
|---|---|---|
| Tag hreflang | Links cruzados entre versões de idiomas | Ver código fonte |
| URL canônica | Link para a versão do idioma atual | Ver código fonte |
| x-default | Links para o idioma padrão | Ver código fonte |
| URLs contendo prefixos de idioma | /zh/ /ja/ | Barra de endereços do navegador |
| Taxa de Conclusão de Tradução | > 95% | Verificação Automatizada |
7. Exemplo Completo: MegaShop — Multilíngue e Multi-moeda
// 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)
}
// Converter preço base em USD para moeda local (simplificado)
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>
❓Perguntas Frequentes
lazy: true e lazy: false?lazy: true carrega arquivos de tradução sob demanda (somente ao trocar de idioma), reduzindo o tamanho do conteúdo acima da dobra. lazy: false carrega todos os idiomas de uma vez; a troca é instantânea, mas o conteúdo acima da dobra carrega mais lentamente. Recomendamos lazy: true.Intl.NumberFormat para formatação, e implemente conversão de moeda em um Composable. O useLocalizedPrice do MegaShop encapsula ambos.ProductTranslation); arquivos de tradução gerenciam apenas texto de UI. A API retorna a tradução correspondente com base no idioma atual.📖Resumo
- O módulo @nuxtjs/i18n suporta carregamento lazy, estratégias de URL SEO e detecção de idioma do navegador
- Arquivos de tradução usam formato JSON e suportam variáveis de interpolação {count}/{amount}
- useI18n().t() traduz texto, localePath() gera rotas localizadas, e switchLocalePath() troca idiomas
- Geração automática de tags hreflang, suporte a sitemap multilíngue, e indexação correta de cada versão de idioma pelo Google
- Suporte multi-moeda usando Intl.NumberFormat + Composable; dados de produtos são armazenados no banco de dados em múltiplos idiomas
📝Exercícios
- Exercício Básico (Dificuldade: ⭐): Instale @nuxtjs/i18n, configure suporte para chinês e inglês, e implemente tradução básica de UI
- Exercício Avançado (Dificuldade: ⭐⭐): Adicione suporte ao idioma japonês + seletor de idioma + tradução de rotas localePath, e verifique a tag hreflang
- Desafio (Dificuldade: ⭐⭐⭐): Implemente o Composable
useLocalizedPricepara suportar conversão e formatação automática para três moedas — USD, CNY e JPY — e exiba preços na moeda correspondente na página do produto com base no idioma.
---|



