国際化

W3C の Localization vs. Internationalization と Authoring HTML の技術索引を取得して読み、 理解した内容を自分の言葉で書いたもの。本文の引用はしていない。

アクセシビリティと同じ場所に置いているのは、 どちらも「自分と違う条件で使う人」を前提に置くかどうかの話だから。 公正のためのエンジニアリングの同じ原則から出ている。

国際化と地域化は別の作業

定義いつ
国際化(i18n)地域化を容易にするような設計と実装設計・開発
地域化(l10n)特定の市場(ロケール)の言語・文化・要件に合わせる適応展開時、市場ごとに繰り返す

分担はこうなる。

国際化   Unicode に対応する。文字方向のマークアップを入れる
         言語を識別できる構造にする。縦書きや非ラテン組版に CSS で対応する
         翻訳対象を実装から分離する
         日時・暦・数体系・照合順序を差し替えられる形にしておく
         名前の順序や住所の形式が複数ありうる前提で作る

地域化   翻訳する。数値・日付・通貨の書式を合わせる
         キーボード配列、照合順序を設定する
         記号やアイコンが文化的に適切か見る。法的要件に合わせる
         文化的な前提が違うなら、論理や画面を作り直す

国際化は1回、地域化は市場の数だけ。 国際化を飛ばすと、地域化のたびに実装に手が入る。

後からやると高くつく

言語と文化を1つ前提にして作ったものを、後から世界向けに作り直すのは明らかに難しく時間がかかる。

W3C はこれを Y2K に例えている。 「年は2桁」という前提がコード全体に散らばった結果、 その前提を剥がすことそのものが巨大な再設計になった。

国際化の前提も同じ性質を持つ。 「文字は1バイト」「テキストは左から右」「姓は1つ」「日付は月/日/年」は、 どれも実装のあちこちに散り、後から一箇所で直せない。

だから後付けの改修ではなく、設計と開発の基本の一段階として扱う。

文字コード

すべてのコンテンツで UTF-8 を使う。

UTF-16 / UTF-32 / ISO-2022 / UTF-7 / CESU-8 などは使わない
UTF-8 が不可能な場合だけ、Encoding 仕様に載っている旧来の符号化を使う

宣言のしかた。

HTTP ヘッダで宣言する(使えるなら、これが主)
ヘッダがあっても、文書内に <meta charset="utf-8"> を必ず書く
宣言は先頭 1024 バイト以内に収める
<a> や <link> の charset 属性は使わない

1024バイト以内が要点で、<head> の中でも後ろに置くと効かないことがある。

HTML と CSS の両方が UTF-8 で HTML 側に宣言があるなら、CSS 側の宣言は要らない。

言語の宣言

<html> タグの lang 属性で、ページの既定言語を必ず宣言する。

<html lang="ja">

body には付けない。<meta http-equiv="Content-Language"> は使わない。

宣言がないと動かないものが具体的にある。

フォントの選択
スペルチェック
ハイフネーション
引用符の形
行分割の規則

ここが見落とされやすいところで、lang は「メタデータ」ではなく「テキスト処理の指示」。 CSS の hyphens:lang() も、宣言がなければ効きようがない。

文中で言語が変わる部分は、その範囲を要素で囲んで lang を付ける。 タグの値はできるだけ短く(BCP 47 の必要な副タグだけ)、 宣言はできるだけ上位の区分に置く。

zh-Hans / zh-Hant   中国語は簡体・繁体を書き分ける
zxx                 言語ではないと確定しているテキスト
lang=""             言語が不明(HTML5)

リンク先の言語は hreflang で示す。言語を国旗のアイコンで示さない。 言語と国は1対1ではない。

文字の方向

dir はマークアップで指定する。CSS の direction プロパティで制御しない。

理由は、CSS では基底方向を正しく設定できないから。 スタイルが当たらない状態でも文字順は保たれる必要がある。

