はじめに
仕事でFlutterにローカリゼーションとWidgetbookの両方を導入することになりました。
それぞれは問題なく導入できて、よっしゃー!と思っていたのですがWidgetbookのAddonに存在するLocaleを変更しても言語が変更されませんでした。調べたところ、WidgetのデフォルトのLocalizationAddon
はFlutterネイティブでLocaleを変更する方式であればうまく動くようですが、今回はslangを使ってるので動いてないっぽい。じゃあ直すか!ということで色々調べ、問題を解決したので共有です。
対象者
- Flutterで開発をしている人
- アプリの多言語化にslangを使用している人
- widgetbookを導入してページやWidgetをカタログ化したい人
前提条件
- ここではwidgetbookやslangの説明はしません
- ローカルにslangとwidgetbookの両方がすでに入ってる
- slangで多言語化を行っている
- widgetbookの設定は以下
// widgetbook.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
// Import the generated directories variable
import 'widgetbook.directories.g.dart';
void main() {
runApp(const WidgetbookApp());
}
@widgetbook.App()
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({super.key});
@override
Widget build(BuildContext context) {
return Widgetbook.material(
// Use the generated directories variable
appBuilder: (context, child) {
return ProviderScope(child: child);
},
directories: directories,
addons: <WidgetbookAddon>[
MaterialThemeAddon(themes: [
WidgetbookTheme(name: "Light", data: ThemeData.light()),
WidgetbookTheme(name: "Dark", data: ThemeData.dark()),
]),
DeviceFrameAddon(devices: [
Devices.ios.iPhoneSE,
Devices.ios.iPhone13,
Devices.ios.iPhone13Mini,
Devices.ios.iPhone13ProMax,
]),
TextScaleAddon(
scales: <double>[1.0, 2.0],
),
LocalizationAddon(
locales: <Locale>[
const Locale('en', 'US'),
],
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
),
],
integrations: const [],
);
}
}
いくつかカスタマイズしてますが、基本的にはデフォルトに近いです。
では本題に入ります。
Localization入ってない?
そう、上記のコードを見るとそもそもLocaleのコードが入ってるんです。
しかしこれは動きません。
上記のローカリゼーションはwidgetbookが用意しているAddonで、これはFlutterのネイティブのローカリゼーション機能を使用しています。slangを使ってる場合、このネイティブのローカリゼーションは行っていないので機能しません。(少なくとも自分は機能させられなかった)
じゃあどうしたら良いのか?
そこで色々試行錯誤し、「自分でAddon作れば良いんじゃね?」という結論になりました。
wdigetbookのAddonは自分でカスタムのものが作れるので、これとslangが用意してる手動で言語を切り替える機能を使って、独自のLocalizationAddon
を作成します。
class CustomLocaleAddon extends WidgetbookAddon<String> {
String initialCustomLocale = "ja";
CustomLocaleAddon()
: super(
name: "Locale",
);
@override
Widget buildUseCase(BuildContext context, Widget child, String setting) {
LocaleSettings.setLocaleRaw(setting);
return child;
}
@override
List<Field<String>> get fields {
return [
ListField<String>(
name: "locale",
initialValue: initialCustomLocale,
values: [
"ja",
"en",
])
];
}
@override
String valueFromQueryGroup(Map<String, String> group) {
return valueOf<String>("locale", group)!;
}
}
基本的なコードはwidgetbookの公式にあるカスタムAddonの項目を参考にしています。
fields
がAddonで選択できる内容を管理しています。
これが選択された時、valueFromQueryGroup
が実行されるのでここで値の変更を行います。
上記の例ではそのまま文字列を返せばいいので特別な処理は入れません。
これが実行されたあと、buildUseCase
が実行され、valueFromQueryGroup
のreturn値がbuildUseCase
のsetting
引数に渡されます。
つまり、この時点でユーザーが選択した言語文字列がsettings
に入ってくるということです。
あとは、slangのLocaleSettings.setLocaleRaw(言語文字列)
を使って、slangの選択されている言語を変更します。
そしたら、これをwidgetbookのAddonに追加します。
// widgetbook.dart
import 'package:comolu_app/i18n/i18n.g.dart';
import 'package:comolu_app/services/firebase.dart';
import 'package:comolu_app/services/get_it.dart';
import 'package:comolu_app/utils/theme.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
// Import the generated directories variable
import 'widgetbook.directories.g.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
LocaleSettings.useDeviceLocale();
// NOTE: 各種コンフィグの設定
await setupFirebase();
await setupGetIt();
runApp(const WidgetbookApp());
}
@widgetbook.App()
class WidgetbookApp extends StatelessWidget {
const WidgetbookApp({super.key});
@override
Widget build(BuildContext context) {
return Widgetbook.material(
// Use the generated directories variable
appBuilder: (context, child) {
return ProviderScope(child: child);
},
directories: directories,
addons: <WidgetbookAddon>[
MaterialThemeAddon(themes: [
WidgetbookTheme(name: "Light", data: defaultTheme),
WidgetbookTheme(name: "Dark", data: defaultTheme),
]),
DeviceFrameAddon(devices: [
Devices.ios.iPhoneSE,
Devices.ios.iPhone13,
Devices.ios.iPhone13Mini,
Devices.ios.iPhone13ProMax,
]),
TextScaleAddon(
scales: <double>[1.0, 2.0],
),
CustomLocaleAddon(), <- 追加
],
integrations: const [],
);
}
}
これで、widgetbook+slangで言語の変更できます。
あとがき
Storybookに慣れてるので最初は設定などに手こずりましたが、やり方がわかってくるとやっぱりページやWidgetのカタログ化は便利ですね。