こんにちは、初めまして。
5月からウーオの中の人になったソフトウェア開発者の石田 (@geckour) です。
今回は Flutter での多言語化対応を地道に進めた話をしたいと思います。
日本語 | 英語 |
---|---|
目次
出発地点
UUUO では、このところ海外向けに展開するために多言語化対応を進めていました*1*2。
最初は flutter_localizations
を使っていたのですが、シンプルなテキストなら問題なく適用できるものの、複雑なテキストを扱おうとした際に問題が起きました。
flutter_localizations
flutter_localizations
とは、Flutter が公式に出している多言語化対応のためのパッケージです。
小さな設定と簡単な記述でそこそこ色々な機能を使って多言語化対応ができます。
flutter_localizations でできること (抜粋)
flutter_localizations
はなかなかに豊富な機能を備えていて、以下のようなことができます。
- Locale による単純な文字列出し分け
- 変数埋め込みへの対応
- 複数形 (変数の数による表現の切り替え) への対応
- Enum 変数による表現の切り替え
これだけでかなりのケースがカバーできると思います。
flutter_localizations だけでは難しいこともある
しかし、例えば RichText
のように "文字列の部分部分で装飾する" 必要がある場合、英語と日本語のように文節で区切ったときに順番が入れ替わる可能性がある言語群に対応しようとすると、無理が生じてしまいます。
以下の例だと装飾の都合上文節レベルで区切って文字列定義を作る必要がありますが、日本語と英語で装飾対象の文節の位置が変わるので、そのままではうまく対応できません。
例1: 以下の日本語における 利用規約
と プライバシーポリシー
を太字にしたい。英語における相当箇所 (Terms of Service
と Privacy Policy
) も同様。
- 日本語:
利用規約とプライバシーポリシーに同意する必要があります
- 英語:
You must agree to the Terms of Service and Privacy Policy
RichText( text: TextSpan( children: [ // 英語の場合は後ろに持っていきたい TextSpan( text: t.terms_of_service, style: TextStyle(fontWeight: FontWeight.bold), ), TextSpan( text: t.and, ), TextSpan( text: t.privacy_policy, style: TextStyle(fontWeight: FontWeight.bold), ), // 英語の場合は前に持っていきたい TextSpan( text: t.must_agree, ), ], ), )
{ "terms_of_service": "利用規約", "and": "と", "privacy_policy": "プライバシーポリシー", "must_agree": "に同意する必要があります" }
{ "terms_of_service": "Terms of Service", "and": " and ", "privacy_policy": "Privacy Policy", "must_agree": "You must agree to the " }
slang
flutter_localizations
のみでは多言語化の実現が難しいパターンがあることを紹介しましたが、そんなときに強い味方となってくれるパッケージがあります。
以前は fast_i18n
として提供されていて、今は slang
として生まれ変わった多言語化対応のためのパッケージです。
slang でできること (抜粋)
基本的に flutter_localizations
でできることは全て slang
でもできます*3。
加えて、
- RichText への対応
- 爆速コード生成
- List/Map で定義した文字列への参照
- 変数埋め込み記法の変更
などが利用できます。
以下に軽く説明していきますが、基本的に公式ドキュメントに全て書かれているので詳しく知りたい方はそちらをご参照ください。
RichText への対応
文字列定義のキー名末尾に (rich)
と付けると、文字列定義の値に RichText
向けのマーカーを設定してコード内で利用することができるようになります。
先程の例を使うと、
例2: 以下の日本語における 利用規約
と プライバシーポリシー
を太字にしたい。英語における相当箇所 (Terms of Service
と Privacy Policy
) も同様。
- 日本語:
利用規約とプライバシーポリシーに同意する必要があります
- 英語:
You must agree to the Terms of Service and Privacy Policy
{ "caution(rich)": "${bold(利用規約)}と${bold(プライバシーポリシー)}に同意する必要があります" }
{ "caution(rich)": "You must agree to the ${bold(Terms of Service)} and ${bold(Privacy Policy)}" }
RichText( text: t.caution( bold: (text) => TextSpan( text: text, style: TextStyle(fontWeight: FontWeight.bold), ), ), )
このように簡潔に記述することができます。
爆速コード生成
flutter_localizations
での文字列定義ファイルからの Dart
コード生成には flutter pub get
を使うかと思いますが、slang
の場合は (build_runner
を利用している場合) flutter pub run build_runner build -d
を使います。
ただ、このコマンドは往々にして実行に時間がかかるため、開発中は代替の flutter pub run slang
が推奨されています (build_runner
を利用している場合も使えます) 。
こちらは爆速です。
公式ドキュメントによると、CI などは (コード生成がいっぺんにできるからか) flutter pub run build_runner build -d
がおすすめだそうです。
変数埋め込み記法の変更
slang
のデフォルトの変数埋め込み識別子は $hoge
または ${hoge}
のような Dart
の文字列への変数埋め込みと同じ記法です。
ただ、flutter_localizations
からの移行の場合、なるべく変換の手間を減らすために {hoge}
の形式にしたいという需要もあるかと思います。
slang
は設定ファイルによる変数埋め込み記法の変更に対応しているのでこちらも実現可能です。
具体的には、(build_runner
を利用している場合) build.yaml
を用意して、
targets: $default: builders: slang_build_runner: options: string_interpolation: braces
このように記載するだけです。
build_runner
を利用していない場合は slang.yaml
が使えますが、そちらを使う場合は
string_interpolation: braces
このようにより簡単です。
まとめ
Flutter で多言語化対応したい場合、flutter_localizations
でも十分色々なことができますが、slang
を利用すればより柔軟な表現に対応できることを紹介しました。
個人的にはいずれ (割と近い未来に) RichText
を使う必要が出てくるなどして乗り換えなければならないかもしれないリスクを抱えながら flutter_localizations
を利用するよりは、最初から slang
を利用することをおすすめします。
最後に
ウーオでは水産流通を革新するため、プロダクトを通じてあらゆるアプローチをしています。ウーオの事業やプロダクト開発にご興味がある方は、以下をぜひご覧ください 👇
各ポジションの募集要項は以下をご覧ください 👇
ソフトウェアエンジニア(Mobile Application/Flutter) / 株式会社ウーオ
ソフトウェアエンジニア(Frontend/Flutter on the Web) / 株式会社ウーオ
ソフトウェアエンジニア(Backend/Ruby on Rails) / 株式会社ウーオ
それでは!