<html dir="rtl">        文書全体の基底方向。body には付けない
<input dir="auto">      入力された内容から方向を判定して、正しい側に寄せる
<textarea dir="auto">   段落単位で判定する
<bdi>                   方向が不明な文字列を囲む。周囲を巻き込ませない
dirname                 入力された方向をサーバーへ渡す

<bdi> が効くのは、利用者が入れた文字列を並べるとき。 ユーザー名やコメントのように方向が事前に分からないものを囲むためにある。 囲まないと、1つの右横書きの名前が周囲の並び順を崩す。

方向の違う語句はきっちり囲む。境界の要素の末尾に空白を残さない。

レイアウトは伸びる

翻訳すると文字数が変わる。 収まる前提で組んだ箱は壊れる。

背景のグラフィックが、中の文字と一緒に伸びるようにする
きつく制約した領域を避ける

CSS は物理プロパティではなく論理プロパティを使う。

margin-left        → margin-inline-start
text-align: left   → text-align: start
text-align: right  → text-align: end

右横書きにも縦書きにもそのまま移せるのが理由。 言語によらず位置を固定したいときだけ、物理的な指定を使う。

縦書き(中国語・日本語・韓国語・モンゴル語)は writing-mode、 縦中横は text-combine-uprightフォーム・リスト・表を縦書きで検証する。

行末を揃えない(ragged)のが既定だと決めつけない。 言語によっては両端揃えが既定。

名前と住所を決めつけない

ここは実装の前提が最も外れやすいところ。

姓と名を別の欄に分ける必要が本当にあるか、先に問う
欄を十分に長くする。データベース側でも長さを絞らない
「名字」「下の名前」ではなく中立的なラベルにする
1文字の入力をイニシャルだと決めつけない
姓の入力を必須にしない
家族が同じ姓を持つと仮定しない
ハイフンやアポストロフィを許可する
大文字だけを強制しない。空白を許可する
「旧姓」ではなく「以前の名前」にする

ラテン文字だけに制限するなら、制限していることを利用者に明示する。 現地表記とラテン表記の両方を別の欄で持つ選択肢もある。

言語の選択欄はページの上部に置く。選択肢は対象言語の表記で出す。 翻訳すると並び順が意図と変わることがあるので、並べ方も決めておく。

文字列を組み立てない

訳文を切り貼りして文を作らない。

「{n} 件の {種類} が {場所} にあります」

この形は、語順・数の一致・助詞が言語ごとに違うため、そもそも翻訳できないことがある。

文のような並びに、あらかじめ決めた部分文字列や番号を差し込む形を避ける
topic-comment 型(見出しと内容を分ける)で書けるなら、そちらにする
翻訳者に、部分文字列が何で、どう組み合わさるかの文脈を渡す
文字列の使い回しは、文脈まで同一か、それ自体で完結しているときだけ
迷ったら使い回さない

使い回した文字列は、表示される最も小さい箱に収まる必要があるという制約も付いてくる。

翻訳させたくない部分(製品名など)には translate 属性を使う。

正規化

クラス名や CSS セレクタの Unicode 正規化形を揃える。推奨は NFC。

見た目が同じで符号列が違う文字列は一致しない。 このリポジトリでも、macOS のファイル名が NFD で分解されていて素朴な文字列一致が効かない、 という形で同じ問題が出ている(READMEの検査スクリプトは NFC に正規化してから比較している)。

アクセシビリティとの関係

README の Understandable に **「言語が指定されている」**が入っている。 つまり lang の宣言は、国際化の要件であると同時に WCAG の達成基準でもある。

スクリーンリーダーは lang を見て読み上げの言語を切り替える。 宣言がないと、日本語のページを英語の音声規則で読み上げることになる。

国際化をやると、アクセシビリティの一部が同時に満たされる。 逆は成り立たない(アクセシブルでも単一言語前提のままでありうる)。

関連