Flutter实现本地化多语言


内置组件本地化

  1. 添加依赖

    Flutter默认组件的本地化只需要添加

    1flutter_localizations:
    2  sdk: flutter

    使用命令行添加就是

    1flutter pub add flutter_localizations --sdk=flutter
  2. 修改入口组件 main.dart

    1import 'package:flutter_localizations/flutter_localizations.dart';
     1return const MaterialApp(
     2  title: 'Localizations Sample App',
     3  localizationsDelegates: [
     4    GlobalMaterialLocalizations.delegate,
     5    GlobalWidgetsLocalizations.delegate,
     6    GlobalCupertinoLocalizations.delegate,
     7  ],
     8  supportedLocales: [
     9    Locale('en'), // English
    10    Locale('zh'), // Chinese
    11  ],
    12  home: MyHomePage(),
    13);

    这样Flutter内置组件所显示的语言就能和系统语言保持一致,如果想要手动修改,只需要添加参数

    1return const MaterialApp(
    2  ...
    3  locale: Locale("zh"),
    4);

    如果想要修改指定的组件,需要使用到 Localizations.override

     1Widget build(BuildContext context) {
     2  return Scaffold(
     3    appBar: AppBar(
     4      title: Text(widget.title),
     5    ),
     6    body: Center(
     7      child: Column(
     8        mainAxisAlignment: MainAxisAlignment.center,
     9        children: <Widget>[
    10          Localizations.override(
    11            context: context,
    12            locale: const Locale('zh'),
    13            child: Builder(
    14              builder: (context) {
    15                return CalendarDatePicker(
    16                  initialDate: DateTime.now(),
    17                  firstDate: DateTime(1900),
    18                  lastDate: DateTime(2100),
    19                  onDateChanged: (value) {},
    20                );
    21              },
    22            ),
    23          ),
    24        ],
    25      ),
    26    ),
    27  );
    28}

自定义本地化

根据官方文档,可以利用 flutter_localozationsintl 来实现。但是如果根据官方文档,那可不是一般的麻烦,而是相当麻烦,操作步骤包括但不限于

  • 修改 pubsepc.yaml

  • 添加 l10n.yaml

  • 添加 .arb 文件

当然,这一切也不能说是很麻烦,毕竟只要第一次改好,后续只用修改 .arb 文件就能自动运行,但根据这个流程生成的多语言本地化,我觉得有几个问题:

  1. 自动生成的 .dart 文件是保存在 {项目目录}/.dart_tool/flutter_gen/gen_l10n 目录下的,这会导致项目默认的代码没有多语言相关,只有一些不知所谓的 .arb 文件,毕竟与多语言相关的代码都是自动生成的

  2. 也是最重要的一点, 我为什么要在默认语言下重复定义一个不能带空格,只允许定义符合 dart方法名称 的关键字以供调用,比如一个简单的字符串

    1Text("Hello World")

    我必须在默认语言定义一个

    1// app_es.arb
    2"helloWorld": "Hello World"

    然后在其它语言定义

    1// app_zh.arb
    2"helloWorld": "你好 世界"

    最后再修改默认的调用

    1Text(AppLocations.of(context).helloWorld)

    我为什么不能直接使用原有的字符串呢,这样就不用再为默认的语言添加额外的翻译,比如

    1Text(AppLocations.of(context).tr("Hello World"))

所以,我仔细研究了一下,大抵不用如此麻烦

