多言語サイト構築中に直面したヘッダーとフッターの言語固定問題と、NextIntlClientProviderを通じた解決プロセスを共有します。
こんにちは!本日は、hafuture サービスの開発過程で経験した、興味深くも少し困惑した技術的なトラブルシューティング事例をご紹介します。テーマは 「多言語環境におけるレイアウトコンポーネントの同期」 です。
問題の発見:「本文は韓国語なのに、メニューはなぜ英語のまま?」
hafuture は、世界中のユーザーをターゲットにテキスト処理、PDF 編集、各種計算機などの便利なツールを提供するプラットフォームです。そのため、韓国語、英語、日本語を完璧にサポートすることが何よりも重要でした。
私たちは next-intl ライブラリを使用して多言語対応を実装しましたが、ある日ブログページをテストしていたところ、奇妙な点に気づきました。/ko/blog のパスでアクセスした際、ブログ記事の本文とタイトルは正しく韓国語で表示されるものの、上部の ヘッダー(Header)メニュー と下部の フッター(Footer) が依然として英語のままでした。
それだけでなく、言語切り替えスイッチで言語を変えても、URL が変わるだけで共通のレイアウト要素は変わりませんでした。まるでレイアウトだけが別のタイムゾーンで生きているかのような感覚でした。
原因分析:クライアントサイド・プロバイダーの不足
原因は大きく分けて 2 つありました。
- NextIntlClientProvider のロケール欠落:
layout.tsxですべてのメッセージを読み込んで提供していましたが、NextIntlClientProviderに対して現在どの言語(locale)を使用すべきか明示的に伝えていませんでした。その結果、ブラウザで動作するクライアントコンポーネント(Header、Footer など)はデフォルトの英語で固定されてしまっていました。 - メッセージ読み込みの不確実性: サーバーコンポーネントで
getMessages()を呼び出す際に引数を省略すると、リクエストのコンテキストを正確に把握できず、デフォルト言語のメッセージを返してしまうリスクがありました。
解決プロセス:明示的なロケール注入
この問題を解決するため、以下の手順でコードを修正しました。
1. Root Layout の修正
まず、RootLayout で URL から取得した locale 情報を NextIntlClientProvider に直接渡すように変更しました。これにより、配下のすべてのクライアントコンポーネントが同一の言語コンテキストを共有できるようになります。
// src/app/[locale]/layout.tsx
<NextIntlClientProvider locale={locale} messages={messages}>
<Header />
{children}
<Footer />
</NextIntlClientProvider>
2. ブログページのメタデータと言語ロジックの強化
ブログ一覧ページでも getTranslations({ locale, namespace: "Blog" }) のようにロケールを明示的に指定し、サーバーサイドレンダリング(SSR)の時点で正確な言語データが含まれるように保証しました。
3. 言語切り替えロジックの単純化
既存の複雑なパス解析ロジックを削除し、next-intl の内蔵機能を活用するようにしました。この過程で発生していた不要なエラーも併せて解決されました。
結果と教訓
修正後、ブログページで言語を切り替えると、ヘッダーの「Text Tools」が「텍스트 도구(テキストツール)」に、「Blog」が「블로그(ブログ)」に即座に翻訳されることを確認できました。
今回の作業を通じて学んだ主要な教訓は以下の通りです:
- コンポーネントのタイプ(Server/Client)に応じた i18n 戦略: サーバーコンポーネントはリクエストコンテキストを、クライアントコンポーネントはプロバイダーを通じたコンテキスト共有が不可欠です。
- 「明示的」は「暗黙的」よりも優れている: 自動で処理されることを期待するよりも、ロケール情報を明示的に渡す方が、デバッグやメンテナンスの面で圧倒的に有利です。
グローバルサービスを目指す開発者の皆様にとって、この記事が少しでもお役に立てれば幸いです。多言語対応は単にテキストを変えるだけでなく、ユーザーの環境を尊重するための第一歩ですから。
ありがとうございました。