ロケールの切り替え

言語スイッチャーを実装して、ユーザーが希望するロケールを変更できるようにします。

基本的な実装

useLingoContextフックを使用して、現在のロケールとそれを変更する関数を取得します。

"use client"; // For Next.js

import { useLingoContext } from "@lingo.dev/compiler/react";

export function LanguageSwitcher() {
  const { locale, setLocale } = useLingoContext();

  return (
    <select value={locale} onChange={(e) => setLocale(e.target.value)}>
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="de">Deutsch</option>
      <option value="fr">Français</option>
    </select>
  );
}

setLocale()が呼び出されると:

  1. 新しいロケールが永続化されます(カスタムリゾルバーまたはデフォルトのCookie経由)
  2. ページがリロードされ、新しいロケールが適用されます

カスタムUI

フラグ付きドロップダウン

"use client";

import { useLingoContext } from "@lingo.dev/compiler/react";

const locales = [
  { code: "en", name: "English", flag: "🇺🇸" },
  { code: "es", name: "Español", flag: "🇪🇸" },
  { code: "de", name: "Deutsch", flag: "🇩🇪" },
  { code: "fr", name: "Français", flag: "🇫🇷" },
];

export function LanguageSwitcher() {
  const { locale: currentLocale, setLocale } = useLingoContext();

  return (
    <div className="relative">
      <select
        value={currentLocale}
        onChange={(e) => setLocale(e.target.value)}
        className="px-4 py-2 border rounded-md"
      >
        {locales.map((locale) => (
          <option key={locale.code} value={locale.code}>
            {locale.flag} {locale.name}
          </option>
        ))}
      </select>
    </div>
  );
}

ボタングループ

"use client";

import { useLingoContext } from "@lingo.dev/compiler/react";

export function LanguageSwitcher() {
  const { locale, setLocale } = useLingoContext();

  const locales = ["en", "es", "de", "fr"];

  return (
    <div className="flex gap-2">
      {locales.map((loc) => (
        <button
          key={loc}
          onClick={() => setLocale(loc)}
          className={`px-3 py-1 rounded ${
            locale === loc ? "bg-blue-500 text-white" : "bg-gray-200"
          }`}
        >
          {loc.toUpperCase()}
        </button>
      ))}
    </div>
  );
}

ドロップダウンメニュー

"use client";

import { useState } from "react";
import { useLingoContext } from "@lingo.dev/compiler/react";

