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