【ITニュース解説】How a Typography Pipeline Actually Rewrites Text: Order, Spans, and Fixed Points
2026年09月23日に「Dev.to」が公開したITニュース「How a Typography Pipeline Actually Rewrites Text: Order, Spans, and Fixed Points」について初心者にもわかりやすく解説しています。
ITニュース概要
テキスト整形処理で複数のルールを適用すると、単独では正しいルールも、組み合わせや適用順序で不整合なバグを起こす。これを解決するため、polytypoは厳格なルール順序と変更範囲を定め、全体として「何度実行しても同じ結果になる(冪等性)」を保証。テキストを正しく正規化するエンジンの開発プロセスを解説する。
ITニュース解説
現代のWebサイトやアプリケーションでは、ユーザーにとって読みやすく、かつ言語や地域(ロケール)の慣習に沿ったテキスト表示が求められる。例えば、ハイフンを適切な長さのダッシュに変換したり、直線的な引用符を言語固有のカーリーな引用符にしたり、句読点の前後にある不要なスペースを削除したりする処理がこれに該当する。しかし、これらの自動的なテキスト修正処理は、一見単純に見えて非常に複雑で、複数のルールが組み合わさると予期せぬ問題を引き起こす可能性がある。
この問題に対処するため、polytypoというタイポグラフィ正規化エンジンが開発された。このエンジンは、テキストを書き換える9つのルールを特定の順序で実行することで、これらのタイポグラフィの問題を解決しようとする。例えば、「スペースの正規化」「三点リーダーの変換」「ダッシュの変換」「引用符の変換」といったルールがあり、それぞれがテキストの特定の部分に作用する。重要なのは、これらのルールが実行される「順序」が厳密に定められていることである。あるルールが別のルールが変換したばかりのテキストにさらに作用し、その結果が望ましくないものになる可能性があるため、どのルールが先に実行されるべきか、どのルールが後から実行されるべきかが厳密に定義されている。この順序は、後続のルールが先行するルールが生成した変更をクリーンアップすることは許容されるが、先行するルールが後続のルールが生成した変更を元に戻すことは許容されない、という原則に基づいている。例えば、引用符の変換ルールがアポストロフィの変換ルールよりも先に実行されるのは、引用符ルールが未変換のアポストロフィを見て、それが引用符の一部であるかどうかを判断するためである。
テキストの修正において、パーサー(テキストを解析するプログラム)の役割も厳密に定義されている。polytypoでは、パーサーは「処理可能なテキストの範囲」を特定するだけで、最終的な出力を生成する役割は持たない。出力は、元の入力テキストに対して、特定された部分文字列を置き換えることで生成される。この設計は、HTMLタグやMarkdownの書式など、タイポグラフィ修正の対象外となる部分が、意図せず変更されてしまうのを防ぐためである。もしパーサーが解析した情報からドキュメント全体を再構築してしまうと、タグの書式などが勝手に変更されてしまう危険がある。このような変更は、タイポグラフィの修正以上に深刻な問題を引き起こす可能性がある。
複数のテキスト断片が混在するドキュメント(例えば、HTMLタグで区切られた文章)を処理する際にも、独特の課題が生じる。各断片を個別に処理すると、断片間の文脈が失われ、不適切なタイポグラフィ変換が行われる可能性がある。また、すべての断片を結合して一度に処理すると、実際には隣接していない箇所が隣接していると誤認識され、誤った変換が生じることもある。polytypoでは、これらの問題を解決するために、テキスト断片の間に「境界マーカー」と呼ばれる特殊な印を挿入して処理する。これにより、ルールは断片間の文脈を考慮しつつ、不要な隣接性の誤認識を避けることができる。さらに、修正されたテキストがスパン(処理対象の範囲)の境界に新しい文字を挿入すると、元のマークアップ構造が壊れる可能性があるため、境界での文字挿入は厳しく制限される。
このようなテキスト変換パイプラインの最も重要な特性の一つが「べき等性(Idempotency)」である。これは、「同じ変換を複数回適用しても、結果は常に同じになる」という性質を指す。つまり、transform(transform(x)) == transform(x)が成り立つことである。個々のルールがそれぞれべき等であっても、それらを組み合わせたパイプライン全体が自動的にべき等になるとは限らない。例えば、ドイツ語の「ダッシュの後にスペースを挿入するルール」と「句読点の前のスペースを削除するルール」がそれぞれ正しくても、この二つのルールが組み合わさって2回実行されると、不完全な形になるバグが発生した。
この問題に対処するため、polytypoは「合成義務」という原則を定めている。これは、「先行するルールがすでに適用され、その結果が正しい状態にある場合、後続のルールがその状態を元に戻すような変更を加えてはならない」というものである。単に「変更がなくなるまでパイプラインを繰り返し実行する」という方法は、バグを隠蔽してしまうため禁止されている。バグは、個々のルールのテストでは見つけられず、パイプライン全体を対象とした厳密なテストによって発見された。解決策としては、問題を引き起こすルールが、後続のルールがその結果を削除する可能性があることを「事前に」認識し、そのような状況では変更を行わないようにする、というアプローチが取られた。
ロケール(地域や言語の設定)の正確性も極めて重要である。polytypoは、オペレーティングシステムやプログラミング言語に組み込まれたロケール解決ライブラリに依存せず、独自の明確なエイリアステーブルを使用してロケールを決定する。これは、異なるプラットフォーム間でロケール解決の挙動が異なることで、同じ入力に対しても異なる出力が生成される可能性を排除するためである。未認識のロケールはエラーとなり、システムが勝手に「最も近い」ロケールを推測したり、デフォルトのロケールで処理したりすることはない。すべてのロケール設定はコードではなくデータとして管理され、それぞれの設定には根拠となる情報源の引用が必須である。これにより、どのタイポグラフィ規則がなぜ適用されるのかが明確になり、仕様の限界についても正直に記述される。
polytypoは、JavaScript、Python、Go、Ruby、PHPという5つの異なるプログラミング言語で実装されている。これらすべての実装は、バージョン管理された共通の「適合性スイート(テストセット)」に対してテストされ、同じ結果を出すことが求められる。ある実装が「適合」しているとは、そのスイートのテストをすべてパスすること、と厳密に定義されている。例えば、PHPの実装では、特定のMarkdownモードが利用できない場合があったが、これは標準ライブラリの制約によるものであり、その限界が正直に記録され、隠蔽されることはなかった。
このpolytypoエンジンは、実際にWebサイトのビルドプロセスに組み込まれ、コミット前の段階でコンテンツのタイポグラフィを正規化するために使用されている。これにより、ライブサイトで表示されるテキストが常に正しいタイポグラフィになっていることを保証する。導入に際しては、単なるMarkdownテキストだけでなく、JSXコンポーネントの属性内に埋め込まれたテキストなど、通常のテキスト処理では見逃されがちな部分の処理も必要であることが判明した。これらの課題は、専用のパスやツールを用いて、変更箇所を正確に特定し、元の文字列に直接置き換えを適用することで解決された。
最終的に、ライブサイトのレンダリング結果が実際に正規化されているかを検証するために、専用のクローラーが開発された。このクローラーは、サイトマップを巡回し、実際にブラウザに表示されるテキストノードを抽出し、それらがpolytypoによって変換しても変化しないことを確認する。これはソースコードを直接調べるだけでは見つけられないような、レンダリング時に発生するタイポグラフィの問題を検出するのに役立つ。ただし、このチェッカーもノード間の隣接性など、すべての複雑な問題を検証するわけではなく、その検証範囲は明確に限定されている。
このように、polytypoの開発と導入の経験は、テキストの自動書き換えが単なる「スマート引用符」スクリプトの適用にとどまらない、より深いエンジニアリング上の課題を提示している。それは、9つのテキスト書き換えルールを任意の入力に対して、固定された順序で、かつドキュメントツリーを再構築することなく実行し、さらにそのパイプライン全体がべき等であることを証明するという、厳密な要求に応えるためのアプローチである。このプロジェクトは、単一のルールではなく、ルール間の相互作用や合成全体を考慮し、実際に発生するバグを網羅的に捉えるための方法論を通じて、大規模なテキスト処理における堅牢な基盤の構築方法を示している。