404 Not Found

404 Not Found


nginx

国際化 (i18n)

MegaShopは日本とグローバル市場に進出したいと考えていますが, すべてのページが中国語のみです。日本のAliceは円建ての価格を見られず, 日本のユーザーは日本語名で商品を検索できません。Charlieはi18nでMegaShopが中国語, 英語, 日本語の切り替えをサポートし, 価格が通貨に応じて自動フォーマットされるようにする必要があります。

1. 学ぶ内容


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) 言語検出 → 読み込み → レンダリングのワークフロー

100%
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: truelazy: 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は現在の言語に基づいて対応する翻訳を返します。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度:⭐):@nuxtjs/i18nをインストールし, 中国語と英語のサポートを設定し, 基本的なUI翻訳を実装してください。
  2. 応用問題 (難易度:⭐⭐):日本語サポート + 言語スイッチャー + localePathルート翻訳を追加し, hreflangタグを検証してください。
  3. チャレンジ (難易度:⭐⭐⭐):useLocalizedPrice Composableを実装し, USD, CNY, JPYの3通貨の自動変換とフォーマットをサポートし, 商品ページで言語に応じた通貨で価格を表示してください。

---|

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%