詳しく
SaaSの多言語対応は初日から:9言語と右から左へのアラビア語
公開日:· 1分で読めます· Anton Shubin
要点
- Seedのアプリ、管理画面、Webサイト、メールは、アラビア語を含む9言語に対応しています。各言語の文言は約3,900件です。
- 文言の欠落、古い翻訳、プレースホルダーの消失があるとテストがビルドを止めます。アラビア語で反転しない「左」「右」の余白も別のテストが止めます。
- 言語は人ごとの設定としてサーバーに保存され、日付の形式、タイムゾーン、時計の表示とは別に選べます。
- 10番目の言語の追加は、リストへの1行、カタログごとのファイル、読み込み用の数行で済みます。
右から左に書くアラビア語も含めて、SaaSを多言語対応させるには、どうすれば後で作り直さずに済むでしょうか。答えは、最初の画面から始めて、ルールの番はテストに任せることです。Seedは9言語で動きます。日本語、英語、ロシア語、スペイン語、ドイツ語、ブラジル・ポルトガル語、アラビア語、フランス語、簡体字中国語です。アプリ、管理画面、Webサイト、メールのすべてが対象で、アラビア語を選ぶとレイアウト全体が左右反転します。
この記事は、「翻訳は後でやろう」が「翻訳は永遠にやらない」と同じ意味だと知っている創業者や開発者のためのものです。文言がどこにあるのか、テストが何を見つけるのか、そして右から左(RTL)への対応に実際どれだけかかるのかをお見せします。
なぜ初日からなのか
後から翻訳すると、すべての画面を二度触ることになるからです。コードに直接書かれた文言、手で組み立てた日付、margin-left を一つ残らず探して直す必要があります。しかもその頃には、コードは機能でいっぱいです。最初から翻訳していれば、新しい画面は作るそばから翻訳されます。かかるのは数分で、誰も手をつけたがらない大仕事にはなりません。
もっと単純な理由もあります。インターネットを使う人の大半は、英語を第一言語として読んでいません。相手の言語で話せるプロダクトは、それだけで一歩先を行けます。
文言はどこにあるのか
Seedの画面に出る言葉は、すべてカタログに入っています。領域と言語ごとに1つのTypeScriptファイルで、i18n/app/ja.ts や i18n/site/de.ts のような形です。領域は5つ。アプリ、管理画面、Webサイト、メール、APIのエラーメッセージです。英語が原文で、ほかのファイルは同じキーを持ちます。
| 領域 | 1言語あたりの文言数 |
|---|---|
| アプリ | 1,503 |
| 管理画面 | 1,487 |
| Webサイト | 682 |
| エラーコード | 164 |
| メール | 79 |
1言語あたり約3,900件で、2026年11月2日に数えた数です。preact-componentsのコンポーネントは、これとは別に自前のカタログを持っています。
アプリは、使っている言語の分だけを小さな別ファイルとして読み込みます。日本語で使う人がドイツ語までダウンロードすることはありません。
翻訳が古くならない仕組み
i18n/i18n.test.ts というテストがあります。次のような場合に失敗します。
- どれかの言語で文言が足りない、または英語にはもうない文言が残っている。
- 翻訳した後で英語が変わった(言語ごとのロックファイルに、どの英語から訳したかが記録されています)。
{count}のようなプレースホルダーや、リンクを囲むタグが消えている、または綴りが違う。- 複数形で、その言語に必要な形が欠けている。アラビア語には6つの形があります。
- どのコードからも使われていない英語の文言がある。
そのため、Notes.create と表示されるボタンや、英語より1週間遅れたドイツ語の画面が本番に出ることはありません。
右から左への対応に本当にかかるもの
レイアウトが「左」「右」と一度も言わなければ、思ったほどではありません。Seedのスタイルは論理的な方向、つまり「始まり」と「終わり」を使います。左から右のページでは始まりが左、アラビア語では右です。反転させるのはブラウザーの仕事です。
これを願いではなくルールにするのがテストです。tests/spacing.test.ts はすべてのアプリを調べ、物理的な方向を名指しする余白のクラスを見つけると失敗します。ページの dir は言語に合わせて設定されます。Webサイトはサーバー側で、アプリは言語を切り替えた瞬間にです。
後で痛い目を見やすい点がもう2つあります。
- 文の中の名前。 「AnnaさんがあなたをTeamグループに招待しました」のような文は、アラビア語では2つの方向が混ざり、ラテン文字の名前が句読点を反対側に引っぱることがあります。ユーザーが入力した値は
<bdi>で囲み、その方向が周りの文に影響しないようにしています。 - 日付、数値、時刻。 すべての画面の日付は、1つのフォーマッターを通ります。別のテスト、
tests/no-direct-date-format.test.tsは、toLocaleString()を直接呼ぶコードを見つけると失敗します。
言語、地域の形式、時計は3つの別の設定
言語はプロフィールと一緒にサーバーに保存されるので、どの端末でも、どのメールでも同じです。地域の形式、タイムゾーン、12時間制か24時間制かは、それぞれ別の設定です。ベルリンに住む日本人は、日本語の画面とベルリンの時刻を見たいはずです。東京で働くイギリス人なら、英語の画面と 2026/11/03 のような日付かもしれません。これを1つの設定に結びつけると、ユーザーの半分に間違った日付を見せることになります。
Webサイトでは、言語はアドレスに入ります(/ja/pricing、/ar/pricing)。ブラウザーやIPによる自動リダイレクトはなく、各ページは検索エンジンに、どの言語版があるかを伝えます。
翻訳は誰がしているのか
AIエージェントです。書かれたガイド i18n/TRANSLATING.md に従って翻訳します。ガイドは言語ごとの口調(日本語なら、です・ます調で「あなた」を使わない)、同じものを全画面で同じ名前で呼ぶための用語集、そして「言葉ではなく状況を訳す」というルールを定めています。新しい翻訳は、別のエージェントが英語に訳し戻して意味を比べます。プロの翻訳会社ではありませんし、そのふりをするつもりもありません。それでも、誰も読めない画面よりはずっと良いものです。
10番目の言語を足すには
言語のリストに1行、領域ごとにカタログファイルを1つ、そして各アプリに読み込み用の数行です。あとはテストが新しい言語に足りない文言をすべて挙げ、そろうまで赤いままです。
試してみてください
デモを開き、プロフィールで言語をアラビア語に切り替えてみてください。アプリ全体がくるりと反転する様子を見たら、日本語に戻しましょう。この記事も9言語すべてで読めますし、コードはMITライセンスで公開しています。