Flutter-国际化-Intl

1,186 阅读3分钟

使用Intl包不仅可以非常轻松的实现国际化,而且可以将字符串文本分离成单独的文件,方便开发和翻译分工协作。
添加依赖:

dependencies:
  #...省略无关项
  intl: ^0.18.1 
dev_dependencies:
   #...省略无关项
  intl_generator:  ^0.4.1 

intl_generator包主要包含了一些工具,在开发阶段主要作用是从代码中提取国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码,而intl包主要是引用和加载intl_generator生成后的代码。 需要注意的是,这里的版本如果和安装的插件Intl不一致可能会有冲突,最好和插件保持一致,插件的使用会更加的便捷。

创建必要目录

首先,在项目根目录创建一个I10n-arb目录,该目录保存接下来通过intl_generator命令生成的arb文件。样例如下:

{
  "@@last_modified": "2018-12-10T15:46:20.897228",
  "@@locale":"zh_CH",
  "title": "Flutter应用",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  }
}

根据"@@locale"字段可以看出arb对应的中文件简体的翻译,里面的title字段对应的是应用标题的中文简体翻译。@title字段是对title的一些描述信息。
接下来,在lib目录下创建一个I10n目录,用于保存从arb文件生成的dart代码目录。

实现Localizations和Delegate类

使用intl包的一些方法实现Localizations和Delegate类。

class DemoLocalizations {
  static Future<DemoLocalizations> load(Locale locale) {
    final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);
    //2
    return initializeMessages(localeName).then((b) {
      Intl.defaultLocale = localeName;
      return DemoLocalizations();
    });
  }

  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }

  String get title {
    return Intl.message(
      'Flutter APP',
      name: 'title',
      desc: 'Title for the Demo application',
    );
  }
}

//Locale代理类
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();

  //是否支持某个Local
  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);

  // Flutter会调用此类加载相应的Locale资源类
  @override
  Future<DemoLocalizations> load(Locale locale) {
    //3
    return  DemoLocalizations.load(locale);
  }

  // 当Localizations Widget重新build时,是否调用load重新加载Locale资源.
  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;
}
  • 注释1:messages_all.dart文件是通过intl_generator工具从arb文件生成的代码,刚开始并没有,运行命令后会生成,后面会介绍使用插件生成,注释2:initializaMessages函数也是生成的。
  • 注释3:直接使用DemoLocalizations.load()即可。

添加国际化属性

Intl库提供了一些方法,这些方法可以帮助我们轻松实现不同语言的一些语法特性,比如复数语境。
intl_generator工具中来提取代码中的字符串到一个arb文件,运行如下命令:

flutter pub pub run intl_generator:extract_to_arb --output-dir=l10n-arb \ lib/l10n/localization_intl.dart

根据arb生成dart文件:

flutter pub pub run intl_generator:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb

可以将两个命令写进脚本中intl.sh 中,记得给脚本权限

//权限
chmod +x intl.sh
//脚本执行
./intl.sh

上面主要是讲述Intl的原理和一些实现思想,下面介绍,如何使用Intl插件来实现国际化:

  1. 打开Android Studio——》Preference——》Plugins——》Marketplace——》输入Intl,点击Install。
  2. 静待安装,安装好后,看下Installed中是否存在。
  3. 回到编码界面,选择——》Tools——》Flutter Intl——》Ininialize for the Project
  4. 静待界面中出现generated 和 I10n文件夹。
  5. 默认I10n文件中有intl_en.arb,在此文件中编写你想要添加的国家化字段,格式如下:
{
  "@@last_modified": "2018-12-10T17:37:28.505088",
  "title": "Flutter APP",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  },
  //带变量字段
  "remainingEmailsMessage": "{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}",
  "@remainingEmailsMessage": {
    "description": "How many emails remain after archiving.",
    "type": "text",
    "placeholders": {
      "howMany": {

      }
    }
  },
  "Main_message": "Message",//常规字段
  "Main_setting": "Setting",
  "Main_home": "Home"
}

使用:

class SSLTestLocalizationsState extends State<SSLTestLocalizations>{
  @override
  void initState() {
    // TODO: implement initState

    super.initState();

  }
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: const Text("SSL Test Localization")
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                onPressed: (){
                  Locale temLocal = Localizations.localeOf(context);//实际只能切换一次,因为系统语言没有改变,如果想内部随意切换应保存内部语言然后再切换
                  debugPrint("current ${temLocal.languageCode}");
                if (temLocal.languageCode == "zh"){
                  temLocal = const Locale("en");

                }else{
                  temLocal = const Locale("zh");
                  S.load(const Locale("zh"));
                }
                  setState(() {
                    S.load(temLocal);
                  });


                },
                child: Text(S.of(context).Main_message)
            ),
            Text(S.of(context).remainingEmailsMessage(10)),
            Text(S.of(context).remainingEmailsMessage(1)),
            Text(S.of(context).remainingEmailsMessage(2)),
            Text(S.of(context).remainingEmailsMessage(0)),
          ],
        ),
      ),
    );
  }
}

效果:

截屏2023-04-27 17.51.37.png