美文网首页
Flutter怎么样做国际化

Flutter怎么样做国际化

作者: QiShare | 来源:发表于2021-10-01 18:00 被阅读0次

    什么是国际化

    国际化是指在设计软件时,将软件与特定语言及地区脱钩的过程。当软件被移植到不同的语言地区时,软件本身不用做内部工程上的改变或修正。

    本地化则是指当移植软件时,加上与特定区域设置有关的资讯和翻译文件的过程。 国际化和本地化之间的区别虽然微妙,但却很重要。国际化意味着产品有适用于任何地方的潜力;本地化则是为了更适合于特定地方的使用,而另外增添的特色。用一项产品来说,国际化只需做一次,但本地化则要针对不同的区域各做一次。 这两者之间是互补的,并且两者结合起来才能让一个系统适用于各地。

    国际化实现中的困难

    开发软件时,国际化和本地化对开发者是一个有挑战性的任务,特别是当软件当初设计时没有考虑这个问题时。通常做法是将文本和其他环境相关的资源与程序代码相分离。这样在理想的情况下,应对变化的环境时无需修改代码,只要修改资源,从而显著简化了工作。

    Flutter的国际化

    Flutter中的国际化包括Flutter组件的国际化和其他文本的国际化两者;

    Flutter组件的国际化

    Flutter给我们提供的Widget默认情况下就是支持国际化,但是在没有进行特别的设置之前,它们无论在什么环境都是以英文的方式显示的。

    如果想要添加其他语言,你的应用必须指定额外的 MaterialApp 属性并且添加一个单独的 package,叫做 flutter_localizations

    截至 2020 年 11 月,该软件包支持 78 种语言。

    pubspec添加依赖

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

    dependencies:
      flutter:
        sdk: flutter
      flutter_localizations:
        sdk: flutter
    

    设置MaterialApp

    • 在localizationsDelegates中指定哪些Widget需要进行国际化

      • 用于生产本地化值集合的工厂
      • 我们这里指定了Material、Widgets、Cupertino都使用国际化
    • supportedLocales指定要支持哪些国际化

      • 我们这里指定中文和英文(也可以指定国家编码)
    MaterialApp(
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate, // 指定本地化的字符串
        GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格
        GlobalWidgetsLocalizations.delegate // 指定默认的文本排列方向, 由左到右或由右到左
      ],
      supportedLocales: [
        Locale("en"),
        Locale("zh")
      ],
    )
    

    注意:如果要指定语言代码、文字代码和国家代码,可以进行如下指定方式:

    // Full Chinese support for CN, TW, and HK
    supportedLocales: [
      const Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
      const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'
      const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'
      const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'), // 'zh_Hans_CN'
      const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'), // 'zh_Hant_TW'
      const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'), // 'zh_Hant_HK'
    ],
    

    Flutter中自定义文本的国际化

    创建本地化类

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

    • 1.我们需要一个构造器,并且传入一个Locale对象
    • 2.定义一个Map,其中存放我们不同语言对应的文本
    • 3.定义它们对应的getter方法,根据语言环境返回不同的结果
    import 'package:flutter/material.dart';
    
    class QWLocalizations {
      final Locale locale;
    
      QWLocalizations(this.locale);
    
      static Map<String, Map<String, String>> _localizedValues = {
        "fr": {"title": "Titre", "hello": "Bonjour"},
        "zh": {"title": "首页", "hello": "你好"}
      };
    
      String get title {
        return _localizedValues[locale.languageCode]?["title"] ?? 'title';
      }
    
      String get hello {
        return _localizedValues[locale.languageCode]?["hello"] ?? 'hello';
      }
    
      static QWLocalizations of(BuildContext context) {
        return Localizations.of(context, QWLocalizations);
      }
    }
    

    自定义Delegate

    上面的类定义好后,我们在什么位置或者说如何对它进行初始化呢?
    我们可以像Flutter Widget中的国际化方式一样对它们进行初始化,也就是我们可以定义一个对象的Delegate类,并且将其传入localizationsDelegates中;

    Delegate的作用就是当Locale发生改变时,调用对应的load方法,重新加载新的Locale资源

    HYLocalizationsDelegate需要继承自LocalizationsDelegate,并且有三个方法必须重写:
    isSupported,shouldReload,load

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/foundation.dart';
    
    import 'QWLocalizations.dart';
    
    class QWLocalizationsDelegate extends LocalizationsDelegate<QWLocalizations> {
      //是否在我们支持的语言范围
      @override
      bool isSupported(Locale locale) {
        return ["fr", "zh"].contains(locale.languageCode);
      }
    
      /*
      * 当Localizations Widget重新build时,是否调用load方法重新加载Locale资源
        一般情况下,Locale资源只应该在Locale切换时加载一次,不需要每次Localizations重新build时都加载一遍;
        所以一般情况下返回false即可;
      * */
      @override
      bool shouldReload(LocalizationsDelegate<QWLocalizations> old) {
        return false;
      }
    
      /*
      * 当Locale发生改变时(语言环境),加载对应的HYLocalizations资源
      这个方法返回的是一个Future,因为有可能是异步加载的;
      但是我们这里是直接定义的一个Map,因此可以直接返回一个同步的Future(SynchronousFuture)
      * */
      @override
      Future<QWLocalizations> load(Locale locale) {
        return SynchronousFuture(QWLocalizations(locale));
      }
    
      static QWLocalizationsDelegate delegate = QWLocalizationsDelegate();
    }
    

    异步加载数据

    假如我们的数据是异步加载的,比如来自Json文件或者服务器,应该如何处理呢?

    QWLocalizations类中如下面代码:

      static Map<String, Map<String, String>> _localizedValues = {};
    
      Future<bool> loadJson() async {
        // 1.加载json文件
        String jsonString = await rootBundle.loadString("assets/json/i18n.json");
        
        // 2.转成map类型
        final Map<String, dynamic> map = json.decode(jsonString);
        
        // 3.注意:这里是将Map<String, dynamic>转成Map<String, Map<String, String>>类型
        _localizedValues = map.map((key, value) {
          return MapEntry(key, value.cast<String, String>());
        });
        return true;
      }
    

    在QWLocalizationsDelegate中使用异步进行加载:

      @override
      Future<QWLocalizations> load(Locale locale) async {
        final localization = QWLocalizations(locale);
        await localization.loadJson();
        return localization;
      }
    

    使用本地化类

    接着我们可以在代码中使用HYLocalization类。

    • 我们可以通过QWLocalizations.of(context)获取到QWLocalizations对象
    Text(
      QWLocalizations.of(context).hello,
    )
    

    国际化的工具---Intl

    认识arb文件

    目前我们已经可以通过加载对应的json文件来进行本地化了。

    但是还有另外一个问题,我们在进行国际化的过程中,下面的代码依然需要根据json文件手动编写

    String get title {
      return _localizedValues[locale.languageCode]?["title"] ?? 'title';
    }
    
    String get hello {
      return _localizedValues[locale.languageCode]?["hello"] ?? 'hello';
    }
    

    有没有一种更好的方式,让我们可以快速在本地化文件即dart代码文件直接来转换呢?答案就是arb文件

    • arb文件全称Application Resource Bundle,表示应用资源包,目前已经得到Google的支持;
    • 其本质就是一个json文件,但是可以根据该文件转成对应的语言环境;
    • arb的说明文档:https://github.com/google/app-r

    使用IDE插件来进行arb和dart文件之间的转换

    • 初始化intl

    选择工具栏Tools - Flutter Intl - Initialize for the Project

    image.png

    完成上面的操作之后会自动生成如下文件目录:

    • generated是自动生成的dart代码
    • I10n是对应的arb文件目录
    image.png

    使用intl

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

    • 1.添加对应的delegate
    • 2.supportedLocales使用S.delegate.supportedLocales
    localizationsDelegates: [
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
      GlobalCupertinoLocalizations.delegate,
      S.delegate
    ],
    supportedLocales: S.delegate.supportedLocales,
    

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

    {
      "title": "home",
      "hello": "hello"
    }
    
    • 编写后ctrl(command) + s保存即可;

    之后按照如下格式在代码中使用

    S.of(context).title
    

    添加中文

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

    image.png
    • 在弹出框中输入zh即可

    我们会发现,会生成对应的intl_zh.arb和messages_zh.dart文件

    image.png

    arb其它语法

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

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

    修改对应的arb文件:

    • {name}:表示传递的参数
    {
      "title": "home",
      "hello": "hello",
      "sayHello": "hello {name}"
    }
    

    在使用时,传入对应的参数即可:

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

    总结

    文本的国际化实质就是根据系统提供的locale信息去获取对应的文本和对UI做相应操作(指从左到右还是从右到左展示),locale信息是指国家代码、地区代码等,通常我们本地需要做的就是把文案按照{国家代码:{通用文本:本地化文案}}的格式进行组织排列。这里国家代码比如中国是zh,美国是en。通用文本一般用英文。最后就是根据locale信息和通用文本去字典获取本地化文本值的过程。

    参考:

    Flutter国际化

    https://zhuanlan.zhihu.com/p/145992691

    相关文章

      网友评论

          本文标题:Flutter怎么样做国际化

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