Flutterの多言語ファイルの管理は難しい
Flutterの多言語は管理がやや複雑です。
具体的な説明や実装方法は詳しい記事がありますので、こちらに譲ります。
Flutterで多言語化対応する(Dart intl toolsの使い方)
簡単にまとめると、
- Dart intl toolsをpubspec.yamlに追加
- 文字列管理クラス(ここではstrings.dart)を用意
- 翻訳する文字列をdartファイルに記述
- 翻訳ファイルであるarbをコマンドで出力
- 他の言語のarbを作成
- arbファイルからdartファイルをコマンドで生成
となります。
また、文言の追加や修正となった場合、[3]の手順から処理を繰り返す必要がある上に、翻訳ファイルの一覧性もなく、管理しづらいという難点がありました。
Spread Sheetで翻訳データを管理する
そこで、翻訳データをスプレッドシートで管理し、FlutterプロジェクトへはGoogle App Scriptを使って必要なファイルを自動出力するようにしてみました。
Flutter多言語翻訳管理シート
Flutter多言語翻訳管理シートのスクリプト
GASのコードはこちらにも置いてあります
https://github.com/shcahill/FlutterStringResourceScript
ちなみに参考にした大元のスクリプトはこちらです。
Android iOS Drive Export
Flutterプロジェクト側の準備
intlパッケージの追加
dependencies:
flutter:
sdk: flutter
# 多言語対応
flutter_localizations:
sdk: flutter
intl:
intl_translation: ^0.17.5
...
strings.dart
の追加
l10フォルダを作り、そこに以下の様なstrings.dart
を用意します。
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart';
import 'strings_impl.dart';
/// 文字列リソース管理クラス
class Strings with StringsImpl {
static Future<Strings> load(Locale locale) {
final String name = '${locale.languageCode}_${locale.countryCode}';
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
return Strings();
});
}
static Strings of(BuildContext context) {
return Localizations.of<Strings>(context, Strings);
}
static final Strings instance = Strings();
}
この時点ではmessages_all.dart
やStringsImpl
などが解決しませんが、必要なファイルはGASによる自動生成と、コマンドでの生成になるので、今はスルーしておいてOKです。
ちなみに、以下のようなdartファイルがGASで出力されます。
import 'package:intl/intl.dart';
// DO NOT EDIT. THIS FILE IS GENERATED by https://docs.google.com/spreadsheets/d/1dU5UR3neGr0jw8qyMS0K-AdO3rLTBB7-sptw_e68FQs/edit
mixin StringsImpl {
/// region Common
String get settings => Intl.message('Setting', name: "settings");
String get next => Intl.message('next', name: "next");
String get back => Intl.message('back', name: "back");
/// endregion
/// region XXX画面
String fromTo(String from, String to) => Intl.message('Go from {from} to {to}', name: "fromTo", args: [from, to]);
String count(String count) => Intl.message('{count}', name: "count", args: [count]);
/// endregion
}
これは、多言語対応のロジックと翻訳部分にファイルを分けることで、GASで出力するファイルの責務を翻訳部分だけに集中させるためです。こうすることで、GASが出力すべきファイルは翻訳内容であるstrings_impl.dart
のみになり、変更が入る可能性のあるロジック部分をGASで気にする必要がなくなります。
Spread Sheetの準備
新規シート作成
Flutter多言語翻訳管理シートをベースに個人のSpread Sheetを用意します。
その後、ツール > スクリプトエディタ
を開き、下記に用意したスクリプトとhtmlをコピーします。
Flutter多言語翻訳管理シートのスクリプト
Spread SheetのメニューにExportメニューを追加する
menu.gsのonOpen()を実行すると以下の画面になります。
許可を確認
を選択
アカウントを選択すると、怪しげな画面が表示されますが、ダイアログ下部の詳細
をクリックして内容を表示します。
menu(安全ではないページ)に移動
をクリックします
(表示されない場合は英語表示だと該当の項目が表示されるかもしれません)
許可
をクリック
Spread Sheetのメニューにエクスポート
メニューが追加されています。
多言語翻訳ファイルの出力
追加されたエクスポート
メニューを実行してしばらく待つと、以下の様になります。
(スクショはAll
を選択した場合です。実行時間はちょっと長いです)
Flutter/Android/iOSそれぞれzipで落とせる様になっています。こちらをDLして展開したら、そのままプロジェクトにコピペできるような構成になっています。
コマンドからdartファイルの生成
GASではstrings_impl.dart
,xxx.arb
が生成されるため、最後にコマンドでdartファイルを生成する必要があります。(Android/iOSはリソースファイルを差し替えるだけ。)
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/strings_impl.dart lib/l10n/intl_messages.arb lib/l10n/intl_ja.arb lib/l10n/intl_ko.arb lib/l10n/intl_zh-cn.arb lib/l10n/intl_zh-tw.arb
以上でビルドエラーも解消し、動く様になると思います。
使い方の補足
言語の追加方法
サンプルでは日英中(簡体/繁体)韓の5カ国語ですが、追加や変更はいくらでもできます。
必要な分だけ横に言語を追加してください。
翻訳データの追加
FlutterKey
(A列)、AndroidKey
(B列)、iOSKey
(C列)にkeyが記載されている場合のみ、そのOS用のリソースを出力します。そのため、必要な翻訳データの部分にはkeyを入力するようにしてください。keyがそのままコードからアクセスするものになります。(例えばAndroidだったらstrings.xmlのリソース名になります)
regionタグの出力
Android Studioのregionタグに対応しています。
<XXX画面>
の様に行を追加しておくと、以下の様に出力されます。(<
でタグを判別しています)
/// region XXX画面
...
/// endregion
複雑な文字列の出力
たとえば、東京から大阪まで行く
という文字列があって、東京
と大阪
の部分が可変だった場合の記述は以下のようになります。
{from}から{to}まで行く
すると、dart側のコードは以下のように出力されます。
String fromTo(String from, String to) => Intl.message('Go from {from} to {to}', name: "fromTo", args: [from, to]);
あとは使う側は以下のようになります。
Strings.of(context).fromTo("東京", "大阪");
TODO
一通り自分が必要な機能は作りましたが、スクリプトは初心者のため、以下の機能は断念しました…
また、パフォーマンスの問題であったり、コードの美しさには自信がありませんので、どなたか教えていただけるととても助かります。
- 変数の受け取りは、Stringしか受け付けられない(Flutter)
- Android/iOS部分は手抜き
- 単数系・複数系の対応ができていない
- 女性名詞・男性名詞などに対応していない
- 実行速度が遅い
- スクリプト実行後、zipのDLが自動で出来ない
- 試してみたものの、Blob周りで詰まってしまいました…
- 同じkey(A列またはB/C列のこと)が登録されていてもエラー検出できない