LoginSignup
19

More than 3 years have passed since last update.

Flutterで多言語化する方法

Last updated at Posted at 2018-04-10

2021/1/8追記

現在はarbファイルからdartファイルを生成してくれるようになっていて結構楽になったようです。

以下は当時の手順

公式のチュートリアルを参考にして、Flutterアプリのテキストのローカライズ方法を説明します。

flutter_localizations, intl, intl_translationを依存関係に追加します。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  # 追記ここから
  flutter_localizations:
    sdk: flutter
  intl:
  intl_translation:
  # 追記ここまで

次のコマンドで依存関係を解決します。

cd <プロジェクトのルートディレクトリ>
flutter pub get

次のコマンドを実行して、intl_messages.arb messages_all.dart messages_messages.dart を生成します。 intl_messages.argがjson形式の翻訳定義ファイルになりますが、今は翻訳対象のリソースが指定されていないので大した内容はありません。このコマンドを一度実行するのはdartファイルを生成したいためです。

mkdir -p lib/l10n
flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart

プログラムで使用されているIntl.messageからリソースファイルを生成するため、先に翻訳リソースを使用する部分を記述しておきます。このシンプルな例では Hello Worldを出力するtitleという名前のリソースが生成されます。
そのためにlib/main.dartに以下のクラスを追加します。

lib/main.dart
class DemoLocalizations {
  static Future<DemoLocalizations> load(Locale locale) {
    final String name = locale.countryCode.isEmpty
        ? locale.languageCode
        : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);
    return initializeMessages(localeName).then((_) {
      Intl.defaultLocale = localeName;
      return new DemoLocalizations();
    });
  }

  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }

  String get title {
    return Intl.message(
      'Hello World',
      name: 'title',
      desc: 'Title for the Demo application',
    );
  }
}

そしてもう一度arbファイルの生成コマンドを実行します。

flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart

するとl10n/intl_messages.arbは次のような内容になります。titleの定義が追加されました。

l10n/intl_messages.arb
{
  "@@last_modified": "2018-04-06T20:58:49.424203",
  "title": "Hello World",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  }
}

このl10n/intl_messages.arbをコピーしてl10n/intl_en.arbl10n/intl_ja.arbを作ります。
内容はそれぞれ次のようになります。単純に翻訳しているだけですね。

l10n/intl_en.arb
{
  "title": "Hello World",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  }
}
l10n/intl_ja.arb
{
  "title": "こんにちは世界",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  }
}

ここまでファイルの準備ができたら以下のコマンドを実行します。

flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/main.dart lib/l10n/intl_*.arb

すると l10n/messages_en.dartl10n/messages_ja.dart という2つのdartファイルが生成されます。
l10n/messages_ja.dartの内容は次のようになっており、先ほど作成したリソースファイルがdartのクラスとして生成されたことがわかります。

l10n/messages_ja.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a ja locale. All the
// messages from the main program should be duplicated here with the same
// function name.

import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';

final messages = new MessageLookup();

// ignore: unused_element
final _keepAnalysisHappy = Intl.defaultLocale;

// ignore: non_constant_identifier_names
typedef MessageIfAbsent(String message_str, List args);

class MessageLookup extends MessageLookupByLibrary {
  get localeName => 'ja';

  final messages = _notInlinedMessages(_notInlinedMessages);
  static _notInlinedMessages(_) => <String, Function> {
    "title" : MessageLookupByLibrary.simpleMessage("こんにちは世界")
  };
}

次にMaterialAppのインスタンス生成部分を次のように変更します。

    return new MaterialApp(
      title: "AppName",
      home: new Invite(),
      // ここから追記
      localizationsDelegates: [
        const DemoLocalizationsDelegate(), 
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale("en", "US"),
        const Locale("ja", "JP")
      ],
      // ここまで追記
    );

DemoLocalizationsDelegateは今回のローカライズのためのDelegateです。
GlobalMaterialLocalizations.delegate は多分組み込みのローカライズのもの?日付のフォーマットなどが管理されている。
GlobalWidgetsLocalizationsはウィジェットをローカライズするために必要?右から書く言語とかに対応するためにいるのか?
supportedLocaledは対応言語のリストですかね。
このあたり必要性がよくわかっていないのですが、書かなかったら動かなかったので、必要なんでしょう。

main.dartには先ほどのDelegateも追加しておきます。

lib/main.dart
class DemoLocalizationsDelegate
    extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => ['en', 'ja'].contains(locale.languageCode);

  @override
  Future<DemoLocalizations> load(Locale locale) =>
      DemoLocalizations.load(locale);

  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;
}

ここまでやってリビルドするとローカライズされたテキストを出力することができます。

英語 日本語

感想

現時点では手順が少し面倒に感じますね。
ただintlを使用しないで直接マップをコードに書く方法もあったりして、選択するライブラリ次第では改善出来るのかもしれません。

そして使用する画面毎にクラスを分けたほうがいいのか。巨大な1つの言語リソース用のクラスを作っておくのか悩ましいところです。
クラス定義して関数まで作っておく必要があるのが、ちょっとしんどい。それをもとにarb作って、またdartのクラスが生成されてって感じなので。

まぁまずは使い続けてみることで自分なりの解決策が見つかるかもしれないので、気長に使ってみます。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19