33
28

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の多言語ファイルを管理する

Last updated at Posted at 2019-08-05

Flutterの多言語ファイルの管理は難しい

Flutterの多言語は管理がやや複雑です。
具体的な説明や実装方法は詳しい記事がありますので、こちらに譲ります。
Flutterで多言語化対応する(Dart intl toolsの使い方)

簡単にまとめると、

  1. Dart intl toolsをpubspec.yamlに追加
  2. 文字列管理クラス(ここではstrings.dart)を用意
  3. 翻訳する文字列をdartファイルに記述
  4. 翻訳ファイルであるarbをコマンドで出力
  5. 他の言語のarbを作成
  6. arbファイルからdartファイルをコマンドで生成

となります。
また、文言の追加や修正となった場合、[3]の手順から処理を繰り返す必要がある上に、翻訳ファイルの一覧性もなく、管理しづらいという難点がありました。

Spread Sheetで翻訳データを管理する

そこで、翻訳データをスプレッドシートで管理し、FlutterプロジェクトへはGoogle App Scriptを使って必要なファイルを自動出力するようにしてみました。
Flutter多言語翻訳管理シート
Flutter多言語翻訳管理シートのスクリプト
GASのコードはこちらにも置いてあります
https://github.com/shcahill/FlutterStringResourceScript

ちなみに参考にした大元のスクリプトはこちらです。
Android iOS Drive Export

Flutterプロジェクト側の準備

intlパッケージの追加

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  # 多言語対応
  flutter_localizations:
    sdk: flutter
  intl:
  intl_translation: ^0.17.5
  ...

strings.dartの追加

l10フォルダを作り、そこに以下の様なstrings.dartを用意します。

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.dartStringsImplなどが解決しませんが、必要なファイルはGASによる自動生成と、コマンドでの生成になるので、今はスルーしておいてOKです。
ちなみに、以下のようなdartファイルがGASで出力されます。

strings_impl.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()を実行すると以下の画面になります。
スクリーンショット 2019-07-17 13.40.03.png
許可を確認を選択
スクリーンショット 2019-07-17 13.40.13.png
アカウントを選択すると、怪しげな画面が表示されますが、ダイアログ下部の詳細をクリックして内容を表示します。
スクリーンショット 2019-07-17 13.40.51.png
menu(安全ではないページ)に移動をクリックします
(表示されない場合は英語表示だと該当の項目が表示されるかもしれません)
スクリーンショット 2019-07-17 13.41.11.png
許可をクリック
スクリーンショット 2019-07-17 13.41.29.png
Spread Sheetのメニューにエクスポートメニューが追加されています。

多言語翻訳ファイルの出力

追加されたエクスポートメニューを実行してしばらく待つと、以下の様になります。
(スクショはAllを選択した場合です。実行時間はちょっと長いです)
スクリーンショット 2019-08-04 22.48.53.png

Flutter/Android/iOSそれぞれzipで落とせる様になっています。こちらをDLして展開したら、そのままプロジェクトにコピペできるような構成になっています。

スクリーンショット 2019-08-04 22.52.22.png

コマンドから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列のこと)が登録されていてもエラー検出できない
33
28
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
33
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?