この記事はFlutter Flutter #2 Advent Calendar 2021 3日目の記事です。
Flutterのアプリを多言語化対応するための私なりのベストプラクティスとハマった箇所を記録します。多言語対応、意外と簡単だ、というブログを結構見かける。しかし、実際やってみると、苦労したので残しておきます。ハマるのは、私だけなのかなぁ、、、
公式はちょっと古くてよく動かんし、他のブログも「これはいるのか?」という処理が書かれていたので、なるべくシンプルな形にしました。私が次のアプリでやるときのメモを兼ねて、書いておきます。
実際に実施している動画もあるので、信憑性を確認してからやって頂いても大丈夫です。
【追記】2022年12月にこの記事を参考にしても動作しましたので、タイトルを更新しました。
対象者
Flutterを使用してアプリ開発をしており、アプリを多言語対応をしようという方向けです。Flutter開発の基本的な知識はあることを想定しています。カウントアップアプリを自由自在に使いこなせていると見なします。
背景
アプリ開発をしていますが、ユーザの対象を日本人だけでなく、外国人も視野に入れようとすると、どうしても外国語対応が必要です。自分のアプリにお金を払ってくれる人が、日本では10名くらいでも、世界中では1000人くらいいないかなぁ、という願望です。
動画で確認
[Flutter]最短で75言語に対応させ、あなたのアプリを世界の人が届ける方法
Flutter Meetup Tokyo #17 & Flutter Meetup Osaka #9 | Flutterの国際化対応
環境
Windows 10
AndroidStudio
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1348], locale ja-JP)
[√] Android toolchain – develop for Android devices (Android SDK version 31.0.0)
[√] Chrome – develop for the web
[X] Visual Studio – develop for Windows
[√] Android Studio (version 2020.3)
手順
プロジェクトを作成
通常通りに作成します。皆さんお馴染みのカウントアップを多言語化対応します。
Flutter自体の多言語対応の処理
pubspec.yamlに以下のように修正します。
dependencies:
flutter:
sdk: flutter
flutter_localizations: #追加
sdk: flutter #追加
cupertino_icons: ^1.0.2
intl: ^0.17.0 #追加
flutter:
uses-material-design: true
generate: true #追加
言語のファイルを作成する
[プロジェクトのルート]/lib の直下に l10n というディレクトリを作成します。その下に app_en.arb というファイルを作成します。こちらが英語で、原本になります。この英語を多言語に翻訳していきます。
{
"@@locale":"en",
"title":"Flutter Demo Home Page",
"message": "You have pushed the button this many times:"
}
言語のクラスを作成する
プロジェクトのルートでターミナルで、以下を実行します。
flutter gen-l10n --no-nullable-getter
そうすると、[プロジェクトのルート]/.dart_tool/flutter_gen/gen_l10n というディレクトリが作成されます。その中に、ファイルが2つ作成されます。
- app_localizations.dart
AppLocalizationsという抽象クラスがあります。言語ファイルの中のキーにクラスでアクセスできるようになってます。 - app_localizations_en.dart
英語の言語ファイルをAppLocalizationsに当てはめると何になるのか、を記載するクラスになります。
–no-nullable-getter のオプションを使用しています。現実にはAppLocalization.of(context)が見つからずNullになるケースもあるのですが、「絶対にある」という前提でソースを作成したいため、このオプションをつけてます。つけないと、「AppLocalization.of(context)!」という風に記述する必要があり、ちょっと面倒です。Nullセーフティの観点からは良くないかもしれませんが、書きやすさ重視で、目をつむってます。
画面のコードを修正する
それでは実際に言語のクラスからのデータを表示できるように、main.dartを修正していきましょう。その前に flutter pub get もお忘れなく。
まず、作成したらクラスをインポートします。(pubspec.yamlのgenerate: true は、flutter_genディレクトリにファイルがあることを示すものだと思われる)
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
MaterialAppに多言語対応用の設定を追加します
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: AppLocalizations.localizationsDelegates, // 追加
supportedLocales: AppLocalizations.supportedLocales, // 追加
メッセージを言語のクラスから取得するように修正します。まずは、タイトル部分。
return Scaffold(
appBar: AppBar(
- title: Text(widget.title),
+ title: Text(AppLocalizations.of(context).title),
),
メッセージ部分も修正します。動的にメッセージを取得するため、固定値でなくなりました。constを削除します。
const Text(
- 'You have pushed the button this many times:',
+ Text(
+ AppLocalizations.of(context).message,
),
こちらで実行して頂くと、カウントアップアプリの外観は変わらない、、、ですが、メッセージは言語のクラスから取得するように変更されました。
日本語のファイルを作成する
引き続き、日本語の言語ファイルを作っていきましょう。app_en.arb と同じディレクトリに、app_ja.arb を作成して、以下を入力してください。
{
"@@locale":"ja",
"title":"Flutterのデモページ",
"message": "あなたは以下の回数ボタンを押しました:"
}
そして、ターミナルで、以下のコマンドを実行します。
flutter gen-l10n --no-nullable-getter
このような感じで、言語ファイルを量産していけば、多くの言語に対応できるようになります。
エミュレータの言語設定
自分のスマートフォンは自分の言葉になっていると思います。しかし、エミュレータを使用していると、デフォルトのままで使用しているので、上記の場合日本語が出てこないかも知れません。
Androidの言語変更
「設定」→、、、って、日本語だから、問題ないや。一度、英語に戻す、、、
「Setting > System > Languages&input > Languages」から「Add a language」をタップ。検索でjと入れたら、「日本語」が出てきたので、選択した。その後、1番上に「日本語(日本)」が来るように移動させた。
ここら辺は、OSや端末ごとに違うので、参考程度でお願いします。
iOSの言語変更
「Settings> Lnaguage & Region」から日本語を上にする
プログラム側で言語を指定する方法
MaterialApp.localeに表示したいLocaleを指定することで、プログラム側で言語を指定することが可能になります。また、この値を変更することで、アプリ内で言語を変更することが可能です。
return MaterialApp(
home: MyHomePage(title: AppLocalizations.of(context).title),
locale: Locale('ja')
);
ハマった点
Android StudioでAppLocalizations関連に赤い下線がつく
AppLocalizationsの下に赤い線がでますが、実行はできます。Android Sutdioのバグかと思われます。現象は、flutter pub get 実行後に発生します。解決策は、Android Studioを再起動する。
Null check operator used on a null value の赤画面が発生した
以下のようにすると、発生します。
return MaterialApp(
home: MyHomePage(title: AppLocalizations.of(context).title),
);
調べたところ、どうやらMaterialApp直下に入れると、AppLocalizationsの初期化完了前に呼び出されるため、Nullでエラーが発生するようです。そのため、AppLocalizations.ofはScaffold内で使った方が安全です。
l10n.yaml があると文句を言われる
flutter gen-l10nを実行すると、以下のようなメッセージが出力された。色々と言語のクラスの作成方法があるようで、参考にしていたページが「アプリを実行すると、自動で言語のクラスが生成される」(私の環境を除く)方法だった。そのため、l10n.yamlがあった。それが残っていた。消したら、実行できるようになった。
Because l10n.yaml exists, the options defined there will be used instead.
To use the command line arguments, delete the l10n.yaml file in the Flutter project.
GoRouteを使っていた
画面遷移のプラグインで、GoRouteというのを使っている。そちらは、MaterialAppに言語設定を追加するのではなく、プラグインのメソッドで言語設定する必要があった。
return MaterialApp(
home: MaterialApp.router(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
以上のように(言語設定以外は削除してます)、しなければならないことに気づかなかった。何かプラグインをかましていたら、気をつける必要があります。
これを機会に、Navigator2.0に入れ替えるかなぁ、、、
Builderを使っていた
いつ入れたのかは定かではないし、入れた覚えもないのだが、以下のようにBuilderが入っていて、そのためかnullチェックが発動して(Null check operator used on a null value)、赤い画面が出力された。
home: Scaffold(
body: Builder(
builder: (context) {
return Center(
Builderを消したら、普通に動いた。
「Error: Couldn’t resolve the package ‘flutter_localizations’ 」のエラー
以下を追記するのを忘れていた。
flutter_localizations:
sdk: flutter
エラーメッセージ
Error: Couldn't resolve the package 'flutter_localizations' in 'package:flutter_localizations/flutter_localizations.dart'.
.dart_tool/flutter_gen/gen_l10n/app_localizations.dart:5:8: Error: Not found: 'package:flutter_localizations/flutter_localizations.dart'
import 'package:flutter_localizations/flutter_localizations.dart';
何回もやっているので、舐めてかかって、痛い目を見てます、、、
ソース
ソースは、以下に置いておきます。
https://github.com/fluttersalon/l10n
ブランチ:
- master 最終版
- 1_change_pubspecyaml: pubspec.yamlを修正
- 2_changeCode: 実際にコードを言語のクラスから取得するように変更
- 3_addJapanese: 日本語の言語ファイルを追加
参考
- Internationalizing Flutter apps
Flutterの公式。 - Flutterの多言語対応は意外に簡単だった
簡単じゃなかったっす。公式を挫折して読みました。どのような仕組みなのかが理解できました。 - Why is Flutter not generating the internationalization files?
flutter gen-l10n を知る - 【Flutter】Localization(L10n)対応
色々細かい設定が載っていて参考になる。iOSはInfo.plistの修正が必要とあるが(他のサイトで書いてある)、私はシミュレータでも、実機でも修正せずに変更できた。なぜだろう。
豆知識
言語をプログラム側で設定する
MaterialApp.localeに設定する。
@override
Widget build(BuildContext context) {
return MaterialApp(
locale: Locale('en'),
);
}
プログラム側で言語設定を取得する
Localizations.localeOf(context).languageCode
//=>'ja'
まとめ
ということで、Flutterの言語化対応を説明しました。皆様のFlutter開発に役に立てば幸いです。
宣伝
この記事を作成していたり、言語ファイルを機械的に大量生成する方法を教えてもらったりした末に、多言語対応に興味が湧いて、色々と調べました。その情報をまとめて、Udemyで講座にしました。アプリを75言語に対応させる方法、アプリ内で動的に言語設定を切り替える方法などをまとめています。
ご興味があれば、見てみてください!