国際化 (i18n)
MegaShopは日本とグローバル市場に進出したいと考えていますが, すべてのページが中国語のみです。日本のAliceは円建ての価格を見られず, 日本のユーザーは日本語名で商品を検索できません。Charlieはi18nでMegaShopが中国語, 英語, 日本語の切り替えをサポートし, 価格が通貨に応じて自動フォーマットされるようにする必要があります。
1. 学ぶ内容
- @nuxtjs/i18nモジュール設定:言語リスト, デフォルト言語, SEO URL戦略
- 翻訳ファイル管理:遅延読み込み翻訳 + JSON形式
- ランタイムAPI:useI18n() / t() / localePath() / switchLocalePath()
- SEOとi18n:hreflangタグ + 代替言語URL + 多言語サイトマップ
- MegaShop:中国語/英語/日本語 + USD/JPY/CNY多通貨サポート
2. アーキテクトのある本当の話
(1) 課題:単一言語によるグローバル化の限界
MegaShopのサイトは中国語のみで, 日本のユーザーは理解できず, アメリカのユーザーは英語名で商品を見つけられません。価格はすべて人民元表示で, アメリカのAliceが「¥2,999」を見ても, それが円か人民元か分かりません。
(2) @nuxtjs/i18nの解決策
Nuxt i18nモジュールはすべてのページで自動的に多言語サポートを有効にし, URL戦略はSEOフレンドリーです:
TEXT
/en/products/123 → 英語ページ, USD価格
/ja/products/123 → 日本語ページ, JPY価格
/zh/products/123 → 中国語ページ, CNY価格
(3) メリット:グローバル市場カバレッジ
3か月後, 日本からのトラフィックは200%増, アメリカからのトラフィックは150%増加しました。hreflangタグによりGoogleが各言語バージョンを正しくインデックスしました。
3. i18n言語処理ワークフロー
(1) 言語検出 → 読み込み → レンダリングのワークフロー
flowchart TB
A[ユーザーリクエスト] --> B{言語検出}
B -->|Cookie| C[保存されたlocaleを使用]
B -->|ブラウザヘッダー| D[Accept-Languageと照合]
B -->|デフォルト| E[defaultLocale: enを使用]
C --> F[翻訳ファイル読み込み]
D --> F
E --> F
F --> G[URLプレフィックス設定]
G --> H[localeでページレンダリング]
H --> I[hreflangタグ生成]
4. @nuxtjs/i18n設定
(1) インストールと基本設定
BASH
npm install @nuxtjs/i18n
(1) ▶ サンプル:i18nモジュール設定
TYPESCRIPT
// 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', // / がen, /zh/..., /ja/...
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n-locale',
redirectOn: 'root'
}
}
})
出力:
TEXT
// 実行成功
(2) URL戦略の比較
| 戦略 | 英語URL | 中国語URL | SEO効果 |
|---|---|---|---|
| prefix_except_default | /products | /zh/products | ✅ 推奨 |
| prefix | /en/products | /zh/products | ✅ 統一 |
| prefix_and_default | /en/products + /products | /zh/products | ⚠️ 重複 |
| no_prefix | /products | /products | ❌ 区別不可 |
5. 翻訳ファイル管理
(1) ▶ サンプル:翻訳ファイル構造
TEXT
locales/
├── en.json # 英語
├── zh.json # 中国語
└── ja.json # 日本語
出力:
TEXT
実行成功
(2) ▶ サンプル:英語翻訳ファイル
JSON
{
"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"
}
}
(3) ▶ サンプル:日本語翻訳ファイル
JSON
{
"common": {
"home": "ホーム",
"products": "商品一覧",
"cart": "カート",
"search": "検索",
"signIn": "サインイン",
"signOut": "サインアウト"
},
"product": {
"addToCart": "カートに追加",
"outOfStock": "在庫切れ",
"reviews": "{count}件のレビュー",
"price": "価格:{amount} {currency}",
"freeShipping": "{amount}通貨以上のご注文で送料無料"
},
"cart": {
"empty": "カートは空です",
"total": "合計:{amount}",
"checkout": "注文に進む",
"items": "{count}点"
}
}
6. ランタイムAPI
(1) ▶ サンプル:コンポーネントでuseI18nを使用
VUE
<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()
// 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>
出力:
TEXT
// 実行成功
(2) ▶ サンプル:言語スイッチナビゲーション
VUE
<!-- 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>
出力:
TEXT
// 実行成功
(3) ▶ サンプル:localePathルート翻訳
VUE
<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>
出力:
TEXT
// 実行成功
7. SEOとi18n
(1) hreflangタグの自動生成
@nuxtjs/i18nは各ページにhreflangタグを自動生成します:
HTML
<!-- 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) ▶ サンプル:多言語サイトマップ
TYPESCRIPT
// nuxt.config.ts - 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'
}
}
})
出力:
TEXT
// 実行成功
(2) i18n SEOチェックリスト
| チェック項目 | 要件 | 検証方法 |
|---|---|---|
| hreflangタグ | 言語バージョン間の相互リンク | ソースコードを表示 |
| 正規URL | 現在の言語バージョンにリンク | ソースコードを表示 |
| x-default | デフォルト言語にリンク | ソースコードを表示 |
| URLに言語プレフィックス含む | /zh/ /ja/ | ブラウザのアドレスバー |
| 翻訳完了率 | > 95% | 自動チェック |
8. 総合例:MegaShop — 多言語・多通貨
TYPESCRIPT
// 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)
}
// USD基準価格をローカル通貨に変換 (簡易版)
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 }
}
VUE
<!-- 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>
❓ よくある質問
Q
lazy: trueとlazy: falseの違いは何ですか?A
lazy: trueは翻訳ファイルをオンデマンドで読み込み (言語切り替え時のみ), 初回表示コンテンツのサイズを削減します。lazy: falseは全言語を一括読み込みし, 切り替えは瞬時ですが初回表示が遅くなります。lazy: trueをお勧めします。Q 翻訳ファイルはJSONとYAMLのどちらを使うべきですか?
A JSONがデフォルト形式で最も互換性が高いです。YAMLは読みやすいですがパーサーのインストールが必要です。チーム協業にはJSONをお勧めします。ネスト構造をサポートしています。
Q i18n戦略の"prefix_except_default"ではデフォルト言語にURLプレフィックスがつきませんか?
A はい。"/"が英語, "/zh/"が中国語です。これは推奨されるSEO戦略で, デフォルト言語のURLが短くなります。統一性が必要な場合は"prefix"戦略を使用してください。
Q 言語切り替えはランタイムでどのように永続化されますか?
A i18nモジュールに組み込みのCookie永続化があります (detectBrowserLanguage.useCookie: true)。setLocaleCookie()で手動設定も可能です。
Q 複数通貨の扱いはどうすればよいですか?i18nは翻訳のみを扱いますか?
A i18nは言語翻訳を扱い, 通貨は別途処理する必要があります。
Intl.NumberFormatでフォーマットし, Composableで通貨変換を実装します。MegaShopのuseLocalizedPriceが両方をカプセル化しています。Q 100万商品ページの翻訳はどう管理しますか?
A 商品名や説明などの動的データはデータベースの複数言語で保存され (
ProductTranslationテーブル), 翻訳ファイルはUIテキストのみを扱います。APIは現在の言語に基づいて対応する翻訳を返します。📖 まとめ
- @nuxtjs/i18nモジュールは遅延読み込み, SEO URL戦略, ブラウザ言語検出をサポート
- 翻訳ファイルはJSON形式で, 補間変数{count}/{amount}をサポート
- useI18n().t()でテキスト翻訳, localePath()でローカライズルート生成, switchLocalePath()で言語切り替え
- hreflangタグの自動生成, 多言語サイトマップサポート, Googleが各言語バージョンを正しくインデックス
- 多通貨サポートはIntl.NumberFormat + Composableで実現, 商品データはデータベースで多言語保存
📝 練習問題
- 基本問題 (難易度:⭐):@nuxtjs/i18nをインストールし, 中国語と英語のサポートを設定し, 基本的なUI翻訳を実装してください。
- 応用問題 (難易度:⭐⭐):日本語サポート + 言語スイッチャー + localePathルート翻訳を追加し, hreflangタグを検証してください。
- チャレンジ (難易度:⭐⭐⭐):
useLocalizedPriceComposableを実装し, USD, CNY, JPYの3通貨の自動変換とフォーマットをサポートし, 商品ページで言語に応じた通貨で価格を表示してください。
---|



