💬

ChatGPTとslangでFlutter Webを多言語化する

2023/04/06に公開2

Flutter大学のLPの英語版を作りたいなと思っていて、こういうのは自分で頑張らずにGPTと一緒にやったほうがいいんだろうなと考え、実際にやってみた記録です。

やったこと

  • flutteruniv.com のLPだけ多言語化(日本語と英語をボタンで切り替え可能)

https://flutteruniv.com/

方法

  • FlutterWebの多言語化には https://pub.dev/packages/slang を使う
  • 英訳はGPT Plusに課金してgpt-3.5-turboにやってもらう
  • GPTくんには英語と日本語のjson作成までやってもらう

slangの実装でやったこと

まずは、多言語化のpackageを比較しました。

https://zenn.dev/flutteruniv_dev/articles/20220422-140216-flutter-localizations

我らがFlutter大学の大エース、すささんが比較記事をすでに書いてくれていたので、その中で一番推しだったfast_i18nを使うことにしました。

しかし、今fast_i18nはslangというpackageが開発を引き継いでいるそうです。
ということでslangを使うことにしました。

https://pub.dev/packages/slang

Flutter大学のFlutter Webはsalon_app_webとsalon_app_commonsという2つのrepositoryに分かれていて、共通項目はsalon_app_commonsに入れてgit submoduleで引っ張る様にしています。

https://zenn.dev/flutteruniv_dev/articles/084e7c85c14f79

今回は、今後のiOS/Androidや管理画面の多言語化も見据えて、salon_app_commonsでslangを管理することにしました。

pubspecに追加

下記のようにslang, slang_flutter, そしてbuild_runnerとslang_build_runnerも追加します。後者の2つに関しては、GitHub Action等のCIで動かしたりしない場合は要らないかもしれません。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
+ slang: ^3.15.0
+ slang_flutter: ^3.15.0

dev_dependencies:
  flutter_test:
    sdk: flutter
+ build_runner: ^2.3.3
+ slang_build_runner: ^3.15.0

JSON作成

JSONの作成にあたっては、公式ドキュメントのほかに以下の記事も参考にしました。

https://zenn.dev/airrnot1106/articles/c04a337d7cf463

まず、i18nというフォルダの中に日本語ファイルと英語ファイルを作ります。

僕の場合は前述の通り、salon_app_commonsの方のpubspec.yamlにpackageを追加したので以下の様に salon_app_commons/lib/i18ntranslations.i18n.jsontranslations_ja.i18n.jsonを作りましたが、普通は lib/i18n に作れば良いです。

├── pubspec.yaml
├── lib
│   ├── main.dart
├── salon_app_commons
│   ├── pubspec.yaml
│   ├── lib
│   │   ├── salon_app_commons.dart
│   │   ├── i18n
│   │   │   ├── strings.g.dart // この後生成される
│   │   │   ├── translations.i18n.json
│   │   │   └── translations_ja.i18n.json
└── web
    ├── index.html

また、translations.i18n.json と言う名前にしようが hogehoge.i18n.jsonにしようが、特に設定しなければ、このあと生成されるファイルは strings.g.dart になるようです。だからなんでもいいと思います。

JSONの中身は例えば、下記の様につくります。

translations_ja.i18n.json
{
  "why": {
    "why_flutter": "なぜFlutterなのか?"
  }
}
translations.i18n.json
{
  "why": {
    "why_flutter": "Why Flutter?"
  }
}

一旦1つだけ翻訳を入れてみましたが、この後、ここに追加するJSONをGPT君に作ってもらいたいと思います。

ビルド

GPT君に行く前に、一旦仕組みとして多言語化を成立させておきましょう。

dartとして文字ファイルに安全にアクセスするために、strings.g.dart のファイルを生成してもらうコマンドを叩きます。

開発中は以下で良いと思います。

flutter pub run slang