自定义本地化(非代码自动生成)

  1. 首先添加 l10n.dart

     1import 'dart:async';
     2
     3import 'package:flutter/foundation.dart';
     4import 'package:flutter/widgets.dart';
     5import 'package:flutter_localizations/flutter_localizations.dart';
     6import 'package:intl/intl.dart' as intl;
     7
     8import 'l10n_en.dart';
     9import 'l10n_zh.dart';
    10
    11abstract class L10n {
    12  L10n(String locale)
    13  : localeName = intl.Intl.canonicalizedLocale(locale.toString());
    14
    15  final String localeName;
    16
    17  static L10n? of(BuildContext context) {
    18    return Localizations.of<L10n>(context, L10n);
    19  }
    20
    21  static const LocalizationsDelegate<L10n> delegate = _L10nDelegate();
    22
    23  static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
    24  <LocalizationsDelegate<dynamic>>[
    25    delegate,
    26    GlobalMaterialLocalizations.delegate,
    27    GlobalCupertinoLocalizations.delegate,
    28    GlobalWidgetsLocalizations.delegate,
    29  ];
    30
    31  static const List<Locale> supportedLocales = <Locale>[
    32    Locale('en'),
    33    Locale('zh')
    34  ];
    35
    36  String tr(String key);
    37}
    38
    39class _L10nDelegate extends LocalizationsDelegate<L10n> {
    40  const _L10nDelegate();
    41
    42  @override
    43  Future<L10n> load(Locale locale) {
    44    return SynchronousFuture<L10n>(lookupL10n(locale));
    45  }
    46
    47  @override
    48  bool isSupported(Locale locale) =>
    49  <String>['en', 'zh'].contains(locale.languageCode);
    50
    51  @override
    52  bool shouldReload(_L10nDelegate old) => false;
    53}
    54
    55L10n lookupL10n(Locale locale) {
    56  // Lookup logic when only language code is specified.
    57  switch (locale.languageCode) {
    58    case 'en':
    59    return L10nEn();
    60    case 'zh':
    61    return L10nZh();
    62  }
    63
    64  throw FlutterError(
    65    'L10n.delegate failed to load unsupported locale "$locale". This is likely '
    66    'an issue with the localizations generation tool. Please file an issue '
    67    'on GitHub with a reproducible sample app and the gen-l10n configuration '
    68    'that was used.');
    69}

    上述代码其实是由根据官方文档自动生成的 .dart 文件转化而来

  2. 添加默认语言的翻译

     1// l10n_en.dart
     2import 'l10n.dart';
     3
     4class L10nEn extends L10n {
     5  L10nEn([String locale = 'en']) : super(locale);
     6
     7  @override
     8  String tr(String key) {
     9    return translations[key] ?? key;
    10  }
    11}
    12
    13const translations = {};

    对的,你没有看错, translations 甚至可以是空的,这样就不用重复定义默认语言的翻译

  3. 添加其它语言的翻译

     1// l10n_zh.dart
     2import 'l10n.dart';
     3
     4class L10nZh extends L10n {
     5  L10nZh([String locale = 'zh']) : super(locale);
     6
     7  @override
     8  String tr(String key) {
     9    return translations[key] ?? key;
    10  }
    11}
    12
    13const translations = {
    14  "Settings": "设置",
    15  "Basic Settings": "基础设置",
    16  "Theme": "主题",
    17  "Language": "语言",
    18  "About": "关于",
    19  "Help": "帮助",
    20};
  4. 修改 main.dart 入口组件

    1import 'app/l10n/l10n.dart';
    2
    3return const MaterialApp(
    4  title: 'Localizations Sample App',
    5  localizationsDelegates: L10n.localizationsDelegates,
    6  supportedLocales: L10n.supportedLocales,
    7  home: MyHomePage(),
    8);

    这样就能使用自定义的翻译了

本地化多语言的使用

最常用的是在 Text 组件里

1Text(L10n.of(context)!.tr("Hello World"))

但是所有的字符都要添加 L10n.of(context)! 未免有些麻烦,所以我增加了自定义扩展

  • BuildContext

    1extension L10nContext on BuildContext {
    2  String tr(String key) {
    3    final t = L10n.of(this);
    4    if (t == null) {
    5      return key;
    6    }
    7    return t.tr(key);
    8  }
    9}

    使用方式

    1Text(context.tr("Hello World"))
  • String

    1extension L10nString on String {
    2  String tr(BuildContext context) {
    3    final t = L10n.of(context);
    4    if (t == null) {
    5      return this;
    6    }
    7    return t.tr(this);
    8  }
    9}

    使用方式

    1Text("Hello World".tr(context))
  • Text

     1extension L10nText on Text {
     2  Text tr(BuildContext context) {
     3    final t = L10n.of(context);
     4    if (t == null || data == null) {
     5      return this;
     6    }
     7    return Text(t.tr(data ?? ''),
     8      key: key,
     9      style: style,
    10      strutStyle: strutStyle,
    11      textAlign: textAlign,
    12      textDirection: textDirection,
    13      locale: locale,
    14      softWrap: softWrap,
    15      overflow: overflow,
    16      textScaler: textScaler,
    17      maxLines: maxLines,
    18      semanticsLabel: semanticsLabel,
    19      textWidthBasis: textWidthBasis);
    20  }
    21}

    使用方式

    1Text("Hello World").tr(context)

    如此,就能最大限度的较少对原有代码的侵略性修改

优化多语言选择

我这里选用的是 riverpod 进行状态管理,首先定义一个本地语言的状态

1final localeProvider = StateProvider<String>((ref) {
2  return "zh";
3});

接着修改 main.dart

 1class MyApp extends ConsumerWidget {
 2  const MyApp({super.key});
 3
 4  @override
 5  Widget build(BuildContext context, WidgetRef ref) {
 6    final localeCode = ref.watch(localeProvider);
 7
 8    return MaterialApp(
 9      locale: L10n.delegate.isSupported(localeCode) ? Locale(localeCode) : null,
10      ...
11    );
12  }
13}

这样就能很方便地修改应用的显示语言

1ref.read(localeProvider.notifier).state = "zh";

参考文档

作者: honmaple
链接: https://honmaple.me/articles/2024/10/fluttershi-xian-ben-di-hua-duo-yu-yan.html
版权: CC BY-NC-SA 4.0 知识共享署名-非商业性使用-相同方式共享4.0国际许可协议
wechat
alipay

加载评论