57
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Flutterで多言語化対応する(Dart intl toolsの使い方)

Last updated at Posted at 2018-03-03

Flutterに入門しました。
サンプルアプリを動かしてみたあと、「とりあえず多言語化対応はどうやるのかな」とAndroidのvalues/strings.xmlみたいなものを念頭に置きながら調べ始めて、最終的に到達した結論を書き残しておきます。
三日ぐらいしかいじってないので、ぼけてるところがあればツッコミお願いします。

Flutterで多言語化対応するには

まず、Flutter/Dartの開発系には文字列リソースという基本的な仕組みはないようです。
Flutter公式では以下のガイドラインが掲載されています。
https://flutter.io/tutorials/internationalization/

リンク先をざっと眺めて欲しいのですが、つまるところMapにKVペアを突っ込んで対応する文字列を返すコードを書け、と言うことになります。

さすがにこれはいくら何でもアレでしょう、ということで、Dart intl toolsというものが用意されていることがAppendixに書いてあります。本稿では、このツールの使い方を説明します。

Dart intl toolsとは

Dartコマンドラインで動く文字列リソースの加工ツールです。

  • dartファイル内の国際化リソースをARBファイルに抽出する。
  • ARBファイルを元に対応するdartコードを生成する。

の二つのツールからなっています。

その前に

Android StudioのNew Flutter Projectで作るサンプルアプリを元に進めていきます。
完成品はこちらに(github)

まず、パッケージをプロジェクトに追加します。

pubspec.yaml
dependencies:
  flutter_localizations:
    sdk: flutter
  intl:
  intl_translation:

Dependenciesセクションに上の四行を追記します。

次に、lib/l10nディレクトリを掘り、以下のファイルを追加します。

Strings.dart
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';

import 'messages_all.dart';

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

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

  static final Strings instance = new Strings();

  // TODO 追加すべき文言
  String get title => Intl.message('Flutter Demo Home Page', name: "title");
  String get message => Intl.message('You have pushed the button this many times:', name: "message");
}

この時点で、importやinitializeMessages()が解決しませんが、問題ありません。
TODOコメントの後が、必要に応じて追加すべき文言となります。

ARBファイルのエクスポート

プロジェクトのルートで以下のコマンドを実行します。

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

これによって、lib/l10n/Strings.dart 内のIntl.message()呼び出しが抽出されて、intl_message.arbが出力されます。

intl_messages.arb
{
  "@@last_modified": "2018-03-04T02:14:41.057019",
  "title": "Flutter Demo Home Page",
  "@title": {
    "type": "text",
    "placeholders": {}
  },
  "message": "You have pushed the button this many times:",
  "@message": {
    "type": "text",
    "placeholders": {}
  }
}

これを元に以下の様なファイルを作成します。

intl_ja.arb
{
  "title": "フラッターデモホームページ",
  "message": "ボタンを押した回数がここに表示されます"
}

ARBからdartコードの生成

ここで、以下のコマンドを実行します。

$ flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/Strings.dart lib/l10n/intl_messages.arb lib/l10n/intl_ja.arb

これによって、以下のファイルが生成されます。

  • messages_all.dart
  • messages_ja.dart
  • messages_messages.dart

これらのファイルは DO NOT EDIT とあるので、編集してはいけません。
この時点で、先のビルドエラーは消えるはずです。

呼び出し側の設定

以下の修正を行います。

main.dart
import 'dart:async';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'l10n/Strings.dart';
main.dart
class _MyLocalizationsDelegate extends LocalizationsDelegate<Strings> {
  const _MyLocalizationsDelegate();

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

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

  @override
  bool shouldReload(_MyLocalizationsDelegate old) => false;
}
main.dart
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      localizationsDelegates: [
        const _MyLocalizationsDelegate(),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', ''),
        const Locale('ja', ''),
      ],
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

MaterialAppのパラメータに、localizationsDelegatesとsupportedLocalesを追加します。

ここまでで準備は終わり。文字列の呼び出しを修正します。

main.dart
 appBar: new AppBar(
   title: new Text(Strings.of(context).title),
 ),
main.dart
 new Text(
   Strings.of(context).message,
 ),

Strings.of(context).[id] の形で文字列を呼び出すことが出来ます。

実行するとこんな感じになります。日本語表記/英語表記

文言の管理

この後の文言の管理手順としては

  1. Strings.dart に項目を追加
  2. ARBに変換
  3. intl_ja.arb に日本語翻訳を追加
  4. dartを生成
  5. 呼び出す
    となります。
    全部手作業でやるよりは少しはマシになったと思われます。

困った時は

Package追加した時、Android Studioの上の方に'get package'と出るので、クリックしましょう。
その際にエラーが出る場合は、FLUTTER_ROOTの設定し忘れかもしれません。
2018/03/04現在のFlutter インストールガイドではFLUTTER_ROOTへの言及がないので、設定しておきましょう。
(Flutterをインストールしたディレクトリを設定します)

参考) flutter projectでpub runコマンドが打てない時の対処法
https://qiita.com/konifar/items/d93e20ac0461e58a5247

参考リンク

Internationalizing Flutter Apps (公式ローカライゼーションのガイド)
https://flutter.io/tutorials/internationalization/#loading-and-retrieving

github dart-lang/intl_translation (ツールのソース)
https://github.com/dart-lang/intl_translation

github konifar/droidkaigi2018-flutter (現時点で日本語化されている数少ないFlutterアプリ)
https://github.com/konifar/droidkaigi2018-flutter

本記事のサンプルソース
https://github.com/jiro-aqua/flutter-localization

免債

Windows/Android環境でしか動かしていません。

感想

当分はポチポチ遊びながら、プロダクト投入可能なラインを探っていくことになるのでしょう。面白そうなので、今後に期待したいと思います。

以上です。

57
32
2

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
57
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?