CIにやらせたりする時は、slungにアクセスできないので、 slang_build_runnerを入れて下記を行います。

flutter pub run build_runner build -d

うまくいくと、下記のように Translations generated successfully. と出ます。

Scanning translations...

 (base) en -> /Users/fujikawakei/app/salon_app_web/salon_app_commons/lib/i18n/translations.i18n.json
        ja -> /Users/fujikawakei/app/salon_app_web/salon_app_commons/lib/i18n/translations_ja.i18n.json

Output: /Users/fujikawakei/app/salon_app_web/salon_app_commons/lib/i18n/strings.g.dart
Translations generated successfully. (0.086 seconds)

そして、strings.g.dartをimportすれば、以下のように文字にアクセスできる様になります。

final why = t.why.why_flutter;

言語設定

今回私は、日本語がデフォルトで、英語に切り替えられるという実装を行いました。iOS/Androidであれば端末設定を取得したりできるはずです。webでどこまで取れるかは調べてないので分かり次第追記したいと思います。(近い未来にFlutter大学は国際化したいと思っているので、その際はユーザーの言語設定を取りたいです)

以下の様にsetLocaleしてから、アプリを立ち上げます。

main.dart
import 'package:flutter/material.dart';
import 'presentation/app/app.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 言語設定、今のところはデフォルト日本語
  LocaleSettings.setLocale(AppLocale.ja);

  runApp(TranslationProvider(child: const App()));
}
app.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter大学',
      locale: TranslationProvider.of(context).flutterLocale,
      supportedLocales: AppLocaleUtils.supportedLocales,
      localizationsDelegates: GlobalMaterialLocalizations.delegates,
    );
  }
}

MaterialAppはTranslationProviderで囲むことが推奨されてます。
使うために、flutter_localizationsも入れておきましょう。

https://pub.dev/packages/flutter_localization

日本語と英語の切り替え

切り替えは、LocaleSettingWidgetっていうTextButtonを並べたものをつくってみました。

LocaleSettings.setLocale(AppLocale.ja); という感じでセットすると、リアルタイムで t.why.why_flutter としている箇所は置き換わります。これは全体をTranslationProviderで囲んでいるためです。

import 'package:flutter/material.dart';

class LocaleSettingWidget extends StatelessWidget {
  const LocaleSettingWidget({
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Row(
      children: [
        TextButton(
          child: Text('日本語'),
          onPressed: () {
            LocaleSettings.setLocale(AppLocale.ja);
          },
        ),
        TextButton(
          child: Text('English'),
          onPressed: () {
            LocaleSettings.setLocale(AppLocale.en);
          },
        ),
      ],
    );
  }
}

ボタンを押したらこんな感じで切り替わります。

以上で、多言語のための仕組みは終了です。

arrayのJSONがある時には、interfaceを作ると管理しやすいという知見を得たので別記事もシェアしときます。

https://zenn.dev/flutteruniv_dev/articles/6be509f86c0fd7

GPTくんに頼んだこと

さあ、ここからはJSONに日本語と英語を追加していき、FlutterWebではそれを参照するという仕組みを作り続けるだけです。こういった作業の時にGPTを使うべきだと感じ、初めて本格的にGPTを使ってみました。

ChatGPT Plusに課金しているので、GPT-4も使えるのですが、出力のスピードが遅いので、今回は3.5-turboで行いました。

方針

