はじめに
Flutterの言語リソースを手作業で扱う手順を自分なりに整理してみました。
最初に書いておくと、実運用ではこの手順をやるより、flutter intl pluginを使うのが良いと思います。
正直、Androidのstrings.xmlの感覚で言語リソースを扱えるようにするには
pluginを導入したほうが早いと思いますが、
手作業の手順もできれば試して覚えておくと良いと思います。
以前は手作業だとちょっと複雑で覚えられなかったのでAndroid Studio pluginのflutter i18nを使っていたのですが、
最新のAndroid Studioバージョン(4.0から?)に対応しなくなって困ってました。
手作業での実装手順を覚えないといけないかな・・・と思っていましたが、
flutter intlのpluginが同じように手軽に使えそうです。
これもサポートが切れたら困りますが、今回、手作業の手順もまとめたので万が一そうなっても今後は大丈夫でしょう。
- flutter i18n pluginは2019/10 で更新が止まっている
- flutter intl pluginは2020/1 に初回リリース、2020/6に最終更新
となっていて、ひとまずflutter intl pluginに乗り換えで良さそうです。
ここではpluginの使い方ではなく、手作業(Dart intl tools)での多言語化(国際化)対応の手順を記載します。
初回手順概要
英語(en)と日本語(ja)をサポートするリソースをベースとして作成する手順で記載しています。
他にサポートする言語がある場合は後から追加します。
- Flutterプロジェクトを用意
- 依存関係のパッケージをpubspec.yamlファイルに追加
- 言語リソース用のファイルを格納するディレクトリを作成
- ベースとなるlocalizationのdartファイルを用意("l10n.dart"とする)
- l10n.dartから多言語対応用のファイルを生成("intl_messages.arb")
- 各言語向けリソースとなるlib/l10n/intl_en.arb、lib/l10n/intl_ja.arbを用意
- intl_xx.arbから多言語対応用のファイルを生成
- 言語リソースを使用するdartファイルにて、多言語実装する
今回の例では以下の配置構成としている。通常はl10n.dartとintl_xx.arbファイルが運用時の編集対象となる。
lib/intl/l10n.dart → 多言語化のベースとして必要なファイル
lib/intl/generated → コマンドで生成されるファイルの格納場所
lib/l10n/intl_xx.arb → 各言語用リソースの格納先
初回手順実行
1. Flutterプロジェクトを用意
例としてflutter_l10n_sample
flutter create flutter_l10n_sample
2. 依存関係のパッケージをpubspec.yamlファイルに追加
以下のようにflutter_localizations、intl、intl_translationを追加。
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl:
intl_translation:
・flutter pub getコマンドでパッケージ情報を更新しておく
flutter pub get
3. 言語リソース用のファイルを格納するディレクトリを作成
cd flutter_l10n_sample
mkdir lib/intl/generated
mkdir lib/l10n
4. ベースとなるlocalizationのdartファイルを用意("l10n.dart"とする)
"<ProjectRoot>/lib/intl/l10n.dart" に新規ファイル作成する。
'generated/messages_all.dart'が見つからないエラーになるが、後で自動生成するため無視する。
「TODO START」~「TODO END」の2か所は、実際の運用で適宜更新する。
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'generated/messages_all.dart';
class S {
S();
static S current;
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
static Future<S> load(Locale locale) {
final name = (locale.countryCode?.isEmpty ?? false)
? locale.languageCode
: locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
S.current = S();
return S.current;
});
}
static S of(BuildContext context) {
return Localizations.of<S>(context, S);
}
//TODO START: add your resource
String get greeting_message {
return Intl.message(
'Hello',
name: 'greeting_message',
desc: '',
args: [],
);
}
String greeting_message_with_name(Object name) {
return Intl.message(
'Hello $name',
name: 'greeting_message_with_name',
desc: '',
args: [name],
);
}
//TODO END: add your resource
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
const AppLocalizationDelegate();
//TODO START: customize your support language
List<Locale> get supportedLocales {
return const <Locale>[
Locale.fromSubtags(languageCode: 'en'),
Locale.fromSubtags(languageCode: 'ja'),
];
}
//TODO END: customize your support language
@override
bool isSupported(Locale locale) => _isSupported(locale);
@override
Future<S> load(Locale locale) => S.load(locale);
@override
bool shouldReload(AppLocalizationDelegate old) => false;
bool _isSupported(Locale locale) {
if (locale != null) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode) {
return true;
}
}
}
return false;
}
}
5. l10n.dartから多言語対応用のファイルを生成("intl_messages.arb")
flutter pub pub run intl_translation:extract_to_arb --locale=messages --output-dir=lib/intl/generated lib/intl/l10n.dart
"lib/intl/generated/intl_messages.arb"にファイルが生成・出力される。
l10n.dartを入力として、「--output-dir」で指定したディレクトリにintl_messages.arbが生成・出力される。
6. 各言語向けリソースとなるlib/l10n/intl_en.arb、lib/l10n/intl_ja.arbを用意
{
"_locale": "en",
"greeting_message": "Hello",
"greeting_message_with_name": "Hello {name}"
}
{
"_locale": "ja",
"greeting_message": "こんにちは",
"greeting_message_with_name": "こんにちは {name}"
}
7. intl_xx.arbから多言語対応用のファイルを生成
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/intl/generated --no-use-deferred-loading lib/intl/l10n.dart lib/intl/generated/intl_messages.arb lib/l10n/intl_en.arb lib/l10n/intl_ja.arb
"lib/intl/generated"に以下ファイルが生成・出力される。
messages_all.dart
messages_en.dart
messages_ja.dart
messages_messages.dart
8. 言語リソースを使用するdartファイルにて、多言語実装する
・MaterialAppにてローカリゼーションを設定する。
import 'package:flutter_localizations/flutter_localizations.dart';
import 'intl/l10n.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// ・・・
// アプリのローカリゼーション設定
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
// アプリのサポートするロケール種別設定
supportedLocales: S.delegate.supportedLocales,
// ・・・
);
}
}
・言語リソースを使う
Text(S.of(context).greeting_message),
Text(S.of(context).greeting_message_with_name("Taro")),
# コマンドから実行。またはIDEから実行。
flutter run
ここまでやって、やっと言語リソース定義を使える。
2回目以降の言語リソース変更手順
言語リソース変更時
intl_xx.arb(サポートする言語ロケール数分)を編集する。
- コマンドで言語リソースを再生成
flutter pub pub run intl_translation:extract_to_arb --locale=messages --output-dir=lib/intl/generated lib/intl/l10n.dart
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/intl/generated --no-use-deferred-loading lib/intl/l10n.dart lib/intl/generated/intl_messages.arb lib/l10n/intl_en.arb lib/l10n/intl_ja.arb
言語リソース追加時
-
l10n.dartとintl_xx.arb(サポートする言語ロケール数分)を編集する。
-
コマンドで言語リソースを再生成
-
dartからリソースを参照する
言語リソース削除時
- dartからリソースの参照箇所を削除する
- l10n.dartとintl_xx.arb(サポートする言語ロケール数分)を編集する。
- コマンドで言語リソースを再生成
サポートする言語を追加時
- l10n.dartのsupportedLocalesにサポートするlanguageCodeを追加
- intl_xx.arb(サポートする言語に対応するlanguageCodeのファイル)を用意する
- コマンドで言語リソースを再生成
終わりに
まだそれほどflutterを使い込んでいないですが、
flutterのUI widgetのコーディングは覚えてくるとサクサクできます。
でも多言語化はstrings.xmlのイメージで公式ページで紹介されているMapやintl toolsを使った手順などを見ると、手番が多くてちょっと面倒です。
それでもpluginを使うとほとんど6と8の作業だけで済むのでplugin使うのが便利です。
pluginの使い方は記事にするほどでもないと思うのでたぶん書かないです。(このintl toolを手動で使う手順ほど整理しておくことが思いつかない。)
flutter intl のVS code、Android Studioそれぞれ使用するIDEのpluginページに記載の
Getting started、General Usage Instructionsに記載のInitialize部分まで実施したら、後は割と直感的に使えると思います。
参考
公式:https://flutter.dev/docs/development/accessibility-and-localization/internationalization
Dart intl tools:https://qiita.com/jiro-aqua/items/fd9896682ca09018fdd3
Dart intl tools実行時に出るlocaleのメッセージ:https://qiita.com/sekitaka_1214/items/2114d1870cfe607b4d87
l10nとi18n:https://ja.stackoverflow.com/questions/21726/internationalization-%E3%81%A8-localization-%E3%81%AE%E9%81%95%E3%81%84
l10nとi18n:https://ja.wikipedia.org/wiki/%E5%9B%BD%E9%9A%9B%E5%8C%96%E3%81%A8%E5%9C%B0%E5%9F%9F%E5%8C%96