ChatGPTとslangでFlutter Webを多言語化する
Flutter大学のLPの英語版を作りたいなと思っていて、こういうのは自分で頑張らずにGPTと一緒にやったほうがいいんだろうなと考え、実際にやってみた記録です。
やったこと
- flutteruniv.com のLPだけ多言語化(日本語と英語をボタンで切り替え可能)
方法
- FlutterWebの多言語化には https://pub.dev/packages/slang を使う
- 英訳はGPT Plusに課金してgpt-3.5-turboにやってもらう
- GPTくんには英語と日本語のjson作成までやってもらう
slangの実装でやったこと
まずは、多言語化のpackageを比較しました。
我らがFlutter大学の大エース、すささんが比較記事をすでに書いてくれていたので、その中で一番推しだったfast_i18nを使うことにしました。
しかし、今fast_i18nはslangというpackageが開発を引き継いでいるそうです。
ということでslangを使うことにしました。
Flutter大学のFlutter Webはsalon_app_webとsalon_app_commonsという2つのrepositoryに分かれていて、共通項目はsalon_app_commonsに入れてgit submoduleで引っ張る様にしています。
今回は、今後のiOS/Androidや管理画面の多言語化も見据えて、salon_app_commonsでslangを管理することにしました。
pubspecに追加
下記のようにslang, slang_flutter, そしてbuild_runnerとslang_build_runnerも追加します。後者の2つに関しては、GitHub Action等のCIで動かしたりしない場合は要らないかもしれません。
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の作成にあたっては、公式ドキュメントのほかに以下の記事も参考にしました。
まず、i18nというフォルダの中に日本語ファイルと英語ファイルを作ります。
僕の場合は前述の通り、salon_app_commonsの方のpubspec.yamlにpackageを追加したので以下の様に salon_app_commons/lib/i18n
に translations.i18n.json
と translations_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の中身は例えば、下記の様につくります。
{
"why": {
"why_flutter": "なぜFlutterなのか?"
}
}
{
"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;
言語設定
LocaleSettings.useDeviceLocale();
というコードで端末設定を取得できます。もしブラウザが英語なら、これで最初から英語になります。
import 'package:flutter/material.dart';
import 'presentation/app/app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
LocaleSettings.useDeviceLocale();
runApp(TranslationProvider(child: const App()));
}
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も入れておきましょう。
日本語と英語の切り替え
切り替えは、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を作ると管理しやすいという知見を得たので別記事もシェアしときます。
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すべてに反映できました。
実際にリリースされてますので気になる方は下記ページをチェックしてみてください。
また、コードに関してはFlutter大学メンバーであれば見れますので、似たような実装をしたいかたは、下記リポジトリをFlutter大学に入った上でご確認ください。
感想
ひとりでがんばったり、英語ができる友人に頼んだりするよりは、GPTと一緒にやった方が格段に早く達成できたなと思います。
5年前にGraffityというスタートアップにいた時に、アプリの英語対応をしたくて、英語ネイティブのメンバーに英訳したリストを作ってもらったことがありましたが、まさにそんなことをGPTはやってくれて、さらにJSON形式で出力するという雑用までやってくれました。
英語のアウトプットとしても、TOEIC Speaking Test 130点/200点(中途半端w) の僕としては、申し分ない英語だと感じています。
以上です。
Discussion
flutter_localizationsの導入方法について
以下サイトは参考にしないこと
正しいと思われる導入方法
理由は分かりませんが、これじゃないとできなかったです、、、
情報ありがとうございます!
意識してなかったですが、僕もそうなってました! (違う時に入れたのかも