1
1

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 3 years have passed since last update.

[Flutter] flutter_i18nによる多言語化対応

Last updated at Posted at 2021-09-19

概要

Flutterでの標準的な多言語化対応はflutter_localizationsを使用するものです。
しかし、flutter_localizationsはOSデフォルトの言語を扱う分にはよいのですが、アプリ内で言語の切り替えを行うのには少し微妙というか、アプリ内で言語を切り替えた後の再描画(画面の更新)がやりづらく感じました。
⇛追記: そこまでやりづらくもなかった。flutter_localizationsによる多言語化対応(アプリ内で言語切り替え)

調べたところ、flutter_i18nというパッケージでは再描画の仕組みが元々用意されているようだったので、これを使ってみることにします。

await FlutterI18n.refresh(context, newLocale);

これだけで対応するロケールでの再描画が行えます。

スクリーンショット 2021-10-04 18.14.03.png

なお、flutter_i18nは現在のバージョンが0.30.0ということもあり、まだ枯れていない部分もあると思われるのでそこは注意してください。

使いかた

大まかな手順は

  • 翻訳リソースの作成
  • pubspec.yamlの修正
  • MaterialAppにlocalizationsDelegatesを設定
  • 翻訳部分の修正
  • 言語切替時に再描画

という感じになります。

翻訳リソースの作成

翻訳リソースをウェブから取ったりもできるようですが、ここでは単にローカルのファイルから取ります。
プロジェクト配下に以下のようにファイルを作成します。

assets/flutter_i18n/ja.json
assets/flutter_i18n/en.json

ディレクトリ名はこれがデフォルトですが "i18n" や "locales" などでもよいようです。
ファイル形式はJSON、YAML、TOML、XMLから選べるようですが、ここではJSONにします。
ファイル名にも国コードを含むかなど命名規則がありますが、ここでは単純に言語コードだけ設定しています。

翻訳ファイルの詳しい書き方についてはドキュメントに記載がなかったのですが、少なくともJSONで次のような書き方はできるようです。

ja.json
{
  "hello": "こんにちは",
  "helloName": "こんにちは{name}さん"
}

上記の設定に対して、次のような呼び出しができます。

I18nText("hello")
I18nText("helloName", translationParams: {"name": yourName})

複数形(Plural)の設定などもっと複雑なこともできるようです。

pubspec.yamlの修正

pubspec.yamlに、依存性とアセットを追加します。

pubspec.yaml
dependencies:
  flutter_i18n: ^0.30.0

flutter:
  assets:
    - assets/flutter_i18n/en.json
    - assets/flutter_i18n/ja.json

修正後は

flutter pub get

を忘れず行ってください。(IDEが勝手にやってくれるとは思いますが。)

MaterialAppにlocalizationsDelegatesを設定

実際にはflutter_localizationsの場合と同様にlocaleResolutionCallbacksupportedLocalesの設定も必要になると思いますが、一旦最小構成のみ示します。

main.dart
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_i18n/flutter_i18n.dart';

:

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        FlutterI18nDelegate(
          // 国コードも使用する場合は useCountryCode: true を指定
          translationLoader: FileTranslationLoader(),
          // ここはサンプルそのまま
          missingTranslationHandler: (key, locale) {
            print("--- Missing Key: $key, languageCode: ${locale?.languageCode}");
          },
        ),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate
      ],
      // RTLサポートが必要な場合(RTLとは?Right-to-Leftの話?)
      // builder: FlutterI18n.rootAppBuilder(),

      :

    );
  }

翻訳部分の修正

翻訳対象のTextを以下のように置き換えていきます。

I18nText("hello")
I18nText("helloName", translationParams: {"name": yourName})

TextでなくStringが必要な場合は次のようにして取れます。

FlutterI18n.translate(context, "hello")

言語切替時に再描画

最後に、本題の再描画です。

// 英語に切り替えた際のイベント
onPressed: () async {
  await FlutterI18n.refresh(context, Locale("en", ""));
  setState(() {});
}

実際には切り替えた言語を設定に保存する処理などもあると思いますが、基本的にはこれだけです。

サンプルアプリ

参考にサンプルアプリの全体を貼っておきます。

ja.json
{
  "languageSettings": "言語設定",
  "hello": "こんにちは"
}
en.json
{
  "languageSettings": "Language Settings",
  "hello": "Hello"
}
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_i18n/flutter_i18n.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        FlutterI18nDelegate(
          translationLoader: FileTranslationLoader(),
          missingTranslationHandler: (key, locale) {
            print("--- Missing Key: $key, languageCode: ${locale?.languageCode}");
          },
        ),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate
      ],
      title: 'i18n sample',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HelloPage(),
    );
  }
}

class HelloPage extends StatefulWidget {
  const HelloPage({Key? key}) : super(key: key);

  @override
  _HelloPageState createState() => _HelloPageState();
}

class _HelloPageState extends State<HelloPage> {

  SimpleDialogOption _changeLocaleDialogOption(
      BuildContext context,
      String text,
      String languageCode) {
    return SimpleDialogOption(
      child: Text(text),
      onPressed: () async {
        await FlutterI18n.refresh(context, Locale(languageCode, ''));
        setState(() {});
        Navigator.pop(context);
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title : I18nText('hello')
      ),
      body: Center(
        child: TextButton(
          child: I18nText('languageSettings'),
          onPressed: () => showDialog(
            context: context,
            builder: (context) {
              return SimpleDialog(
                title: I18nText('languageSettings'),
                children: <Widget>[
                  _changeLocaleDialogOption(context, 'English', 'en'),
                  _changeLocaleDialogOption(context, '日本語', 'ja'),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}
1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?