開発記録
翻訳がなければ、翻訳を装う言語ページは作らない
固定ページとノートで異なるルート生成ルールを通じて、空の状態を保ち、実際の翻訳だけにリンクし、未翻訳の言語を見せかけのページで埋めない多言語サイトの設計を説明します。
- 多言語コンテンツ
- Astro
- 翻訳
- コンテンツ設計
多言語サイトで見落とされやすいのは、翻訳があるかどうかよりも、翻訳がないときに、あるように見せてしまうかどうかです。
固定ページなら、ビルド時に繁体字中国語、英語、日本語の各バージョンを用意できることが多いです。一方、記事には繁体字中国語の下書きしかない場合も、公開済みの中国語版が一つだけある場合もあります。両方に「言語一覧にある言語のページをすべて生成する」という同じルールを適用すると、URL はあるのに対応するコンテンツがない、実体のないページができてしまいます。
このサイトでは別の方法を採っています。固定ページには各言語の入口をそろえられますが、ノートの記事ページは、実際の翻訳ファイルが存在し、公開条件も満たしている場合にだけ生成します。基本となる判断は、コンテンツが空の言語別の入口は、状態を正直に示す形で存在してよい一方、翻訳があるように見せかける記事ページは生成すべきではないというものです。
固定ページとノートではデータの取得元が異なります
ホーム、概要、作品、協業、プロダクト、ノート一覧は、サイトの固定ページです。これらの対応言語はサイト設定の一部です。英語と日本語の固定ページには、ビルド時に対応する route props と翻訳済みの文言が用意されています。
ノートの記事は Content Collection から取得します。各 Markdown 記事には、それぞれの locale、stableId、slug、公開状態があります。記事のルートは、言語一覧をそのまま全言語に展開して作るのではありません。まず正式な公開条件を満たす記事を取得し、その記事データからパスを生成します。
二つの異なるデータの流れとして整理できます。
固定ページ:対応言語 → 固定ルート → 対応する文言
記事:実際の Markdown → 公開条件による絞り込み → 実際の記事ルート
固定ページの対応言語は「サイトのインターフェースがどの言語をサポートするか」を表します。記事データは「その時点でどの記事が実際に存在するか」を表します。二つの集合の数が異なっていても、矛盾はありません。
stableId で同じ記事の翻訳を探します
同じ記事の各言語版では stableId を共有できます。ただし、slug、タイトル、概要、本文は言語ごとに持ちます。記事ページで言語切り替えリンクを作るときは、公開済み記事の集合から、同じ stableId を持つ別の言語版を探します。
この検索には二つの制約があり、それが利点にもなっています。
- 対応する
stableIdを持つファイルがなければ、翻訳として扱いません。 - ファイルがあっても、下書きのまま、または公開日時を迎えていなければ、公開記事の翻訳リンクには表示しません。
そのため、公開済みの繁体字中国語の記事に、繁体字中国語の URL しかなくても問題ありません。英語版が完成し、校正を終えて公開条件を満たした段階で、英語の URL が同じ言語別の代替リンク群に加わります。
言語メニューは存在しない記事へのリンクを作るべきではありません
固定ページの言語メニューでは、三つの言語を表示できます。いずれにも対応する固定ページのルートがあるためです。一方、記事ページの languageLinks は、実際に存在する翻訳の集合から作ります。ある言語の記事がなければ、その言語版は利用できない状態として表示します。ホームに戻ったり、原文を表示したり、404 になったりする、実体のないリンクは作りません。
読者にとっての違いは明確です。
- 翻訳があれば、実際に移動できるリンクを用意します。
- 翻訳がなければ、その言語版は現在利用できないと伝えます。
- 原文を別の言語の URL にコピーして、翻訳済みのコンテンツに見せかけません。
- 読者を黙って別の言語に移動させ、言語切り替えが成功したと思わせません。
言語メニューの役割は、データの状態を表示することです。コンテンツ担当者に代わって、不足する記事を補うことではありません。
空の状態を示すほうが、翻訳を装う記事より誠実です
繁体字中国語の原稿が書かれた 2026 年 10 月 2 日の時点では、英語と日本語のノート一覧に公開済みの記事はありませんでした。それでも、/en/notes と /ja/notes という、それぞれの入口はありました。一覧ページでは空の状態を表示し、その言語の入口は存在するものの、コンテンツはまだ準備できていないことを伝えていました。これは原稿が記録した当時の状態であり、現在の公開状態ではありません。その後、この記事を含む英語版と日本語版の翻訳が追加されています。
画面を充実して見せるために繁体字中国語の記事を英語や日本語の一覧にコピーすると、いくつかの問題が生じます。
- URL は翻訳記事のように見えるのに、本文は対象の言語ではありません。
- 検索エンジンに、URL は異なるのに内容が同じページを複数渡す可能性があります。
- 言語を切り替えた読者は、翻訳が完成していると受け取ってしまいます。
- 後で本当の翻訳を追加したとき、既存の見せかけのページの識別やリンクの整理が難しくなります。
空の状態では表示するコンテンツは少なくなりますが、状況は明確になります。コンテンツサイトでは、完成して見える記事カードで不足を隠すよりも、そのほうが保守しやすいです。
sitemap にも同じ境界を適用します
翻訳を装うページは、言語メニューに表示しないだけでは不十分です。正式な出力にある、ほかの入口からも除く必要があります。sitemap の記事パスは公開済み記事の集合から生成するため、翻訳がない言語の記事 URL は追加しません。
同じルールは、次の箇所にも適用します。
- ホームの最新記事欄。
- 各言語のノート一覧。
- ページネーションのルート。
- 記事ページの canonical。
hreflangの代替リンク。- 関連記事と参考記事。
どれか一つの入口が存在しない翻訳を再び追加してしまうと、サイト内でコンテンツの状態が食い違います。翻訳の有無は、コンテンツデータを取り込む段階で判断すべきです。各ページに推測させるべきではありません。
言語別のページがあることと、その言語のコンテンツがあることは別です
このサイトの設計では、次の二つが同時に成立します。
/en/aboutと/ja/aboutは、読める固定ページです。/en/notes/some-articleと/ja/notes/some-articleは、存在しない場合があります。
前者は、サイトのインターフェースを英語と日本語で提供していることを意味します。後者は、ある記事の翻訳がまだ用意されていないことを意味します。これは不整合ではなく、二種類のデータの責任を分けた結果です。
将来、すべての記事を翻訳する必要があれば、同じ stableId を持つ各言語のファイルを記事ごとに追加できます。そのうえで、それぞれの校正と公開の状態に応じて公開対象の集合に加えます。将来の翻訳のために、空の記事ページを先に作っておく必要はありません。
翻訳を追加する前に確認すること
- 既存の記事の翻訳であり、似たタイトルを持つ別の新しい記事ではないことを確認します。
- 同じ
stableIdを維持し、対象言語で自然なslug、タイトル、概要、本文を用意します。 - 対象言語のファイルの
localeが、配置先のディレクトリと一致していることを確認します。 - 内容の校正と公開範囲の確認が終わるまでは、
draft: trueを維持します。 - 記事ページ、言語メニュー、canonical、
hreflang、sitemap が、新しい状態をそろって反映しているか検証します。 - 翻訳がなければ空の状態を保ち、placeholder 記事を先に作りません。
ルートの数で、多言語コンテンツがそろっているように見せるべきではありません。固定ページが三つの言語に対応していても、すべてのノートに三つの言語版があるとは限りません。記事が一つの言語にしか対応していなくても、サイトの多言語設計が失敗しているわけではありません。各 URL が実際に提供するコンテンツを正直に示せば、読者は未完成のものを見ているのかどうかを推測せずに済みます。
この記録の範囲
この記事は、2026 年 10 月 2 日の繁体字中国語の原稿が記録した、固定ページ、記事の Content Collection、言語切り替え、空の状態、canonical、hreflang、sitemap の間のコンテンツの境界を整理したものです。翻訳の手順が自動化されたとは主張していません。また、当時の英語と日本語の一覧が空だったことを、翻訳の進捗や読者の需要を測定した結果として扱ってもいません。空の状態は当時の記録であり、翻訳が追加された現在も一覧が空だという意味ではありません。