编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 01.设计高效日志框架库
  • 02.国际化项目方案实践
  • 03.设计通用缓存框架
  • 04.设计轻量级线程池库
  • 05.设计通用轮训方案
  • 06.设计异步线程框架
  • 07.设计隐私合规检测库
  • /zh/android/plan/08.设备串口通信实践.html
  • 09.设计TTS语音实践
  • 10.设计悬浮窗开发实践
  • 13.SDK基础架构实践

02.国际化项目方案实践

目录介绍

  • 01.整体概述介绍
    • 1.1 项目背景的说明
    • 1.2 国际化要做什么
    • 1.3 国际化事项说明
  • 02.Locale实践探索
    • 2.1 Locale基础概念
    • 2.2 获取系统Locale
    • 2.3 应用设置Locale
    • 2.4 应用更新Locale
    • 2.5 应用获取Locale
    • 2.6 获取默认Locale
  • 03.多语言国际化适配
    • 3.1 如何获取系统语言
    • 3.2 如何获取应用语言
    • 3.3 如何切换应用语言
    • 3.5 拒绝硬编码字符串
  • 04.多布局国际化适配
  • 05.多资源国际化适配
    • 5.1 获取国家缩写
    • 5.2 货币符号适配
  • 06.根据时区校验时间
    • 6.1 时区的概念介绍
    • 6.2 如何获取时区
    • 6.3 如何设置时区
    • 6.4 根据时区获取时间

01.整体概述介绍

1.1 项目背景的说明

  • 应用上线到谷歌应用市场
    • 那么应该做好本地化的支持,用来支持不同语言及地区的风俗习惯,当然也要结合公司拓展的海外市场需要,那么对于一款应用,至少应该做到多语言和多布局的支持。
  • 国际化组件背景说明
    • 团餐机,门禁等终端设备,为了使用出海政策,因此需要做国际化处理。

1.2 国际化要做什么

  • 1、必须要进行资源文件的国际化
    • 这些资源主要是指:文字资源,字体资源【使用默认】,图片资源
  • 2、针对特殊的翻译,需要做做适配。
    • 语言翻译,可以获取系统local自动配置相关语言。如果app中有语音播放,则音频文件也需要处理
  • 3、接口需要做国际化,需要在接口参数中增加字段。
    • 看看接口是否需要添加字段用来区分不同的区域。注意:下发数据对应的资源
  • 4、组件UI兼容适配
    • 针对一些组件,可能需要做兼容,比如在中文下内容长度合适,但是在其他语言情况下,长度超出,那么可能得做适配。

1.3 国际化事项说明

02.Locale实践探索

2.1 Locale基础概念

  • Locale对象表示了一个特定的地理,政治或文化区域。
    • 需要使用到Locale执行其任务的操作称为区域设置敏感,并使用Locale为用户定制信息。
    • 例如显示一个数字就是一个区域设置敏感的操作–该数字应根据用户所在国家,地区或文化的习俗和惯例进行格式化。
    • Locale是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境。
  • locale就是某一个地域内的人们的语言习惯和文化传统和生活习惯。
    • 一个地区的locale就是根据这几大类的习惯定义的。其中暂时只关注这几点:语言、数字、时间、货币、距离。
  • locale有什么属性
    • languageTag 普通语言标签
    • language 语言
    • country 地区
    • script 脚本
    • variant 变体
    • 还有其他属性 其他
  • 重点关注几个属性
    • variant:变体子标签用于指示附加的、公认的方言,定义一种不被其他子标签代表的语言。
    • 如果有两个或更多个变体值表示它们自己的语义,则这些值应按重要性排序,最重要的排第一个,用下划线(’_’)分隔。 变体字段区分大小写。
  • 举一个例子
    获取系统locale的属性 : zh_CN_#Hans
    获取系统locale的tag : zh-Hans-CN
    获取系统locale的language : zh
    获取系统locale的country : CN
    获取系统locale的script : Hans
    获取系统locale的variant : 
    
    获取系统locale的displayLanguage : 中文
    获取系统locale的ISO3Language : zho
    获取系统locale的displayCountry : 中国
    获取系统locale的ISO3Country : CHN

2.2 获取系统Locale

  • 系统语言的设置在手机设置页面的语言中选中即可。在应用中如果想要获取到系统当前语言Locale,可以用该方法。
    public static Locale getSysLocale() {
        Locale locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            locale = Resources.getSystem().getConfiguration().getLocales().get(0);
        } else {
            locale = Resources.getSystem().getConfiguration().locale;
        }
        return locale;
    }

