【Flutter】モノレポでslangを用いる場合の注意点

https://github.com/slang-i18n/slang/issues/145#issuecomment-1652585351


## モノレポ構成とは

例えばフィットネス系のトレーナーアプリ・ユーザーアプリを同時に開発する場合、どのようにアプリを管理しますか？また、どのようにアプリを配布しますか？

- 1つのアプリとして、トレーナーログイン・ユーザーログインのページを用意し、とルートで切り分けていく
- リポジトリを完全に分けて別物として扱う

しかし、上記の方法は以下のようなメリデメを含むと思います。

#### 1つのアプリとして、トレーナーログイン・ユーザーログインのページを用意し、とルートで切り分けていく

| メリット | デメリット |
|:-:|:-:|
| 1つのリポジトリで一括管理ができる<br>ロジックを共通化させやすい | リポジトリ内のメインディレクトリが<br>肥大化していく（Flutterで言うlib） |

#### リポジトリを完全に分けて別物として扱う

| メリット | デメリット |
|:-:|:-:|
| 固有のロジックの切り分けが容易である。<br>全く違うデザインにしても問題ない | 共通ロジック・設定があった場合二重で実装することになる |

そのため、特にデメリットの方が負担に感じるような場合、モノレポ構造のアプリ開発を推奨します。

モノレポ構造って何？と言う方はAltive社の[flutter_app_template](https://github.com/altive/flutter_app_template/tree/main/packages)が参考になります。

リポジトリの一番上の階層に`packages`ディレクトリを容易し、その中に以下のようにそれぞれ管理したいグループ単位で配置します。

![スクリーンショット 2024-12-03 9.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/4fe4742b-030d-77fb-a8e0-d03e93210d90.png)

例えば`flutter_app`から`theme`のものを呼び出したい場合、直接ディレクトリを参照するのではなくpackageとして取り込んで利用します。

このモノレポ構成は以下のメリデメを含みます

| メリット | デメリット |
|:-:|:-:|
| 各パッケージの責務が明確になる。<br>共通要素の実装がスムーズになる | 各種モノレポ用の設定をしなくてはならない |

## 多言語化対応の話

アプリ開発の際にはTextクラスにセットする文字列をハードコーディングしない目的や、複数言語に対応したアプリケーションとする場合localizationパッケージを用いると思います。

### 多言語化パッケージの紹介

多言語化パッケージはいくつかありますが、代表して以下のようなものがあります

#### Flutter Gems

https://fluttergems.dev/localization-internationalization/

#### 代表パッケージ

https://pub.dev/packages/easy_localization

https://pub.dev/packages/translator

https://pub.dev/packages/slang

## slangの紹介

今回はその中でSlangの紹介をします。

基本的な使い方は以下の通りです。

### packageのインストール

```bash
```

### build.yamlの実装

```yaml:build.yaml
targets:
  $default:
    builders:
      slang_build_runner:
        options:
          base_locale: ja
          input_directory: lib/i18n # 多言語ファイルを設置する場所
          input_file_pattern: .yaml # フォーマット形式
          output_directory: lib/gen # 自動生成後の出力ディレクトリ
          timestamp: false # 自動生成の度にファイルに記録される
          statistics: false
```

### yamlの記載

```yaml:strings.i18n.yaml
app:
  title: "APP NAME"
```

### アプリへの設定

`MyApp()`を以下のようにラップしてください

```dart
TranslationProvider(
    child: const MyApp()
),
```

### 使い方

自動生成のため、build_runnerコマンドを走らせると以下のように利用できます。

```dart
context.t.app.title // APP NAME
```


## モノレポ特有の複数パッケージでまたがる場合の問題

ここでタイトルの問題ですが、例えば共通パーツ用パッケージとアプリ本体のパッケージそれぞれでslangを用いる場合に注意が必要です。
slangを用いる際には`TranslationProvider`でラップする必要があるので、別パッケージのslangを用いたい場合もそのクラスの`TranslationProvider`でさらにラップする必要があります。
ただその場合クラスの衝突が発生します。

## 回避方法

以下のissueのようにasを用いるなどして表記を分けてあげれば良いだけです。

https://github.com/slang-i18n/slang/issues/145#issuecomment-1652585351

```Dart
import 'package:package1/i18n/translations.g.dart' as package1;
import 'package:package2/i18n/translations.g.dart' as package2;

void main() {
  runApp(
    package1.TranslationProvider(
      child: package2.TranslationProvider(
        child: const MyApp(),
      ),
    ),
  );
}
```

しかし、以下のような書き方もできます。importの際には`as`だけではなく個別クラスだけを取得する`show`、逆に個別クラスは排除する`hide`があるのでそれを用います。


```dart
import 'package:themes/themes.dart' as theme show TranslationProvider;
import 'package:themes/themes.dart' hide TranslationProvider;

// ...

TranslationProvider(
    child: theme.TranslationProvider(
        child: const MyApp(),
    ),
),
```

結局やっていることはissueと大差ないのですが、せっかくならこう言う書き方もありますよと言う紹介でした。
ちなみにSlangはyaml形式でワードの定義ができる点もおすすめです。