  • 多言語化したい部分のDartのコードを投げて、そこで使うためのJSONを日本語版と英語版それぞれ作ってもらう

以下の部分を例にプロンプトと出力の例を紹介します。

投げたプロンプト

You are a great software Engineer.

I would like to make https://flutteruniv.com to English.
Then, I will use https://pub.dev/packages/slang
So, could you make json file to use this package?
Please make Japanese one and English one.
Note that use key-value style instead of array because array is not appropriate for this package.

Just follow the below code.

children: [
                    const JoinStepWidget(
                      numberText: '01',
                      title: 'メールアドレスと\nパスワードを入力',
                    ),
                    
                    const JoinStepWidget(
                      numberText: '02',
                      title: 'メールアドレス認証',
                    ),
                    
                    const JoinStepWidget(
                      numberText: '03',
                      title: 'プランを選択して決済',
                    ),

                    const JoinStepWidget(
                      numberText: '04',
                      title: 'Slackにて1on1のご案内',
                    ),

ChatGPTからの出力

Sure, here's the JSON file for both Japanese and English using the key-value style:

Japanese:

{
  "joinSteps": {
    "step01": {
      "numberText": "01",
      "title": "メールアドレスと\nパスワードを入力"
    },
    "step02": {
      "numberText": "02",
      "title": "メールアドレス認証"
    },
    "step03": {
      "numberText": "03",
      "title": "プランを選択して決済"
    },
    "step04": {
      "numberText": "04",
      "title": "Slackにて1on1のご案内"
    }
  }
}

English:

{
  "joinSteps": {
    "step01": {
      "numberText": "01",
      "title": "Enter email address\nand password"
    },
    "step02": {
      "numberText": "02",
      "title": "Email address verification"
    },
    "step03": {
      "numberText": "03",
      "title": "Select plan and make payment"
    },
    "step04": {
      "numberText": "04",
      "title": "Guidance for 1on1 on Slack"
    }
  }
}

Note that you can modify the keys and values according to your preference.

人間の仕事

上記のJSONをそれぞれ以下の2つのjsonに貼り付けます。

  • translations.i18n.json
  • translations_ja.i18n.json

gファイルに反映させるために以下を叩きます。

flutter pub run slang

生成されたら、titleをこちらに置き換えます。

                    JoinStepWidget(
                      numberText: '01',
                      title: t.join.joinSteps.step01.title,
                    ),
                    const LineForMobile(),
                    JoinStepWidget(
                      numberText: '02',
                      title: t.join.joinSteps.step02.title,
                    ),
                    const LineForMobile(),
                    JoinStepWidget(
                      numberText: '03',
                      title: t.join.joinSteps.step03.title,
                    ),
                    const LineForMobile(),
                    JoinStepWidget(
                      numberText: '04',
                      title: t.join.joinSteps.step04.title,
                    ),

結果

ボタン1つで下記のように切り替えられる様になりました。(英語は少し修正したりしてます

日本語 英語

以上です。この要領で項目ごとに翻訳をかけてJSONを作ってもらい、LPすべてに反映できました。

実際にリリースされてますので気になる方は下記ページをチェックしてみてください。

https://flutteruniv.com/

また、コードに関してはFlutter大学メンバーであれば見れますので、似たような実装をしたいかたは、下記リポジトリをFlutter大学に入った上でご確認ください。

https://github.com/flutteruniv/salon_app_web

感想

ひとりでがんばったり、英語ができる友人に頼んだりするよりは、GPTと一緒にやった方が格段に早く達成できたなと思います。

5年前にGraffityというスタートアップにいた時に、アプリの英語対応をしたくて、英語ネイティブのメンバーに英訳したリストを作ってもらったことがありましたが、まさにそんなことをGPTはやってくれて、さらにJSON形式で出力するという雑用までやってくれました。

英語のアウトプットとしても、TOEIC Speaking Test 130点/200点(中途半端w) の僕としては、申し分ない英語だと感じています。

以上です。

Flutter大学

Discussion

kira_sogakira_soga

flutter_localizationsの導入方法について

以下サイトは参考にしないこと

https://pub.dev/packages/flutter_localization/install

正しいと思われる導入方法

dependencies:
  flutter_localizations:
    sdk: flutter
  intl: ^0.18.0

理由は分かりませんが、これじゃないとできなかったです、、、

kboykboy

情報ありがとうございます!
意識してなかったですが、僕もそうなってました! (違う時に入れたのかも