Flutterで多言語化したいなと思い、いろいろ調べてみたときの備忘録(*´ω`*)
よくみるのはこんなコードだけど、
Locale locale = Localizations.localeOf(context);
日本語と英語のみをサポートしているときに、
- 簡体中文(デフォルト)
- 日本語
- English
みたいな感じの並びで、デフォルトが中国語の場合だと、
英語で表示してほしいけど、日本語が採用されてしまう。。
Localizations版の動き
いろいろ試してみるとこんな感じっぽい
Localizations.localeOf()
はアプリでの表示言語を取得MaterialApp
のlocale
で設定したものが最優先- システムの言語設定にある一覧の上から順番に試し、サポートされてる言語が採用
- 一致する言語がない場合、サポート言語の1つ目が採用
MaterialApp.router( // ... locale: Locale("en", "US"), // ... ),
- localeOf method - Localizations class - widgets library - Dart API
- locale property - MaterialApp class - material library - Dart API
なので、
- 簡体中文(デフォルト)
- 日本語
- English
の場合は、日本語が採用され、
- 簡体中文(デフォルト)
- English
- 日本語
の場合は、英語が採用され、
- 簡体中文(デフォルト)
の場合は、サポート言語の並び順が、
英語→日本語の場合は、英語が採用される
デフォルトのLocale解決アルゴリズムは、
このあたりに書かれているっぽい。
- basicLocaleListResolution function - widgets library - Dart API
- supportedLocales property - MaterialApp class - material library - Dart API
Locale解決アルゴリズムのカスタマイズ
このアルゴリズムは、localeListResolutionCallback
で
カスタマイズできるっぽい
たとえば、以下の並びで、
- 簡体中文(デフォルト)
- 日本語
- English
サポートしてない言語のときには、
必ず英語で表示してほしい場合は、こんな感じ
MaterialApp.router( // ... localeListResolutionCallback: (locales, supportedLocales) { // デバイスで設定されているロケールの取得 final currentLocale = PlatformDispatcher.instance.locale; debugPrint('current=$currentLocale device=$locales supported=$supportedLocales'); // 取得できない場合は英語を返す if (locales == null) return const Locale('en', 'US'); for (var locale in supportedLocales) { // サポートされてる言語の場合は、その言語を返す if (currentLocale.languageCode == locale.languageCode) { return locale; } } // それ以外は、英語を返す return const Locale('en', 'US'); }, // ... ),
PlatformDispatcher版
上記で出てくるPlatformDispatcher.instance.locale
で、
デバイスに設定されてる言語情報を取得できるっぽい
// デフォルトの言語 Locale locale = PlatformDispatcher.instance.locale; // 言語の一覧 List<Locale> locales = PlatformDispatcher.instance.locales;
BuildContextを使わないので、便利っぽいけど、
非サポート言語のfallbackはないので、Localizations.localeOf()
のほうが安全。
ただ、runApp前にも使えるので、
riverpodを使った言語切替機能などのときは便利。
@Riverpod(keepAlive: true) Locale currentLocale(CurrentLocaleRef ref) { throw UnimplementedError(); } FutureOr<void> main() async { WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); // 端末の言語を取得する final currentLocale = PlatformDispatcher.instance.locale; // TODO: フォールバック処理は適宜 runApp(ProviderScope( overrides: [ // 取得したcurrentLocaleで上書き currentLocaleProvider.overrideWithValue(currentLocale), ], child: const App(), )); }; class App extends HookConsumerWidget { const App({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // Providerから現在の言語を取得 final locale = ref.watch(currentLocaleProvider); return MaterialApp.router( // ... // 現在の言語を設定 locale: locale, // ... ); } }
以上!! なんとなくわかった気がする(*´ω`*)