2.3 应用设置Locale

  • Android更新应用(某个apk)内的Locale
    • 是通过context的resource更新的,而Application的resource和所有Activity的resource是不同的两个资源对象。
    • 也就是说所有Activity的resource共享一个对象(一份配置),Application的是单独的一份配置,这也就表示了我们使用这两种context获取或更新Locale时,会出现差异性:一个更新了一个没更新。
    • 由于我们在获取Locale时,无法保证使用Application的context还是Activity的context,所以需要我们在更新Locale时,将两种context都更新。

2.4 应用更新Locale

  • updateConfiguration。切换应用的Locale时,通过context设置新的Locale,不同API版本有不同的方法。
    public static void updateLocale(Context context, Locale newLocale){
        Configuration configuration = context.getResources().getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            configuration.setLocales(new LocaleList(newLocale));
        } else {
            configuration.setLocale(newLocale);
        }
        context.getResources().updateConfiguration(configuration,null);
    }
  • 可以看到,通过将新的Locale设置到context的resource. updateConfiguration方法即可实时更新Locale。
    • 但是根据上面说的问题:假如我们使用Application的context更新Locale时,Activity的context不会同步更新,这就会导致我们在后续使用Activity的context.getString()时,使用的Locale还是之前的Locale,造成错误,反之也是一样。
    • 所以通常我们可以在更新Locale时将两种context都更新。
    updateLocale(context,locale)
    updateLocale(context.applicationContext,locale)

2.5 应用获取Locale

  • 更新Locale后,我们通过context拿到的Locale应该就是更新后的Locale了。
    public static Locale getAppLocale(Context context){
        Locale locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            locale = context.getResources().getConfiguration().getLocales().get(0);
        } else {
            locale = context.getResources().getConfiguration().locale;
        }
        return locale;
    }
  • 这里同理,context可能是Activity的,也可能是Application的,所以还是那句话,一定要保证上述更新Locale的正确方式。

2.6 获取默认Locale

  • Locale.getDefault() 这个方法是获取JVM启动时默认的Locale
    • 默认是和系统当前Locale一致,也可以通过Locale.setDefault()设置。
    • 通常在我们手动切换了Locale后,最好通过Locale.setDefault()方法设置一下默认的locale,否则在使用Locale.getDefault()时可能会拿不到当前的Locale。

03.多语言国际化适配

3.1 如何获取系统语言

  • 自Android7.0系统起,由LocaleList管理语言
    • 系统可设置多个语言列表,根据优先级来选定语言。正常情况下,若应用语言跟随系统,则直接LocaleList.getDefault().get(0)则可拿到系统当前语言。
    • 可是,若应用通过configuration.setLocale(locale)设置语言后(源码实际是new LocaleList(locale)),该locale会被塞进系统语言列表的首位,此时系统当前语言并不是首位的语言。
    • 因此,若应用再次选择跟随系统后,拿到语言列表首位的语言就不是系统当前的语言。

3.2 如何获取应用语言

3.3 如何切换应用语言

  • 首先获取语言的列表,这个举个例子
    public List<Locale> getDefaultList() {
        List<Locale> defaultList = new ArrayList<>();
        defaultList.add(new Locale("en"));
        defaultList.add(new Locale("es"));
        defaultList.add(new Locale("pt"));
        defaultList.add(new Locale("ja"));
        defaultList.add(new Locale("zh"));
        return defaultList;
    }
  • 然后获取当前手机的locale,进行比较,如果语言相同则选中
    • 这里是比较语言是否相同。提问:为何不能比较languageTag?
    for (int i=0 ; i<supportLocaleList.size() ; i++){
        Locale locale = supportLocaleList.get(i);
        String displayLanguage = locale.getDisplayLanguage();
        boolean isSame = LocaleUtils.equalLocales(appLocale, locale);
        stringBuilder2.append("语言 ")
                .append(isSame ? "当前语言 : " : " : ")
                .append(displayLanguage)
                .append("\n");
    }
  • 对语言进行切换,需要重启app
    LocaleUtils.updateLocale(LocaleActivity.this,new Locale("en"));

3.5 拒绝硬编码字符串

  • 拒绝任何形式的硬编码字符串,所有字符串应该通过string.xml资源文件加载,便于本地化
     ../values-en/strings.xml 英语
     <string name="my_topic_btn">My Topic</string>
     ../values-ar/strings.xml 阿拉伯语
     <string name="my_topic_btn">موضوعي</string>
     ../values-fr/strings.xml 法语
     <string name="my_topic_btn">mon sujet</string>
     ../values-hi/strings.xml 印度语
     <string name = "my_topic_btn"> मेरा विषय</string>
    
    //而不要使用     
    tvView.setText(“硬编码字符串,这样无法做到动态加载语言”);

