Next.js 15とnext-intlで韓国語・英語・日本語ページを運用する
GRAXELポータルの多言語ルーティング、メタデータ、検索エンジン向け整備についてまとめます。
Next.js 15とnext-intlで韓国語・英語・日本語ページを運用する
現在、私たちのポータルサイトのトラフィックの約35%は、韓国語以外の言語からのアクセスです。1つのコードベースで、韓国語(ko)、英語(en)、そして日本語(ja)の3つの言語を完璧にサポートし、かつSEOのペナルティを受けずに運用することは、1人開発者にとってパズルのような難題でした。Next.js 15のApp Routerと、国際化ライブラリであるnext-intlを用いた私たちの多言語化の戦いと、その中で踏み抜いた地雷についてお話しします。
ルーティングとas-needed戦略
言語ごとに別々のドメインを用意するリソースはないため、URLのパスで言語を区別する方式(例: /en/about)を採用しました。しかし、メインのターゲット層は韓国のユーザーであるため、韓国語の場合はURLに/ko/を含めたくありませんでした。next-intlの機能であるlocalePrefix: "as-needed"オプションを設定することで、デフォルト言語のプレフィックスを非表示にするというエレガントなURL設計を実現しました。
ミドルウェアの順序が引き起こした「無限リダイレクト地獄」
多言語化の実装において、私が最も時間を溶かした失敗は、Next.jsのmiddleware.tsにおける処理順序のミスでした。
当初、私は認証チェック(ログインしているかどうかのガード)の処理を、next-intlの言語判定処理よりも「先」に実行するようにコードを書いていました。これが大きな間違いでした。ユーザーが/ja/dashboard(日本語版のダッシュボード)にアクセスした際、システムは言語(ja)を認識する前に認証ガードを発動させ、強制的にデフォルトのログインページである/login(韓国語)へとリダイレクトさせてしまったのです。ユーザーからすれば、突然言語が切り替わり、しかも永遠に元の言語のページに戻れないという、完全な「リダイレクト地獄」でした。ミドルウェアでは必ず「言語のルーティング解決」を最優先で行い、その確定したURLに対して認証ガードをかけるべきだという、Next.jsのアーキテクチャの根幹に関わる痛い教訓でした。
バンドルサイズの最適化とSEO対策
もう一つの課題はパフォーマンスでした。初期実装では、管理画面専用の翻訳文字列など、クライアント側で不要な全ての言語辞書ファイル(JSON)をブラウザに送信しており、無駄な通信が発生していました。これをサーバーコンポーネント(Server Components)側で厳密にフィルタリングし、必要な名前空間(Namespace)の辞書のみをクライアントに渡すようリファクタリングした結果、初期ロード時のバンドルサイズを15〜20%削減することに成功しました。
また、Next.jsの公式ドキュメントに則り、すべてのページでhreflangメタタグを動的に生成しています。これにより、Googleのクローラーに対して「このページには別の言語バージョンが存在する」ことを正確に伝え、各国の検索結果で適切な言語のページが表示されるようSEOを最適化しています。
多言語対応は、単にテキストを翻訳するだけではなく、ユーザーのコンテキストを途切れさせないための緻密なシステム設計が求められます。多言語展開を含む私たちのプラットフォームの成長の軌跡については、著者ページやブログの運営記を、何かお気づきの点があればお問い合わせを通じてフィードバックをいただければ幸いです。
共有
関連記事
同じテーマやタグに基づき、GRAXELの運用文脈を続けて確認できます。
韓国語政策データのハイブリッド検索 — pgvectorと全文検索を組み合わせる
MyHyetaekで意味検索とキーワード検索を組み合わせる理由を説明します。
OllamaでローカルLLMを運用に組み込む — 使いどころと限界
GRAXELがローカルLLMをどのようにコスト削減と自動化に使うかを整理します。
Rust Axum + Cloudflare Tunnelでpolicy-api.graxel.aiを運用する
MyHyetaekの背後にある政策検索APIを安全に公開するための構成を解説します。