開発記録
言語は提案しても、ページの切り替えは読者に任せます
英語版・日本語版の案内を例に、実際の翻訳、ブラウザの言語設定、非モーダルのダイアログ、一時的な非表示の記録を整理します。自動転送しないことと、フォーカスを動かさないことの違いも確認するノートです。
- 多言語サイト
- HTML
- 状態設計
- プログレッシブエンハンスメント
日本語に設定したブラウザで繁体字中国語の記事を開いた読者は、検索や友人からの共有、特定の段落へのリンクから来たのかもしれません。ブラウザの優先言語は、別の言語版を案内する手がかりにはなります。ただし、その読者が今、原文から離れたいことまでは示しません。
このサイトでは言語の提案という形を採り、元のページにとどまったまま、実際に存在する英語版や日本語版へのリンクを用意しています。移動するかは読者が選びます。2026 年 10 月 26 日に、案内を表示する条件、閉じた記録、実際のフォーカスを確認しました。「自動的に別ページへ移動しない」ことと「操作をまったく中断しない」ことは、同じではありません。
まず、別の言語版が本当にあるかを確認します
案内のコンポーネントを出力するのは、繁体字中国語のページで、機能が無効化されておらず、英語版か日本語版のリンク先が少なくとも一つある場合だけです。タイトルから記事の URL を推測したり、まだ翻訳されていない記事への入口を作ったりはしません。
固定ページのリンク先は、既存の言語別ルートから取得します。記事ページでは、実際に公開条件を満たす翻訳の集合を渡します。コンポーネントが出力されていても、スクリプトが提案対象の言語に対応するリンクを見つけるまでは、案内を表示しません。
たとえば、英語版のリンク先しかないのに、ブラウザの優先言語から日本語を提案する条件になった場合、代わりに英語を勧める処理はありません。これはコードの条件から確認できる分岐であり、あらゆる翻訳の欠落を扱う言語ネゴシエーションができているわけではありません。
この順序は、翻訳がなければ、翻訳を装う言語ページは作らないという原則に沿っています。案内は実際のコンテンツへの入口であり、案内があること自体はコンテンツの存在を証明しません。
先頭の優先言語だけを読み、一覧全体との照合はしません
navigator.languages は、優先順に並んだ言語タグを提供します。最も優先される言語が先頭です。[4] このサイトはその最初の項目を使い、値がなければ navigator.language を読みます。
続くルールは単純です。タグの先頭で言語を判断し、日本語なら日本語を提案し、中国語なら案内を表示しません。それ以外の言語では、英語を提案しようとします。優先設定の一覧を順にたどって、サイトが提供できる最初の言語を探す処理はありません。
独立したブラウザページでテスト用の優先言語を設定し、次の結果を観察しました。
| テスト用の優先言語 | 繁体字中国語ページでの結果 |
|---|---|
| 先頭が日本語で、後ろに英語がある | 日本語版の案内を表示する |
| 先頭が中国語で、後ろに英語がある | 案内を開かない |
| 先頭が英語 | 英語版の案内を表示する |
| 先頭がドイツ語で、二番目が日本語 | 英語を提案するルールを使う |
これはブラウザ内のテスト設定であり、読者調査や実際の端末での言語分布ではありません。同時に、このルールの限界も示しています。英語は実装が選んだフォールバックであって、読者が英語をより好むとコードによって証明されたわけではありません。
非モーダルでも、フォーカスは動くことがあります
案内はネイティブの dialog の show() を使い、showModal() は使いません。ローカルでの観察では、ダイアログは開いていましたが、:modal には一致しませんでした。元の繁体字中国語版の URL も変わりませんでした。
この案内は、モーダルのようにドキュメントのほかの部分を操作不能にはしません。ただし、HTML の show() の処理でも、dialog のフォーカス手順は実行されます。[2] 背景が操作不能になっていないからといって、フォーカスにまったく影響しないとは言えません。
実際に日本語版の案内を表示すると、提案先の日本語リンクにフォーカスが移りました。これは明示しておくべきトレードオフです。ページを強制的に切り替えなくても、キーボードを使う読者を元の読書位置から移動させる可能性があります。フォーカスを動かさずに案内することが目標なら、モーダルを非モーダルに変えるだけでなく、案内の見せ方から再検討する必要があります。
閉じ方も、実際の操作経路を確認します。「Not now」またはそれに対応する日本語のボタンにフォーカスを当て、Enter で閉じました。コードの関数を直接呼び出して、操作の代わりにはしていません。コンポーネントは cancel を監視していますが、この観察だけで、Escape や外側のクリックによるすべての閉じ方まで検証済みとは言えません。
「今は切り替えない」は、恒久的な言語設定ではありません
案内を閉じると、スクリプトは提案した言語ごとに拒否した状態を記録し、sessionStorage に入れます。恒久的な「現在の言語」を保存するものではありません。
sessionStorage の範囲は、origin とトップレベルのタブのページセッションに結び付いており、再読み込み後も保持されます。端末をまたぐアカウントの設定ではありません。[5] ローカルで日本語版の案内を閉じ、同じタブを再読み込みすると、案内は再び開きませんでした。
ストレージの読み書きも失敗する可能性があるため、これらの処理には例外処理があります。session storage を利用できない状態も別途シミュレートしました。この場合も閉じるボタンで案内を閉じられましたが、再読み込みすると、拒否した状態を保存できていないため、再び案内が現れました。
この結果を「一度閉じたら、その後は二度と表示されない」とは書けません。保証できるのは、ストレージが使えるときは、その言語の案内を閉じたことをそのページセッション中は覚えておき、使えないときも、その場では閉じられるという範囲です。
リンク先の有無だけでなく、どの状態を引き継ぐかも確認します
提案するリンクを作るときには、その時点の query と hash をコピーします。たとえば、繁体字中国語の作品ページのデザインカテゴリから英語版の案内を受け入れると、英語版の作品ページのパスだけになるのではなく、そのパラメーターとカテゴリもリンク先へ引き継ぎます。
キーボードで実際の英語リンクをたどると、英語版の作品ページに到達し、元の query、デザインカテゴリの hash、ページの言語がそれぞれ対応していました。これは一つの具体的な経路での観察であり、ほかのページのすべての fragment にも同じように対応する翻訳先があるとは限りません。
コピーするのは、案内を初期化するときだけです。案内を開いたままページ内の状態が後から変わっても、このコードは変化のたびにリンク先を更新するわけではありません。初期化時のスナップショットと継続的な同期は、分けて設計する必要があります。
案内がなくても、言語を選ぶ入口は残します
英語と日本語のページでは、この案内自体を出力しません。JavaScript がない場合、繁体字中国語ページの案内は非表示のままです。それでも主なナビゲーションにはネイティブの言語リンクが残るため、読者は案内を開けなくても、ほかの言語版を見つけられます。
こうすることで、言語の提案は補助的な役割にとどまります。ブラウザの情報に応じて入口を案内できますが、公開ページやナビゲーションは、この案内に依存しなくても成り立ちます。優先言語の判断、実際の翻訳、フォーカス、拒否の記録には、それぞれ別の役割があります。「多言語対応」の一言でまとめて済ませることはできません。
この確認には、実機のスマートフォン、スクリーンリーダー、ブラウザ横断の検証、すべてのストレージポリシーは含まれていません。また、クリック率や滞在時間から、この案内のほうが効果的だと証明したわけでもありません。開くときにフォーカスを移す設計を残すかどうかは、実際の閲覧状況を踏まえて決める必要がある UI 上の選択です。
参考資料
[2] https://html.spec.whatwg.org/multipage/interactive-elements.html — WHATWG HTML: Interactive elements [4] https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages — MDN: Navigator languages [5] https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage — MDN: Window sessionStorage