一. 国际化的认识

国际化的英文称呼:internationalization(简称为i18n)
App国际化开发主要包括:文本国际化(包括文本的顺序),Widget显示的国际化:

  • 某些文本在英文环境下应该显示为英文;
  • 某些Widget在中文环境下,应该显示中文(比如弹出的时间选择器);

    二. 国际化的适配

    2.1. Widget的国际化

    Flutter给我们提供的Widget默认情况下就是支持国际化,没有进行设置,无论在什么环境都是以英文的方式显示的。如果想要添加其他语言,必须指定额外的 MaterialApp 属性并且添加一个单独的 package,叫做 flutter_localizations。

    2.1.1. pubspec添加依赖

    想要使用 flutter_localizations 的话,需要在 pubspec.yaml 文件中添加它作为依赖:
    dependencies:
    flutter:
    sdk: flutter
    flutter_localizations:
    sdk: flutter

2.1.2. 设置MaterialApp

  • 在localizationsDelegates中指定哪些Widget需要进行国际化
    • 用于生产本地化值集合的工厂
    • 这里指定了Material、Widgets、Cupertino都使用国际化
  • supportedLocales指定要支持哪些国际化
    • 这里指定中文和英文(也可以指定国家编码)
      1. MaterialApp(
      2. localizationsDelegates: [
      3. GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值
      4. GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格
      5. GlobalWidgetsLocalizations.delegate // 指定默认的文本排列方向, 由左到右或由右到左
      6. ],
      7. supportedLocales: [
      8. Locale("en"),
      9. Locale("zh")
      10. ],
      11. )
      注意:如果要指定语言代码、文字代码和国家代码,可以进行如下指定方式:
      // Full Chinese support for CN, TW, and HK
  1. supportedLocales: [
  2. const Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
  3. const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'
  4. const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'
  5. const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'), // 'zh_Hans_CN'
  6. const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'), // 'zh_Hant_TW'
  7. const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'), // 'zh_Hant_HK'
  8. ],

2.1.3. 查看Widget结果

但是对于iOS,将语言切换为中文,依然显示是英文的Widget

  • 这是因为iOS定义了一些应用的元数据,其中包括支持的语言环境;
  • 必须将其对应的元数据中支持的语言添加进去;
  • 元数据的设置在iOS项目中对应的info.plist文件中;

修改iOS的info.plist文件配置:

  • 选择 Information Property List 项;
  • 从 Editor 菜单中选择 Add Item,然后从弹出菜单中选择 Localizations;
  • 为array添加一项选择 Add Item,选择Chinese;

配置完成后,卸载之前的app,重新安装:

2.2. 其它文本国际化

App中除了有默认的Widget,也希望对自己的文本进行国际化,如何做到呢?

2.2.1. 创建本地化类

该类用于定义我们需要进行本地化的字符串等信息:

  • 1.需要一个构造器,并且传入一个Locale对象
  • 2.定义一个Map,其中存放我们不同语言对应的显示文本
  • 3.定义它们对应的getter方法,根据语言环境返回不同的结果 ```dart import ‘package:flutter/material.dart’;

class WLLocalizations { final Locale locale;

HYLocalizations(this.locale);

static Map> _localizedValues = { “en”: { “title”: “home”, “greet”: “hello~”, “picktime”: “Pick a Time” }, “zh”: { “title”: “首页”, “greet”: “你好~”, “picktime”: “选择一个时间” } };

String get title { return _localizedValues[locale.languageCode][“title”]; }

String get greet { return _localizedValues[locale.languageCode][“greet”]; }

String get pickTime { return _localizedValues[locale.languageCode][“picktime”]; } }

  1. <a name="OQseE"></a>
  2. #### 2.2.2. 自定义Delegate
  3. 上面的类定义好后,在什么位置或者说如何对它进行初始化呢?
  4. - 可以像Flutter Widget中的国际化方式一样对它们进行初始化;
  5. - 也就是我们也定义一个对象的Delegate类,并且将其传入localizationsDelegates中;
  6. - Delegate的作用就是当Locale发生改变时,调用对应的load方法,重新加载新的Locale资源;
  7. WLLocalizationsDelegate需要继承自LocalizationsDelegate,并且有三个方法必须重写:
  8. - isSupported:用于当前环境的Locale,是否在我们支持的语言范围
  9. - shouldReload:当Localizations Widget重新build时,是否调用load方法重新加载Locale资源
  10. - 一般情况下,Locale资源只应该在Locale切换时加载一次,不需要每次Localizations重新build时都加载一遍;
  11. - 所以一般情况下返回false即可;
  12. - load方法:当Locale发生改变时(语言环境),加载对应的HYLocalizations资源
  13. - 这个方法返回的是一个Future,因为有可能是异步加载的;
  14. - 但是我们这里是直接定义的一个Map,因此可以直接返回一个同步的Future(SynchronousFuture)
  15. ```dart
  16. import 'package:flutter/cupertino.dart';
  17. import 'package:flutter/foundation.dart';
  18. import 'package:i18n_demo/i18n/localizations.dart';
  19. class HYLocalizationsDelegate extends LocalizationsDelegate<HYLocalizations> {
  20. @override
  21. bool isSupported(Locale locale) {
  22. return ["en", "zh"].contains(locale.languageCode);
  23. }
  24. @override
  25. bool shouldReload(LocalizationsDelegate<HYLocalizations> old) {
  26. return false;
  27. }
  28. @override
  29. Future<HYLocalizations> load(Locale locale) {
  30. return SynchronousFuture(HYLocalizations(locale));
  31. }
  32. static HYLocalizationsDelegate delegate = HYLocalizationsDelegate();
  33. }

