2018-08-06本記事は下記のtweetから始まるスレッドに触発され、@qnighyや@na4zagin3からアイディアを拝借して書いた。皆さんのおかげで最強のi18n国家が建設されつつある。長い前置きソフトウェアのi18nは難しい。自文化では当たり前と思っていてハードコードしてしまった仮定が崩れて、大幅な再設計を余儀なくされるからだ。気づいて再設計できればまだ良くて、気づかずに出荷してユーザビリティに深刻な問題を引き起こしたり、セキュリティ上の問題を生んだり、国際問題になったりする。逆に、たとえ難しいi18n上の問題であっても自文化で見聞きしたことがあれば理解は容易だし、備えようという気にもなるし、ある程度勘も働くから変なバグを作り込みにくくなる。日本で暮らしていると文字エンコーディングとタイムゾーンの話が対照的だ。最近でこそUTF-8が主流になったとは言え日本では長らくISO-2022-JPとEUC-JPとShift_JISとこれらの変種がそれぞれ使われており、「世の中にはエンコーディングというものが存在するし取り扱いを間違えると文字化けする」という程度のことは文字列処理を書く人ならみんな知っている。日本に暮らして国内向けの製品を書いているとタイムゾーンとは向き合わなくても済むので、ついついタイムゾーン情報を捨てて時刻を操作するコードを書いたりしがちだ。私も随分そういうコードを書いた。USの開発者は正反対の状況にいた。USには複数のタイムゾーンがあってユーザーの入力や期待される出力がどれになるかは分からないので、その処理には慣れている。1.9への移行でRubyの文字列オブジェクトがエンコーディングを持つようになったとき、結構混乱したユーザーもいたようだった。1ここから考えられるのは、もしあらゆる軸で最強に他国と互換性を欠く文化の国があったとすれば、その国出身の開発者は自然にi18nのあらゆる勘所を体得していて変な設計を無意識に避けていける最強のi18nエンジニアな訳だ。それはどんな国だろう。国内に複数の言語があるインドとか。国内市場向けなら言語を決め打ちできるという仮定が成立しない。複数の公用語を平等にサポートするように法律で規定されている場合もあり、「割り切ってスモールスタート」もできない。国内でも複数の言語をサポートする必要があるので、間違いなくエンドユーザー向けメッセージをソースコード内に埋め込む習慣はないだろう。gettextのような仕組みでメッセージはカタログから引く仕組みを自然に書いている筈だ。同じ開発チーム内でも話す言語が異なるのが普通なので自分の母語でコードにコメントを書く習慣はないだろう。主たる公用語で書くか、英語で書くと思われる。インドとかそんな風だと聞いたことがあるような。USの会社のi18nにありがちな、英語版webページと多国語版webページでURL体系が異なるみたいなものもないだろう。他国で使われている言語の方言が使われているブラジルのポルトガル語(pt-BR)とか。言語と国が1:1に対応しないのをよく知っているので、UI言語の選択肢を国旗で表現する悪習は身につけていないだろう。tagをちゃんと処理する習慣を持っているだろう。相手の性や既婚・未婚により敬称が変化する。相手の属性を知らなければ相手を呼べないので、単純な文字列置き換えによる多言語対応が無理なのを知っている筈だ。textすら生成できないことを認識している。templateを書くことはないだろう。helperで文字列を生成するようになるだろう。template1つ書くのも容易ではない。%>が入室しましたというテンプレートの「入室しました」の部分がuser_listの内容によって変化する訳だ。仕方がないのでhelper関数を呼び出して、最低でもセンテンス単位で文字列を生成することになるだろう。相対敬語を持つ聞き手が誰であるかに依存して、特定の主語に対する敬語の使い方が変化する。このため、コピペで似たメッセージ文を使い回す習慣がなくなるだろう。複数の言語で単語の長さが極端に違うたとえば、中国語や日本語に合わせた画面デザインでは、ドイツ語表記が領域内に収まらなくてボタンに何が書いてあるのか読めなくなることがある。逆にドイツ語に合わせたデザインでは、中国語UIがスカスカで不自然になることがある。単純に翻訳するのではなく画面全体のバランスを考えて翻訳できるように、UI翻訳者に依頼するときに画面レイアウト画像を添付し機能の意図や文字数制限のメモを渡すような良い習慣が定着しているだろう。最悪の場合、言語ごとにアプリのレイアウトを個別に設計することもあり、レイアウトを差し替え可能なように設計しているかもしれない。1バイトずつ読めば1文字ずつ処理できるという古いコードにありがちな仮定が成立しない。文字境界を意識して、文字列検索時に意図せぬ部分マッチを避ける習慣が身についているだろう。ラテンアルファベット表記が困難だと、まずi18nバグを報告しても相手がバグの内容を読めないので理解してもらえないという経験を積める。このためi18nバグをうまく説明するスキルが自然に身についていく。歴史的にISO-2022ベースの文字コードとUnicodeと独自エンコーディングが混在Unicodeは基本として、まず現存するデータが単一の文字エンコーディングになっているという仮定が崩壊する。入力時にUnicodeに変換するか、もしくは文字列データにエンコーディングのメタデータを付ける処理を行うだろう。ISO-2022でまず文字列処理がstatefulになる。「特定の文字を表すバイト列を読めば、それが何の文字か分かる」という仮定が崩壊するので、文字列先頭から丁寧に処理するアルゴリズムを書くようになるだろう。独自エンコーディングがあって、他の規格と異なる固有のエンコーディング処理実装を併存させる必要がある。Shift_JISの5C問題のようにセキュリティホールを作ることもある。文字列処理はセキュリティホールになりえるという基本的認識が身につくだろう。知らないと「面倒くさいからUnicodeで良いのでは?」と思うかもしれない。住基ネットのようにUnicode未収録文字を扱っていると、人は結局は用途に応じた文字集合やエンコーディングの使い分けに向き合わなければならないことを知るだろう。Rubyならできるよ。日本語組版の要件にまとまっているように、フリガナやら縦中横やらがあると「文章の表示とは文字を横に並べることである」「スタイル情報がない限り文字のサイズは一定で良い」という仮定が崩壊する。図3.70にあるように振られた範囲がややこしいこともあるので、単純にカッコ書きで代替はできない。日本語に関して言えば禁則処理やら更にいろいろな話題があるこの文化出身だと、組版というのはすごく専門的で難しいものだという認識を自然に持つだろう。自分で実装しようとせずに、素直に既存ライブラリの肩に乗ることを考えるようになる。日本語とかタイ語とか。分かち書きしないので「空白文字のところを改行候補として字間を調整する」アルゴリズムが機能せず、自然に組版処理が難しい分野だと言うことを認識するはずだ。日本語は行分割可能点のルールが単純で、文字種から決定できるが、タイ語では形態素解析が必要になる。「空白文字区切りで単語を識別できる」という仮定が成立しないので、ごく簡単な文字列処理をするだけでも形態素解析というジャンルを知ることができる。]+がURLだと仮定すると後続の文章までURL扱いされるやつも、正しく処理できる右書き、左書き、縦書きを併用するアラビア語が右から左なのは有名。日本語や中国語は上から下にも書く右書き、左書き、縦書きが全部混在する言語があるという話は聞いたことがない。日本語の縦書きの本でアラビア語に言及すると少しはそういうこともあるけれど、日常的ではない。古代言語では牛耕式や下から上の書き方もあったそうだが、現存しないので現代のi18nとしては一般的な要件ではないだろう。左右書きを併用する文化出身だと、画面デザインの際に左右反転しても見やすいかどうかを自然に気に掛けるようになるだろう。(アラビア語に対応しようとして左右反転デザインにすると画像の配置のバランスが悪くなったり、想定していなくて画面が崩壊するのはありがちなミス)縦書きを併用する文化出身だと、文字レンダリングの際に画面を最初に行分割してから割り付ける方式以外も気に掛けるだろう。リガチャヨーロッパ諸語とかFontには文字単位でグリフを収録すれば良いという仮定が崩壊する一文字ずつ見て順番にレンダリング/印字すれば良いという仮定が崩壊するたぶんUnicodeに合成済みの文字も採録されていて、Unicode正規化を自然に意識するようになるかもしれない。文字がたくさんある日本語や中国語などFontを気軽にデザインしたりアプリに埋め込んだりダウンロードしたりできるという仮定が崩壊するこの文化出身だと、雰囲気を出すために専用デザインのFontを埋め込むという無茶を安易に試そうと思わないだろう文字種が混在するかな漢字アルファベット混じり文など特定の文字種のFontは他の文字種のFontと独立して選定できるという仮定が崩壊するうっかりするとアルファベット用のフォントデザインは他の文字を使う言語に影響しないという仮定を措きがちだが、この文化出身だとそれは避けられるだろう。同一文中でアルファベットのデザインとかな漢字のデザインが一貫しないミスを避けられる。CJK統合漢字は日中韓に共通する漢字をどの言語でも同じコードポイントで表現することを意図しているが、あいにくとこれらの言語では漢字のグリフがかなり異なる場合がある。「文字とフォントが定まればグリフが定まる」という仮定が崩壊するので、自然と文字列や文書に言語タグ情報をメタデータとして付ける習慣を身につけるだろう。同じ文字でもソート順が異なる複数の言語が使われているドイツ語とスウェーデン語など。Intl.Collatorなどを用いた適切なソートを実装するようになる。表記された文字ではなく発音によって文字列をソートする習慣がある日本語。表記された文字だけを見てもどちらを先に並べるべきか分からない。最低でも辞書データを引くか、最悪の場合辞書を引いても文字列の発音が分からない。表記だけではなく発音もメタデータとして保持する習慣がつく。「タイムゾーン呪いの書」も参照のこと。独自の太陰暦グレゴリオ暦とはどんどん年がずれていくので、日付換算が複雑になる。単純にYYYY-mm-ddみたいな文字列に当てはめれば日付表記を生成できるという仮定から自由になれる。20世紀以降に暦法を変更した比較的最近の日付でもX年のY月Z日が(X-1)年Y月Z日の1年後とは限らない。このため、きちんと日付表記を暦法データベースと照合して解析し、正しく処理する習慣が身につくことだろう。週の始まりの曜日は月曜日とする地域や日曜日とする地域が多いが、いっその事イスラム圏の一部のように土曜日始まりだとこの問題を強く意識するようになるだろう。カレンダー表示を実装するときや週次売上集計などを実装する際、設定を切り替える機能をつけ忘れなくなる。国内に複数のタイムゾーンがあるユーザープロファイルに必ずタイムゾーンを記録するようにして、時刻入力を受け付けるときには入力者のプロファイルと見比べたり、別途タイムゾーンを入力させたりするUIを作るようになるだろう。UTCからのオフセットが12時間以上うっかりするとタイムゾーンはUTC-12からUTC+12だと誤解しそうだが、キリバスなどの例を正しく処理するコードが書けるようになるUTCからのオフセットが1時間未満の端数を持つうっかりするとタイムゾーンのオフセットは1時間が最小単位だと思い込みがちだが、ネパールなどのタイムゾーンを正しく処理するコードが書けるようになる。仕様上はオフセットの最小単位はナノ秒である。サマータイムが途中から導入された季節によってタイムゾーンが変化するため、地域ごとにタイムゾーンを固定するようなコードを書かなくなる。タイムゾーン切り替え時に表記上は同じになる時刻が繰り返される(サマータイム終了時に01:59の次は01:00になる)ので、時刻を素朴に整形した値が単調増加であるという間違った仮定に依存しなくなるだろう。バックアップファイルを上書きしたり夜間バッチが走らなくて死ぬのを避けられる。過去にはサマータイムが適用されなかった年が存在するので、時刻表記のparseをハードコードできなくなる。よって、季節だけでなく年も含めて正しくデータベースを引いてタイムゾーンを選択するコードを書けるようになる。サマータイムの時期は毎年発表されるもはやサマータイムデータベースを埋め込んでおくこともできないので、通信するなり保守要員が現地に行くなりしてデータベースを更新する運用が計画されている。この文化出身だとtzdataを更新する運用を組むのは自然なので、他国のタイムゾーンが変更されたり国が分裂したりしても難なく対応できる。日本とかイギリスとか。地域によって「X丁目Y番Z号」方式の表記と「ベイカー通り221B番」や「寺町通御池上る」のような道路方式が混在しているので、住所表記解析器を書くときに異なる実装を使い分けることを想定できる道路方式の感覚で道案内を書くと街区方式の地域ではユーザーが道に迷うので、適切な方法を選択して案内したりサイトに地図をマメに埋め込む習慣ができるだろう。隣国との係争領域がある自国版の地名表記のまま輸出したりwebサイトを提供したりすると炎上するので、機器が利用されている地点やアクセス元IPに応じて地名データベースを使い分ける習慣が身につく純粋なビジュアルデザインでも、国境線を引くときは市場を意識するようになる首都領域が法定行政区画としては存在しない。諸説あるが、仮に「東京」だとしても「東京」が意味するものは謎だ。東京都では広すぎるし東京市は既に存在せず、適当な行政区画がない。シドニー(州都)も同様である。このため、国から首都への対応関係を保持するRDBテーブルにおいて、行政区画テーブルの主キーを参照しようとするとまずいことになる。いう文化出身だと、行政区画と都市を区別してモデリングする習慣が付く。用途によってはIATA都市コードが比較的近いのだが、これが振られていない都市もあるので難しい。非法人地域を持つ比較的歴史の長い国家では領土内の任意の地点が何らかの自治体に属するが、USなどではそうではない。いった文化出身の場合、住所をグルーピングする目的で安易に自治体名を使うようなコードは書かないだろう。広告ターゲティング目的ではこういった体系が住所や行政区画より便利なことがあると聞いたことがあるような気がする。いう文化出身だと、マーケティングや広告向けのシステムで地域指定処理を書くときには住所以外の体系をサポートするのを忘れないようになるだろう。単位を切り替える機能を忘れずに付けられるようになる。USの製品にはよくある機能。桁区切りがピリオド)日本では1,234,567.89のように書くが、フランスのように1.234.567,89と書く地域もある。インドのように桁区切りの単位が途中で変化する地域もある。数値を整形するときに、固定テンプレートを使ったり言語組み込みのデバッグ目的の簡易文字列化メソッドを呼んだりするのではなく、Locale依存のテンプレートを利用するのを忘れないようになる数値部分を文字列から切り出すとき、固定の正規表現をコードに埋め込むミスもなくなっているだろう。name)がたくさんあるもはや姓だけでも名だけでも、ミドルネームを除いた名の部分ですら長過ぎて表示スペースに収まらない可能性がある。NameやNicknameを記録できるような設計を忘れないだろう。姓名の変更が届け出制ユーザーが自分で姓・名を変更できるUIを付けるのを忘れなくなる。東南アジアにそんな例があったと思うんだけど、届け出制なため、中二病に掛かると3回ぐらい格好いい名前に変更するとか聞いたような気がする。姓名の変更が稀だと安易に仮定してUIを付けないでおくと、ユーザーサポートがパンクする性別の変更手続きが存在する日本とか西欧諸国とかユーザーが自分で性別を変更するUIを付けるのを忘れなくなる性別が原則的に変わらないと仮定してUIを付けなかったり使いもしないのに性別情報を収集すると、ユーザーサポートに無駄な負荷が掛かるExcelの関数名がローカライズされている例はフランスだったかな?Excelの関数名が英語版とは異なるので、Excelファイル生成時には固定のひな形ファイルを利用できない。コマンド名がローカライズされているショートカットキーに対応する文字が統一できないメニュー上の順番も異なるためGUIの場合アイコンでしかコマンドを識別できなくなるあとで書くかも外国人が文字集合を整備したので不十分な点があり、規格が混乱している国際規格があるからそれに従って文字符号化できると思った?グリフは同一だが支配者一族を表記するための専用の文字がある文章の文脈を読んで補正しないとOCRや他言語からの翻訳に失敗して、不敬罪的なもので処刑されるのでは。ssもそうドイツ語のßに対応する大文字として慣習的にSSが使われるが逆変換ができない。2017年に言語がバージョンアップして大文字ẞが正式に足された。対応する大文字が存在しない小文字があるドイツ語のエスツェット(大文字も最近定義されつつはあるらしい)文字符号の順序と人間向けのソート順は異なる文字ごとに文字符号を比較して辞書式にソートしても見やすくない。スペイン語のchはcの後でdの前)キー入力イベント列だけでは入力された文字を特定できない(IME)OSごと、インストールされているアプリケーションごと、その時点までに学習したデータごとに、キー入力シーケンスが意味する文字が変わるんだ。未変換のカナが送信されました。文字入力方式とキーボードのレイアウトが独立(IME)キーボードのどのキーをどの符号に割り当てるかと、どんな符号列をどんな文字列として認識するかは全く独立した話題なんだ。ドイツ語キーボード対応するのと同じ感覚で一つのドロップダウンメニューで選択させようとすると死ぬ。IMEを選択する機能はキーボードを選択する機能とは別につける必要がある。10進法でない通貨補助単位が存在する不定期に暦の紀元が切り替わる(元号)言い訳とお願いi18nがすごく専門的で難しい分野なのは知っていて、自分がそれに精通しているという認識はない。本稿は業務上実際に悩まされた問題と、私が本で読んだことがある知識と、どこかで聞いたことがある話題が混在しており、情報精度にはばらつきがある。このため、間違いを見つけたら是非とも編集リクエストをいただけるとありがたい。