[Flutter] ライトな多言語対応

「flutter 多言語対応」で検索するとかなりの記事が出てくると思います。

一番多いのはFlutterSDK標準機能のflutter_localizationsを使ったもので、次にfast_i18nだったりeasy_localizationだったりで実現したものです。

しかしflutter_localizationsは事前準備が多くメッセージを表示するためにcontextが必要だったり、他のパッケージもツールによるビルドが必要だったりと面倒なことが多かった印象です。

そこで私としては雑ではあるものの簡単に多言語対応できるよう以下の構成で実装をしてみました。

  • 言語ごとのメッセージ定義はYAMLのリソースファイルで定義
  • デフォルトは端末の言語コードを参照して表示
  • メッセージ呼び出しはMessage.translate(‘Here is the message’)という感じでcontext不要

上記の対応方針でアプリを起動させたのが下記の画像で、端末の言語を「日本語」に設定したのが左、言語を「英語」にしたのが右です。

ざっくりとですが、対応した内容をご紹介していきます。

メッセージファイル

---
ja:
  Add novel: 小説追加
  All: 全て
  Novel category: 小説カテゴリー
  Novel list: 小説一覧
fr:
  Add novel: Ajout de roman
  All: Tout
  Novel category: Catégories de romans
  Novel list: Liste des romans
  • 上記の例は日本語(ja)とフランス語(fr)です
  • 英語のスペルに対してそれぞれの言語のメッセージを定義しています
    • Dart側で「Add novel」と書けば、端末の言語コードが日本語なら「小説追加」、フランス語なら「Ajout de roman」、言語コードが英語かメッセージファイルにないものであれば「Add novel」が表示されるようにしました

言語コード取得

import 'dart:io';

// 英語端末ならen_US、日本語端末ならja_JPという文字列が取得できる
String languageCode = '';
String countryCode = '';

List<String> localeSet = Platform.localeName.split('_');
if (localeSet.length == 2) {
  languageCode = localeSet[0];
  countryCode = localeSet[1];
}

// MaterialAppにサポートロケールを設定
return MaterialApp(
  :
  supportedLocales: [const Locale('en'), Locale(languageCode)],
);
  • Platform.localeNameで「ja_JP」のようなロケール文字列が取得できます
    • この先頭2文字を言語コードとして利用し、メッセージファイルで定義した言語セットを取得します
  • 標準やパッケージのウィジェットにも同じ言語で表示させる場合、MateriapAppのsupportedLocalesに言語コードを指定することで実現可能です

メッセージ取得

定義したメッセージファイルを読み込みます。

pubspec.yamlでyamlが追加されていることが前提です。

import 'package:yaml/yaml.dart';
YamlMap messages = loadYaml(await rootBundle.loadString('assets/config/messages.yaml'));

下記のようなメソッドを定義しておき、keyに「Add novel」を渡して呼び出すことで「小説追加」の文字列を得られます。

static String translate(String key) {
  String message = '';
  if (messages.containsKey(languageCode)) {
    if (messages[languageCode].containsKey(key)) {
      message = messages[languageCode][key];
    } else {
      message = key;
    }
  } else {
    message = key;
  }
  return message;
}
  • コード中では省略していますが、messages、languageCodeは共通アクセスできる場所で保持されている変数の前提です
    • staticなクラスを用意しそこでmessagesやlanguageCode、上記のメソッドを持たせる等

使い方

下記は冒頭の画像にあったフローティングアクションボタンに「小説追加」を表示させている例です。

:
floatingActionButton: FloatingActionButton(
  label: Text(translate('Add novel')),
),
:
  • contextも不要なメソッド呼び出しだけなのですっきり見られると思います
  • メッセージファイルに定義がない場合はそのまま「Add novel」が表示されるので定義忘れも発見できます

さいごに

以上がライトな多言語対応でした。

設定画面で言語を切り替えるアプリを作る場合でも、共通クラスで保持しているlanguageCodeを変えて画面を再表示するだけで済みます。

うまくいかないシーンもあると思いますが、そこまで手間のかからない・全て自分で管理できる多言語対応の案の一つとして見てもらえたらと思います。