美文网首页flutter
flutter国际化处理

flutter国际化处理

作者: FluOrAnd | 来源:发表于2019-09-30 17:31 被阅读0次

    前言

    这里主要是讲解一下Flutter国际化的一些用法,包括Localization widget,intl包和flutter_i18n包的用法

    flutter_localizations使用方式

    ① 加入依赖
    dependencies:
      flutter:
        sdk: flutter
      flutter_localizations:
        sdk: flutter
    
    ② 然后指定MaterialApp的localizationsDelegates,supportedLocales和locale
          locale: _locale,
          localizationsDelegates: [
             // 本地化代理
            ChineseCupertinoLocalizations.delegate,
            KhmerCupertinoLocalizations.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            CustomLocalizations.delegate,
          ],
          supportedLocales: CustomLocalizations.supportedLocales,
    

    本地化代理是处理flutter不提供的语言,或者在ios上失败的问题。代理处理方式见下面附件1。
    GlobalWidgetsLocalizations.delegate定义widget默认的文本方向,
    从左到右或从右到左,这是因为有些语言的阅读习惯并不是从左到右,比如如阿拉伯语就是从右向左的。

    _locale:为用户在app中自主选择后的语言。包过languageCode和countryCode:
    获取当前的语言环境:Locale currentLocale = Localizations.localeOf(context);
    Localizations Widget一般位于Widget树中其它业务组件的顶部,它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化,WidgetsApp将创建一个新的Localizations Widget并重建它,这样子树中通过Localizations.localeOf(context) 获取的Locale就会更新。

    ③ 模块化处理国际化的方式:
    1 新建国际化模块:Localizations

    。。提供用户修改语言的界面
    。。提供获取app当前语言,window.locale.languageCode/countryCode也可以直接获取到手机 当前语言。
    。。提供合并各个子模块的所有语言资源的方法AllLocalizedValues.getAllLocalizedValues.

    2 在main.dart中调用

    该方法将在初始化flutter时进行调用:

    setLocalizedValues(AllLocalizedValues.getAllLocalizedValues(_getModuleList()));
    

    _getModuleList是在初始时注册合并各个模块中的国际化资源文件:

    List<Map<String, Map<String, Map<String, String>>>> moduleList = new List<Map<String, Map<String, Map<String, String>>>>();
        moduleList.add(localizedValues);
    
    3 各个模块的语言资源问题:
    class Ids{
      static const String testPage = 'test_page';
    
      Map<String, Map<String, Map<String, String>>> localizedValues     
      = {
      'en': {
        'US': {
          Ids.testPage: 'TestPage',
        },
      }, 
    'zh': {
        'CN': {
             Ids.testPage: '测试页面',
            }
        }
    }
    }
    

    记得在子模块中抛出:

    export 'src/res/strings.dart' hide Ids;
    

    intl包使用方式

    加入依赖

    dependencies:
      intl: ^0.15.7 
    dev_dependencies:
      intl_translation: ^0.17.2
    

    intl包是用来翻译前面所说的Localization widget相关的dart的文件生成arb文件(arb文件下面会讲解),如前面所说的Localization widget类会改成这样:

    import 'dart:async';
    
    import 'package:intl/intl.dart';
    import 'package:flutter/widgets.dart';
    class DemoLocalizations {
      DemoLocalizations(this._locale);
      final Locale _locale;
      
      static Future<DemoLocalizations> load(Locale locale) async {
        String name =
            locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
        String localeName = Intl.canonicalizedLocale(name);
        return initializeMessages(locale.toString())
            .then((Object _) {
          return new DemoLocalizations(locale);
        });
      }
        
      static DemoLocalizations of(BuildContext context) {
        return Localizations.of(context, DemoLocalizations);
      }
    
      String get title =>
          Intl.message("这个是一个标题", name: "title", desc: "标题用的翻译文本", args: []);
      }    
    }
    

    这里的静态函数load,是为了方便代理类调用新增,里面有一个initializeMessages会显示报错,先可以忽略,上面Localization类中使用了Intl.message,Intl.message的相关用法可以参考官方api(点击查看),这里就是需要翻译的文字,通过intl包会把Intl.message相关的资源"提取"生成arb文件,需要运行命令行如下:

    $ flutter pub pub run intl_translation:extract_to_arb --output-dir="保存arb文件的目录" "需要提取翻译的dart文件"
    
    例子:
    $ flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/demo_localizations.dart
    

    需要在lib中新建l10n文件夹用于保存arb文件,后面带的参数就是上述的DemoLocalizations类所在位置,运行后会在l10n看到一个intl_messages.arb文件。
    把上面的intl_messgaes文件当成模板复制粘贴intl_zh.arb和intl_en.arb,然后翻译相关的内容,arb是一个json结构的文件,intl_zh.arb文件结构如下

    {
      "@@last_modified": "2019-01-10T14:17:00.088434",
      "title": "这个是一个标题",
      "@title": {
        "description": "标题用的翻译文本",
        "type": "text",
        "placeholders": {}
      }
    }
    

    翻译完所有arb资源后,intl_translation包就是用来把arb生成相关的dart代码文件,运行命令行:

    $ flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n \
       --no-use-deferred-loading lib/demo_localizations.dart lib/l10n/intl_*.arb
    

    运行结果会出现

    No @@locale or _locale field found in intl_en, assuming 'en' based on the file name.
    No @@locale or _locale field found in intl_messages, assuming 'messages' based on the file name.
    No @@locale or _locale field found in intl_zh, assuming 'zh' based on the file name.
    

    各自生成如下的新文件
    然后在刚才initializeMessages保存的DemoLocalizations中引入

    import 'l10n/messages_all.dart';
    

    警告就会消失
    然后修改Localization代理中的load,引用上面的Localization widget load方法即可
    调用资源的方法和上面的一样

    DemoLocalizations.of(context).title
    DemoLocalizations.of(context).name
    

    使用flutter_i18n

    flutter_i18n是另外一种进行国际化的方法,有别于intl,它主要是利用json文件来进行翻译,个人认为最简单的一种,可以自己定app语言,方便刷新切换语言,不依赖系统语言

    加入依赖

    dependencies:
      flutter:
        sdk: flutter
      flutter_i18n: ^0.5.2
    flutter:
      assets:
      - flutter_i18n/  
    

    新建json文件,命名规则可以这样{languageCode}_{countryCode}.json或者{languageCode}.json,这里我命名为zh_CN.json,内容如下:

    {
      "test": {
        "title": "这个是一个标题哦",
        "name": "我的名字叫{name}"
      }
    }
    

    添加Localization代理

    localizationsDelegates: [
            FlutterI18nDelegate(useCountryCode, [fallbackFile, basePath]),
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate
    ],
    

    useCountryCode参数是用于json文件的命名规则:
    如果你是用{languageCode}_{countryCode}来命名,useCountryCode参数必须为true
    如果你是用{languageCode}来命名, useCountryCode就必须为false

    fallbackFile参数引入版本0.1.0并提供默认语言,在未提供当前运行系统的转换时使用。这应该包含assets文件夹中有效的json文件的名称如"zh_CN"
    basePath参数可选地用于设置翻译的基本路径。如果未设置此选项,则默认路径为assets / flutter_i18n。此路径必须与pubspec.yaml中定义的路径相同。
    配置例子如:

    FlutterI18nDelegate(true, "en_US", "flutter_i18n")
    
    flutter_i18n实现

    配置完成后,可以通过以下方法调用:

    FlutterI18n.translate(buildContext, "your.key")
    FlutterI18n.translate(buildContext, "test.title");//这是一个标题哦
    FlutterI18n.translate(context, "test.name", {"name": "AAA"});我的名字叫做AAA
    

    如果你想切换应用语言可以这样做:

    await FlutterI18n.refresh(buildContext, languageCode, {countryCode});
    await FlutterI18n.refresh(buildContext, "en", "US");//如切换英语
    

    flutter_i18n 支持复数,你可以调用以下方法:

    FlutterI18n.plural(buildContext, "your.key", pluralValue);如
    "clicked": {
        "times-0": "You clicked zero!",
        "times-1": "You clicked {time} time!",
        "times-2": "You clicked {times} times!"
      }
      
    FlutterI18n.plural(buildContext, "clicked.times", 0)//You clicked zero!
    FlutterI18n.plural(buildContext, "clicked.times", 1)//You clicked 1 time!
    FlutterI18n.plural(buildContext, "clicked.times", 2)//You clicked 2 times!
    

    附件1

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_localizations/flutter_localizations.dart';
    
    class KhmerCupertinoLocalizations implements CupertinoLocalizations {
      final materialDelegate = GlobalMaterialLocalizations.delegate;
      final widgetsDelegate = GlobalWidgetsLocalizations.delegate;
      final local = const Locale('km');
    
      MaterialLocalizations ml;
    
      Future init() async {
        ml = await materialDelegate.load(local);
        debugPrint(ml.pasteButtonLabel);
      }
    
      @override
      String get todayLabel => "Today";
    
      @override
      String get alertDialogLabel => ml.alertDialogLabel;
    
      @override
      String get anteMeridiemAbbreviation => ml.anteMeridiemAbbreviation;
    
      @override
      String get copyButtonLabel => ml.copyButtonLabel;
    
      @override
      String get cutButtonLabel => ml.cutButtonLabel;
    
      @override
      DatePickerDateOrder get datePickerDateOrder => DatePickerDateOrder.mdy;
    
      @override
      DatePickerDateTimeOrder get datePickerDateTimeOrder =>
          DatePickerDateTimeOrder.date_time_dayPeriod;
    
      @override
      String datePickerDayOfMonth(int dayIndex) {
        return dayIndex.toString();
      }
    
      @override
      String datePickerHour(int hour) {
        return hour.toString().padLeft(2, "0");
      }
    
      @override
      String datePickerHourSemanticsLabel(int hour) {
        return "$hour" + "ពេល";
      }
    
      @override
      String datePickerMediumDate(DateTime date) {
        return ml.formatMediumDate(date);
      }
    
      @override
      String datePickerMinute(int minute) {
        return minute.toString().padLeft(2, '0');
      }
    
      @override
      String datePickerMinuteSemanticsLabel(int minute) {
        return "$minute" + "ចែក";
      }
    
      @override
      String datePickerMonth(int monthIndex) {
        return "$monthIndex";
      }
    
      @override
      String datePickerYear(int yearIndex) {
        return yearIndex.toString();
      }
    
      @override
      String get pasteButtonLabel => ml.pasteButtonLabel;
    
      @override
      String get postMeridiemAbbreviation => ml.postMeridiemAbbreviation;
    
      @override
      String get selectAllButtonLabel => ml.selectAllButtonLabel;
    
      @override
      String timerPickerHour(int hour) {
        return hour.toString().padLeft(2, "0");
      }
    
      @override
      String timerPickerHourLabel(int hour) {
        return "$hour".toString().padLeft(2, "0") + "ពេល";
      }
    
      @override
      String timerPickerMinute(int minute) {
        return minute.toString().padLeft(2, "0");
      }
    
      @override
      String timerPickerMinuteLabel(int minute) {
        return minute.toString().padLeft(2, "0") + "ចែក";
      }
    
      @override
      String timerPickerSecond(int second) {
        return second.toString().padLeft(2, "0");
      }
    
      @override
      String timerPickerSecondLabel(int second) {
        return second.toString().padLeft(2, "0") + "ជាលើកទីពីរ";
      }
    
      static const LocalizationsDelegate<CupertinoLocalizations> delegate =
          _ChineseDelegate();
    
      static Future<CupertinoLocalizations> load(Locale locale) async {
        var localizaltions = KhmerCupertinoLocalizations();
        await localizaltions.init();
        return SynchronousFuture<CupertinoLocalizations>(localizaltions);
      }
    }
    
    class _ChineseDelegate extends LocalizationsDelegate<CupertinoLocalizations> {
      const _ChineseDelegate();
    
      @override
      bool isSupported(Locale locale) {
        return locale.languageCode == 'km';
      }
    
      @override
      Future<CupertinoLocalizations> load(Locale locale) {
        return KhmerCupertinoLocalizations.load(locale);
      }
    
      @override
      bool shouldReload(LocalizationsDelegate<CupertinoLocalizations> old) {
        return false;
      }
    }
    
    

    相关文章

      网友评论

        本文标题:flutter国际化处理

        本文链接:https://www.haomeiwen.com/subject/qbecpctx.html