05.多资源国际化适配

5.1 获取国家缩写

  • Android国家缩写是一种用来标识国家或地区的字符串
    • 多语言支持:不同国家或地区使用不同的语言,通过国家缩写可以方便地切换应用的语言环境,实现多语言支持。
    • 货币单位显示:不同国家或地区使用不同的货币单位,通过国家缩写可以判断用户所在的国家或地区,然后显示对应的货币单位。
  • 通过Locale类,可以获取当前设备的国家缩写,然后根据需要进行相应的处理。
    // 获取当前设备的国家缩写
    Locale currentLocale = getResources().getConfiguration().locale;
    String countryCode = currentLocale.getCountry();
    
    // 根据国家缩写进行不同的处理
    if (countryCode.equals("CN")) {
        // 处理中国的逻辑
    } else if (countryCode.equals("US")) {
        // 处理美国的逻辑
    } else {
        // 处理其他国家的逻辑
    }

5.2 货币符号适配

  • 货币符号格式化的处理方式:
    • 方法1: 使用系统格式化工具处理
    • 方法2: 使用自定义货币符号适配
  • 使用系统格式化工具处理说明
    • 货币格式化最简单的方式是调用NumberFormat.getCurrencyInstance()获得 NumberFormat 实例来把数字格式化为货币格式的字符串。
    • 这个方法有一个可能的弊端,就是它可能不会反映最新的货币变化,特别是在货币单位或者分母有变化时。
  • 使用自定义货币符号适配说明
    • 自定义需要适配国家的货币符号,然后根据国家或者唯一标识去获取货币符号。

06.根据时区校验时间

6.1 时区的概念介绍

  • 对于时区来说,有着不同标准,比如:GMT和UTC
    • 先说结论:在编程领域,我们可以认为两者完全相同。GMT+8 也就是 UTC+8。
    • 各地的标态追强准时间为格林威治时间(G.M.T)加上 (+) 或减去 (-) 时区中所标的小时和分钟数时差。
  • 时区(Timezone)是如何礼节
    • 大家都知道北京是东八区,可以记作 UTC+8;而美国阿拉斯加是西九区,记作UTC-9。
    • UTC 是指“协调世界时”,是一个基准时间。后面跟着的“+n”可以理解为“快了n 个小时“。“-n”同理。所以北京时间比阿拉斯加快了17个小时。
    • 一天有 24 小时,所以分为 24 个时区:西十一区到东十二区(UTC-11 到 UTC+12)。
    • https://juejin.cn/post/6860788045449429000

6.2 如何获取时区

  • 格式1:GMT+08:00
    • TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT)
    • 这种是获取GMT时区的方式,但是会存在返回“PST”、“WIB”等别名的时区,这会导致返回格式不统一,解析起来会存在问题。
  • 格式2:+0800
    • SimpleDateFormat("Z").format(Calendar.getInstance(TimeZone.getTimeZone("GMT"),Locale.getDefault()).time)
    • 其实这种格式已经是比较通用,业务上可以直接使用这种方式统一时区格式。

6.3 如何设置时区

6.4 根据时区获取时间

  • 第一种方式,通过SimpleDateFormat获取某时区时间
    • 这个方法获取的结果是24小时制的,月份也正确。即使手机设置成别的时区,不是东八区,这个方法返回的也照样是北京时间。
    SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
    dff.setTimeZone(TimeZone.getTimeZone("GMT+08")); 
    String ee = dff.format(new Date());
  • 第二种方式,通过Calendar获取某时区时间
    Calendar calendar = Calendar.getInstance();
    SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy hh:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    String rt = sdf.format(calendar.getTime());
  • 第三种方式,通过网络获取某时区时间
    URL url=new URL("http://www.baidu.com");//取得资源对象
    URLConnection uc=url.openConnection();//生成连接对象
    uc.connect(); //发出连接
    long ld=uc.getDate(); //取得网站日期时间
    Date date=new Date(ld); //转换为标准时间对象

其他

  • 国际化语言
  • https://juejin.cn/post/6844903588313956365?searchId=20240122105714756FF3E9760273C6C8F9
  • https://juejin.cn/post/7046685955230531597?searchId=20240122105714756FF3E9760273C6C8F9
贡献者: yangchong211
上一篇
01.设计高效日志框架库
下一篇
03.设计通用缓存框架