2.2.3. 使用本地化类

  • 我们可以通过Localizations.of(context, HYLocalizations)获取到HYLocalizations对象

    1. Widget build(BuildContext context) {
    2. return Scaffold(
    3. appBar: AppBar(
    4. title: Text(Localizations.of(context, HYLocalizations).title),
    5. ),
    6. body: Center(
    7. child: Column(
    8. children: <Widget>[
    9. Text(Localizations.of(context, HYLocalizations).greet),
    10. RaisedButton(
    11. child: Text(Localizations.of(context, HYLocalizations).pickTime),
    12. onPressed: () {
    13. showDatePicker(
    14. context: context,
    15. initialDate: DateTime.now(),
    16. firstDate: DateTime(2019),
    17. lastDate: DateTime(2022)
    18. ).then((pickTime) {
    19. });
    20. },
    21. )
    22. ],
    23. ),
    24. ),
    25. );
    26. }

    可以对Localizations.of(context, WLLocalizations)进行一个优化

  • 给HYLocalizations定义一个of的静态方法

    1. class WLLocalizations {
    2. static WLLocalizations of(BuildContext context) {
    3. return Localizations.of(context, WLLocalizations);
    4. }
    5. }

    使用:

    1. appBar: AppBar(
    2. title: Text(WLLocalizations.of(context).title),
    3. )

    2.2.4. 异步加载数据

    假如数据是异步加载的,比如来自Json文件或者服务器,应该如何处理呢?
    修改WLLocalizations的数据加载: ```dart static Map> _localizedValues = {};

    Future loadJson() async { // 1.加载json文件 String jsonString = await rootBundle.loadString(“assets/json/i18n.json”);

    // 2.转成map类型 final Map map = json.decode(jsonString);

    // 3.注意:这里是将Map转成Map>类型 _localizedValues = map.map((key, value) {

    1. return MapEntry(key, value.cast<String, String>());

    }); return true; }

在HYLocalizationsDelegate中使用异步进行加载: @override Future load(Locale locale) async { final localization = HYLocalizations(locale); await localization.loadJson(); return localization; }

  1. <a name="DJhS5"></a>
  2. ## 三. 国际化的工具
  3. <a name="xodHQ"></a>
  4. ### 3.1. 认识arb文件
  5. 目前已经可以通过加载对应的json文件来进行本地化了。有另外一个问题,我们在进行国际化的过程中,下面的代码依然需要根据json文件手动编写:
  6. ```dart
  7. String get title {
  8. return _localizedValues[locale.languageCode]["title"];
  9. }
  10. String get greet {
  11. return _localizedValues[locale.languageCode]["greet"];
  12. }
  13. String get pickTime {
  14. return _localizedValues[locale.languageCode]["picktime"];
  15. }

arb文件可以快速在本地化文件-dart代码文件直接来转换:

  • arb文件全称Application Resource Bundle,表示应用资源包,目前已经得到Google的支持;
  • 其本质就是一个json文件,但是可以根据该文件转成对应的语言环境;

    3.2. intl package

    3.3. 使用IDE插件

    插件:Flutter Intl

  • 该插件更新维护频率很高,并且广受好评;

  • 在Android Studio和VSCode中都是支持的

    3.3.1. 安装插件

    在Android Studio的Plugins中安装插件:

    3.3.2. 初始化intl

    选择工具栏Tools - Flutter Intl - Initialize for the Project
    完成上面的操作之后会自动生成如下文件目录:

  • generated是自动生成的dart代码

  • I10n是对应的arb文件目录

    3.3.3. 使用intl

    在localizationsDelegates中配置生成的class,名字是S

  • 1.添加对应的delegate

  • 2.supportedLocales使用S.delegate.supportedLocales

    1. localizationsDelegates: [
    2. GlobalMaterialLocalizations.delegate,
    3. GlobalWidgetsLocalizations.delegate,
    4. GlobalCupertinoLocalizations.delegate,
    5. HYLocalizationsDelegate.delegate,
    6. S.delegate
    7. ],
    8. supportedLocales: S.delegate.supportedLocales,

    因为目前还没有对应的本地化字符串,所以需要在intl_en.arb文件中编写:

  • 编写后ctrl(command) + s保存即可

{
“title”: “home”,
“greet”: “hello~”,
“picktime”: “Pick a time”
}
使用

  • 按照如下格式:S.of(context).title

    3.3.4. 添加中文

    如果希望添加中文支持:add local

  • 在弹出框中输入zh即可

编写intl_zh.arb文件:
{
“title”: “首页”,
“greet”: “您好~”,
“picktime”: “选择一个时间”
}

3.4. arb其它语法

如果我们希望在使用本地化的过程中传递一些参数:

  • 比如hello kobe或hello james
  • 比如你好啊,李银河 或 你好啊,王小波

修改对应的arb文件:

  • {name}:表示传递的参数

{
“title”: “home”,
“greet”: “hello~”,
“picktime”: “Pick a time”,
“sayHello”: “hello {name}”
}
使用:

  1. Text(S.of(context).sayHello("李银河")),