「flutter_localizations」による多言語対応

## 1. はじめに
Flutterでの多言語対応として、公式が推奨する「flutter_localizations」を導入してみましたので、導入手順をまとめておきます。

※ シンプルに導入手順をまとめた記事です。

### <前提知識>

* Flutterでプロジェクトを作成できること
* Flutterでパッケージを導入できること

### <執筆時の環境>

* macOS Ventura 13.4
* Flutter 3.16.8 • channel stable
* Dart 3.2.5

## 2. そもそも多言語対応って何？
アプリを（言語の異なる）複数の国に配布する場合、アプリに表示される文言はその国の言語で表示された方が嬉しいですよね。
端末の言語設定に応じてアプリに表示される文言が変わるように、アプリをいろんな言語に対応することを **「多言語対応」** と呼んでいます。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3619463/cc32919e-25eb-4652-1873-0c1c4434ebd3.png">

## 3. 導入手順
flutter_localizationsは以下の5つの手順で導入が完了します。

1. 必要なパッケージを追加する
2. 構成ファイル（l10n.yaml）を作成する
3. 各言語ごとのARBファイルを作成する
4. ローカライゼーションファイルを自動生成する
5. 実際に使ってみる

以下、詳細です。

### 3-1. 必要なパッケージを追加する
以下のコマンドを実行して、「flutter_localizations（※）」と「intl」パッケージをプロジェクトに追加します。

※ pub.devに「[flutter_localization](https://pub.dev/packages/flutter_localization)」というパッケージがありますが、別物なのでご注意ください。
```
$ flutter pub add flutter_localizations --sdk=flutter
$ flutter pub add intl
```

コマンドを実行すると、pubspec.yamlに以下の行が追加されます。

```
dependencies:
	..略..

	flutter_localizations:
      sdk: flutter
    intl: ^0.18.1
```

また、pubspec.yamlに以下の行を追加します。

```
# The following section is specific to Flutter packages.
flutter:
  generate: true # この行を追加
```

### 3-2. 構成ファイル（l10n.yaml）を作成する
プロジェクトのルートディレクトリに「l10n.yaml」ファイルを追加します。

内容は以下のようにします。

```
arb-dir: lib/l10n # ARBファイルを格納するディレクトリを指定します。
template-arb-file: app_ja.arb # テンプレートのARBファイルを指定します。デフォルトの言語のARBファイルを設定すると良いと思います。 
output-class: L10n # 手順3-4で自動生成されるクラス名。設定しないとAppLocalizationsになります。
output-localization-file: app_localizations.dart # 手順3-4で自動生成される言語ファイル名。ローカライゼーションを使うときはこのファイルをインポートします。
```

### 3-3. 各言語ごとのARBファイルを作成する
各言語ごとのARBファイルを`${FLUTTER_PROJECT}/lib/l10n`に追加します。
（※「l10n.yaml」で、arb-dirに`lib/l10n`を指定した前提です。）

```app_ja.arb
{
  "@@locale": "ja",
  "appTitle": "アプリタイトル",
  "@appTitle": {
    "description": "appTitleの説明"
  },
  "appText": "アプリテキスト",
  "@appText": {
    "description": "appTextの説明"
  }
}
```

```app_en.arb
{
  "@@locale": "en",
  "appTitle": "Application Title",
  "appText": "Application Text"
}
```

descriptionは「l10n.yaml」でテンプレートに指定したARBファイルにだけあれば良いと思います。
単語の説明ですが、基本的には使わないと思います。

### 3-4. ローカライゼーションファイルを自動生成する
以下のコマンドを実行するとローカライゼーションファイルが自動生成されます。
ファイルは`${FLUTTER_PROJECT}/.dart_tool/flutter_gen/gen_l10n`の下に作られます。

```
$ flutter gen-l10n
```

実際に自動生成されたファイルを見てみると、たしかにファイル名とクラス名が「l10n.yaml」で指定した名前になっています。

![スクリーンショット 2024-01-23 23.35.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3619463/a6470e90-3016-5f40-7978-8ace9e51da85.png)

試しに「l10n.yaml」でファイル名とクラス名を変更して、`flutter gen-l10n`をしてみると、新たにローカライゼーションファイルが生成されました。（変更前のファイルも残っていましたが）

### 3-5. 実際に使ってみる
#### 使うまでの流れ
■ STEP1
L10nを使いたいファイルにローカライゼーションファイルをインポートします。

```
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
```

■ STEP2
MaterialAppに以下を追加します。

```
const MaterialApp(
  title: 'Localizations Sample App',
  localizationsDelegates: L10n.localizationsDelegates, # 追加
  supportedLocales: L10n.supportedLocales, # 追加
);
```

■ STEP3
あとはこんな感じで使います。

```
appBar: AppBar(
  title: Text(L10n.of(context)!.appTitle),
),
```

#### 使用例
冒頭のキャプチャのサンプルでは以下のように使っています。

```main.dart
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: L10n.localizationsDelegates, # 追加する
      supportedLocales: L10n.supportedLocales, # 追加する
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final l10n = L10n.of(context)!; # 使うときはこんな感じ

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.appTitle), # 使う
        centerTitle: true,
        backgroundColor: Theme.of(context).colorScheme.primary,
      ),
      body: Center(
        child: Text(l10n.appText), # 使う
      ),
    );
  }
}

```

## 4. まとめ
思ったよりもシンプルで分かりやすかったです。
Flutterで多言語対応するための方法は他にもありそうなので、いろいろ比較してみてもおもしろいかなと思いました。


## 5. 参考

* [公式ドキュメント](https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization)
* https://zenn.dev/reinf0rce/articles/618938598fc4dd