export function LanguageSwitcher() {
  const { locale, setLocale } = useLingoContext();
  const [isOpen, setIsOpen] = useState(false);

  const locales = [
    { code: "en", name: "English" },
    { code: "es", name: "Español" },
    { code: "de", name: "Deutsch" },
    { code: "fr", name: "Français" },
  ];

  const currentLocaleName = locales.find((l) => l.code === locale)?.name;

  return (
    <div className="relative">
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="px-4 py-2 border rounded-md flex items-center gap-2"
      >
        <span>{currentLocaleName}</span>
        <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
        </svg>
      </button>

      {isOpen && (
        <div className="absolute top-full mt-2 bg-white border rounded-md shadow-lg">
          {locales.map((loc) => (
            <button
              key={loc.code}
              onClick={() => {
                setLocale(loc.code);
                setIsOpen(false);
              }}
              className={`block w-full text-left px-4 py-2 hover:bg-gray-100 ${
                locale === loc.code ? "bg-blue-50" : ""
              }`}
            >
              {loc.name}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

永続化

デフォルトでは、setLocale()は設定されたロケール永続化方式(デフォルトではCookie)を使用します。

カスタム永続化

カスタムロケールリゾルバーを使用してカスタム永続化を実装します:

// .lingo/locale-resolver.client.ts
export function persistLocale(locale: string): void {
  // Custom logic: localStorage, URL params, API call, etc.
  localStorage.setItem("locale", locale);
  window.location.reload();
}

setLocale()は自動的にpersistLocale()関数を呼び出します。

ページリロードの回避

デフォルトでは、setLocale()は新しいロケールを適用するためにページをリロードします。リロードを回避するには:

  1. クライアント側の状態管理を使用する
  2. すべてのロケールの翻訳を事前読み込みする
  3. ページリロードなしで翻訳を切り替える

注意: これにはカスタム実装が必要です。コンパイラーのデフォルト動作は、シンプルさのためにページをリロードします。

URLベースのロケール切り替え

URLベースのロケールルーティング(/en/about/es/about)の場合:

"use client";

import { useRouter, usePathname } from "next/navigation";

export function LanguageSwitcher() {
  const router = useRouter();
  const pathname = usePathname();

  // Extract current locale from path: /es/about → es
  const currentLocale = pathname.split("/")[1];

  function switchLocale(newLocale: string) {
    // Replace locale in path: /es/about → /de/about
    const newPath = pathname.replace(`/${currentLocale}`, `/${newLocale}`);
    router.push(newPath);
  }

  return (
    <select value={currentLocale} onChange={(e) => switchLocale(e.target.value)}>
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="de">Deutsch</option>
    </select>
  );
}

サブドメインベースの切り替え

サブドメインベースのルーティング(es.example.com)の場合:

"use client";

export function LanguageSwitcher() {
  const currentLocale = window.location.hostname.split(".")[0];

  function switchLocale(newLocale: string) {
    const host = window.location.hostname;
    const domain = host.split(".").slice(1).join(".");
    const newHost = `${newLocale}.${domain}`;

    window.location.href = `${window.location.protocol}//${newHost}${window.location.pathname}`;
  }

  return (
    <select value={currentLocale} onChange={(e) => switchLocale(e.target.value)}>
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="de">Deutsch</option>
    </select>
  );
}

ネイティブ言語名

より良いUXのために、言語名をそのネイティブ言語で表示します:

const locales = [
  { code: "en", name: "English" },
  { code: "es", name: "Español" },
  { code: "de", name: "Deutsch" },
  { code: "fr", name: "Français" },
  { code: "ja", name: "日本語" },
  { code: "zh", name: "中文" },
  { code: "ar", name: "العربية" },
  { code: "ru", name: "Русский" },
];

アクセシビリティ

言語切り替えをアクセシブルにします:

const { locale, setLocale } = useLingoContext();

<div role="group" aria-label="Language selector">
  <label htmlFor="language-select" className="sr-only">
    Choose language
  </label>
  <select
    id="language-select"
    value={locale}
    onChange={(e) => setLocale(e.target.value)}
    aria-label="Select language"
  >
    {locales.map((loc) => (
      <option key={loc.code} value={loc.code}>
        {loc.name}
      </option>
    ))}
  </select>
</div>

一般的なパターン

ナビゲーションバーへの統合

export function Navbar() {
  return (
    <nav className="flex items-center justify-between p-4">
      <Logo />
      <div className="flex items-center gap-4">
        <NavLinks />
        <LanguageSwitcher />
      </div>
    </nav>
  );
}

フッターへの統合

export function Footer() {
  return (
    <footer className="p-4 border-t">
      <div className="flex justify-between items-center">
        <p>© 2024 Your Company</p>
        <LanguageSwitcher />
      </div>
    </footer>
  );
}

モバイルメニュー

export function MobileMenu() {
  return (
    <div className="mobile-menu">
      <NavLinks />
      <div className="border-t pt-4 mt-4">
        <p className="text-sm text-gray-500 mb-2">Language</p>
        <LanguageSwitcher />
      </div>
    </div>
  );
}

テスト

開発環境でロケール切り替えをテストします:

  1. 言語切り替えコンポーネントを追加
  2. 開発サーバーを起動
  3. クリックしてロケールを切り替え
  4. 新しいロケールでページがリロードされることを確認
  5. 翻訳が更新されることを確認

疑似翻訳が有効になっている場合、偽の翻訳がすぐに表示されます。

よくある質問

なぜページがリロードされるのですか? コンパイラは新しいロケールを適用するためにページをリロードします。これにより、すべてのサーバーコンポーネントとメタデータが正しく更新されます。

リロードを回避できますか? はい、ただしカスタム実装が必要です。すべての翻訳を事前にロードし、クライアント側の状態を手動で管理します。

LanguageSwitcherをSuspenseでラップする必要がありますか? いいえ。useLingoContextは同期的であり、Suspenseは不要です。

ユーザーのブラウザ言語を自動的に検出できますか? はい。カスタムロケールリゾルバーで実装してください:

export function getClientLocale(): string {
  return navigator.language.split("-")[0] || "en";
}

サポートされているすべてのロケールを表示すべきですか、それとも一部だけですか? すべてをドロップダウンで表示してください。ロケールが多い場合は、地域ごとにグループ化するか、検索/フィルターUIを使用してください。

次のステップ