美文网首页
Flutter多语言 2022-11-15 周二

Flutter多语言 2022-11-15 周二

作者: 勇往直前888 | 来源:发表于2022-11-15 13:38 被阅读0次

    方案选择

    • Flutter对多语言是支持的,不够功能有限,也不是很好用。

    • GetX中对多语言的支持做得很好,果断选择采用。

    GetX中关于多语言介绍

    • 继承类Translations,以key,value的形式实现多语言。第一级的key为语言选择,第二级的key为字段标签。最后的value就是最终显示的文本。
    import 'package:get/get.dart';
    
    class Messages extends Translations {
      @override
      Map<String, Map<String, String>> get keys => {
            'en_US': {
              'hello': 'Hello World',
            },
            'de_DE': {
              'hello': 'Hallo Welt',
            }
          };
    }
    
    • 使用的话,只要加.tr后缀就可以,非常方便
    Text('hello'.tr);
    
    • 通过@标记,还可以带参数,这个和OC的习惯一致。
    import 'package:get/get.dart';
    
    
    Map<String, Map<String, String>> get keys => {
        'en_US': {
            'logged_in': 'logged in as @name with email @email',
        },
        'es_ES': {
           'logged_in': 'iniciado sesión como @name con e-mail @email',
        }
    };
    
    Text('logged_in'.trParams({
      'name': 'Jhon',
      'email': 'jhon@example.com'
      }));
    
    • 初始化,使用Translations的地方在程序初始化的地方。这也导致Flutter的热加载不适用于多语言,每次改动都要重新加载,不是很方便。
    return GetMaterialApp(
        translations: Messages(), // your translations
        locale: Locale('en', 'US'), // translations will be displayed in that locale
        fallbackLocale: Locale('en', 'UK'), // specify the fallback locale in case an invalid locale is selected.
    );
    
    • 切换语言,其实就是更新Locale,更换Translations的第一级key
    var locale = Locale('en', 'US');
    Get.updateLocale(locale);
    
    • 获取系统语言设置。也就是获取系统的语言key,自动设置。说实话,这作用不大,还不如固定一个默认语言(用在初始化中),然后提供入口进行切换(比如app的设置页面)。
      如果非要根据手机的语言设置,自动设置程序的默认语言,那么就可以按照下面的方式来写
    return GetMaterialApp(
        locale: Get.deviceLocale,
    );
    

    修改方案

    所有的语言定义都在Translations一个文件中,很容易出现文件过长的问题。所以会把两级的Map进行拆分,把第二级的Map独立为各个语言文件。

    • 将Translations的第二级Map独立成文件
    class TranslationService extends Translations {
      @override
      Map<String, Map<String, String>> get keys => {
            'en': en_language,
            'es': es_language,
            'zh': zh_language,
          };
    }
    
    • 第二级的语言文件就是一个Map
    const Map<String, String> en_language = {
      /// 公共部分-基础
      WidgetIds.commonBaseTitle: 'Title',
      WidgetIds.commonBaseContent: 'Content',
      WidgetIds.commonBaseDescription: 'Description',
      WidgetIds.commonBaseOk: 'OK',
      WidgetIds.commonBaseCancel: 'Cancel',
      WidgetIds.commonBaseSubmit: 'Submit',
    }
    
    • 各个语言文件的key是公用的,所以集中在一个地方进行定义。
    class WidgetIds {
      /// 简要说明:
      /// 这里定义组件的id
      /// 变量定义采用小驼峰的命名习惯
      /// 变量的值采用小写加点隔离的方式,类似包名的习惯
      /// 可以用来区分组件: dart中组件统一用Widget表示,所以这里命名为WidgetIds
      /// 使用场景有多语言,统计等等需要组件定位的场景。
      /// 多语言:使用的时候,需要加上.tr后缀,比如:  WidgetIds.commonBaseTitle.tr
      /// 多语言:定义文件中,作为字典的key来用,比如: WidgetIds.commonBaseTitle: 'Title',
    
      /// 公共部分-基础
      static const String commonBaseTitle = 'common.base.title';
      static const String commonBaseContent = 'common.base.content';
      static const String commonBaseDescription = 'common.base.description';
      static const String commonBaseOk = 'common.base.ok';
    }
    
    • 相关的文件可以放在一个文件夹中
    企业微信截图_8eeea650-0d45-4533-8170-207242935534.png

    简略方案

    • 将语言定义独立成单独的key ,value文件,这个保持不变。

    • 组件id定义,也就是语言文件的key定义,与实际的字符显示相差较远。另外定义想名字也是键头疼的事。

    • 使用起来也比较麻烦,像WidgetIds.commonBaseTitle.tr之类的,看上去也不直观。在这种时候,更容易忘记.tr后缀,导致显示'common.base.title'之类的内容。

    • 如果key对应的value没有定义,那么会直接用key的内容来代替,比如Text('hello'.tr); 如果定义了语言文件,会显示定义过的 'Hello World'或者'Hallo Welt'。但是如果没有定义对应的key,value,那么就会直接显示'hello'

    • 我们的应用,默认语言是英语,所以为图方便,我们就直接拿英语做key了,这样还可以少两个文件。widget_ids.dart这key的定义文件就不需要了。

    • 当然,这样做对于那种给@参数的用法就不适用了。凡事有利有弊。当然,要用也是可以的,把两种思路结合起来:大部分用英文作为key;带参数的,就自定义key。

    • 其他的语言文件,直接以英语当做key

    const Map<String, String> zh_language = {
      /// 公共部分-基础
      'Title': '标题',
      'Content': '内容',
      'Description': '描述',
      'OK': '确定',
      'Cancel': '取消',
    }
    
    • 适用的地方直接在英语后面加个.tr后缀就可以了。
        Text(
          'Cancel'.tr,
          style: TextStyle(
            color: const Color(0xFF333333),
            fontSize: 12.sp,
            fontWeight: FontWeight.w400,
          ),
        );
    

    Locale简介

    • 构造函数需要两个字符串,语言代码和地区代码。其中语言代码是必选的,地区代码是可选的
    const Locale(
        this._languageCode, [
        this._countryCode,
      ])
    
    • iOS的手机的菜单路径: 设置 =》通用 =》语言与地区
    企业微信截图_b4c1ae98-aab3-41bc-92c5-d4f6be5b9384.png
    • 可以理解为语言代码是主键,地区代码是副键。比如同样是中文,也区分中文大陆,中文香港,中文新加坡,中文台湾等。
      语言地区代码
    企业微信截图_19fd2a9c-c866-414e-9a99-e2f245f2df72.png
    • Translation。s中的key,也是以语言代码为主,地区代码为辅。中划线用下划线代替。
      另外,也可以简单处理,只用语言代码,不要地区代码。就算地区代码对不上,只要语言代码对了,也能选中。
    class MultiLanguage extends Translations {
      @override
      Map<String, Map<String, String>> get keys => {
            /// 英文
            'en_US': en_US,
    
            /// 中文
            'zh_CN': zh_CN,
          };
    }
    

    更新的话,给Get.updateLocale(const Locale('en', 'TT'));仍然能够选出英文。
    既然如此,干脆key值只给语言代码,不管区域代码。

    简单实践

    • 只管语言代码,不管区域代码,简单化处理。当前的需求,只要求有中文版和英文版,还没有那么细。

    • 初始化取手机的系统设置;

    • 默认语言设定为中文

    • main.dart中的设置

    企业微信截图_7ad8d70c-d49a-4abe-9a1d-6a02ace8c5a6.png
    • 字典中只管语言代码,不管区域代码
    import 'en.dart';
    import 'zh.dart';
    
    class MultiLanguage extends Translations {
      @override
      Map<String, Map<String, String>> get keys => {
            /// 英文
            'en': en,
    
            /// 中文
            'zh': zh,
          };
    }
    
    /// 中文多语言字典
    const Map<String, String> zh = {
      'title': '这是标题',
      'login': '登录用户 @name,邮箱账号 @email',
    };
    
    /// 英文多语言字典
    const Map<String, String> en = {
      'title': 'This is Title!',
      'login': 'logged in as @name with email @email',
    };
    
    • 切换时只给语言代码,不管区域代码
    Get.updateLocale(const Locale('en'));
    
    • 简单例子
      代码
    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    
    import 'home_controller.dart';
    
    class HomePage extends GetView<HomeController> {
      const HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return GetBuilder<HomeController>(
          builder: (context) {
            return Scaffold(
              appBar: AppBar(
                title: const Text('HomePage'),
                centerTitle: true,
              ),
              body: Center(
                child: GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onTap: () {
                    Get.updateLocale(const Locale('en'));
                  },
                  child: Text(
                    'login'.trParams(
                        {'name': 'zhang san', 'email': 'zhangsan@baidu.com'}),
                    style: const TextStyle(fontSize: 20),
                    textAlign: TextAlign.center,
                  ),
                ),
              ),
            );
          },
        );
      }
    }
    

    初始界面:(手机设置为中文)

    企业微信截图_d81b0547-16a3-478b-9eb1-99157ec2fdfa.png

    点一下文字,就切换为英文

    企业微信截图_3b40853e-876c-4b76-be43-ccbafed21f78.png
    • 关于key的定义
      (1)如果想规范一点,那么就用全局变量,按照组件id的的模式统一规范;
      (2)如果想方便一点,就用英语作为key。那么en.dart绝大部分可以key和value一样;
      zh.dart就算没定义,也会显示有意义的英文,既方便又使用。

    我们一开始是用方法(1)的;很规范,但是真的烦。 后来就改成了方法(2);工作量减少很多。

    系统日期组件多语言

    • 系统的DateTime组件有一个locale参数,直接加上会崩溃。

    • 如果想要这种系统组件支持多语言,需要额外引入一个库flutter_localizations

    • 参考文章:

    Flutter配置国际化localizations

    相关文章

      网友评论

          本文标题:Flutter多语言 2022-11-15 周二

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