旅行アプリ「Daylight Trip」が英語に対応しました。端末の言語設定が英語なら、アプリ全体——画面、日付、持ち物リスト、PDF、そして最初に入っているデモ旅程まで——が英語で動きます。v1.3.0 として本日公開です。

実は、英語対応の作業はほぼ1日で終わりました。以前別のアプリを多言語化したときは何日も苦しんだ記憶があるので、自分でも驚いています。今回はその理由と、途中で踏んだ2つの罠を書きます。
速かった理由: 3週間前の設計判断が効いた
国際化でいちばん怖いのは、文字列の翻訳ではありません。「自国前提」がロジックの奥に埋まっていることです。
着手前に洗い出したところ、Daylight Trip にはそれがほぼありませんでした。
- 時刻処理は開発初日から「UTCで計算、表示は各都市のタイムゾーンで」を徹底していて、端末のタイムゾーンすら使っていない。ニューヨーク在住の人がパリ→オスロの旅程を組んでも、コードは1行も変えずに正しく動く
- 都市データ(GeoNames)は英語名が主キーで、日本語名は「追加」の構造。英語検索は最初から動いていた
- 持ち物リストの「変換プラグ」「パスポート」は日本人専用ではなく海外旅行者みんな共通の持ち物。翻訳するだけでよかった
つまり残った作業は「UI文字列の翻訳」だけ。旅行アプリという性質上、時差を最初から真剣に扱っていたことが、そのまま国際化の下準備になっていたわけです。
技術選定: あえてARBを使わず「tr(日本語, English)」方式に
Flutterの多言語化の王道は ARB ファイル+gen_l10n ですが、今回は採用しませんでした。理由は2つ。
ひとつは、このアプリには BuildContext が存在しない場所に文字列が多いこと。PDF生成サービスや、カテゴリ名を返す純関数がそれです。context 経由で翻訳を引く標準方式とは相性がよくありません。
もうひとつは、規模。個人開発の1アプリに、キーを考えて、ARBを2ファイル編集して、生成コードを経由して…という距離の長さは過剰でした。代わりにこうしました。
/// 起動時に端末言語を1回判定
String tr(String ja, String en) => L10n.isJa ? ja : en;
// 使う場所に日英を並記
Text(tr('持ち物を追加', 'Add an item'))
翻訳がコードのその場に並ぶので、レビューも修正も一目瞭然。PDFでもどこでも使えて、しかもテスト環境ではデフォルト日本語なので既存テスト90本が1文字も修正なしで通りました。数十言語に増やすならARBにすべきですが、日英の2言語なら、この素朴さが正解だったと思います。
罠その1: tr() は const の敵
置換作業で一番多かったコンパイルエラーは「Method invocation is not a constant expression」。tr() は実行時に切り替える関数なので、const Text(...) の中では使えません。const を外して回る地味な作業が発生します。しかも const SnackBar(content: Text(tr(...))) のように、constが親ウィジェットに付いているとエラー位置から一見分かりにくい。「tr を入れたら、その上の const を疑え」が合言葉になりました。
罠その2: London と入れたのに「ロンドン」で保存される
実機を英語にして試したときに見つけた不具合がこれです。UIは完璧に英語なのに、都市検索で London を選ぶと、旅程にはロンドン(カタカナ)で保存される。
原因は、都市選択時の「表示名は日本語優先」という処理が言語設定を見ていなかったこと。日本語ユーザーには正しい親切が、英語ユーザーには意味不明の挙動になる——国際化とは、こういう「片方では正しい思い込み」を一つずつ引き剥がす作業なのだと実感しました。表示名と保存名をUI言語に追従させて解決です。
デモ旅程まで英語にする理由
地味にこだわったのが、初回起動時に入っているデモ旅程です。英語端末では「Winter in the Nordics (demo)」として、都市も Tokyo / Paris / Oslo / Tromsø、予定も「Louvre Museum」「Northern lights tour」で生成されます。
初めて開いた画面が母語かどうかは、アプリの第一印象そのものです。UIだけ英語でデモが日本語だったら、「日本のアプリを無理やり使っている感」が出てしまう。最初の10秒のために、ここは手を抜きませんでした。
まとめ
- 国際化の重さは翻訳量ではなく「自国前提が設計に埋まっている深さ」で決まる。時差を最初から正しく扱うアプリは、国際化の半分が終わっている
- 2言語なら tr(ja, en) 並記方式は十分戦える。ARBは言語が増えてから
- tr() と const は共存できない。「あるのに気づけない」ならぬ「動くのに保存が違う」(London/ロンドン問題)にも注意
- デモデータの言語は第一印象。UIと一緒に必ず翻訳する
Daylight Trip は App Store / Google Play で公開中です。海外の友人がいる方、ぜひ教えてあげてください——今日から英語で使えます。
アプリの詳細はこちら: Daylight Trip 紹介ページ
Daylight Trip は SINQWELL のアプリです(Android / iOS)。


