• 笔记:国际化


    国际化英文单词为:Internationalization,又称I18N,I为因为单词的第一个字母,18为这个单词的长度,而N代表这个单词的最后一个字母。国际化又称本地化(Localization,L10N)。

    Java国际化主要通过如下3个类完成

    • java.util.ResourceBundle:用于加载一个资源包
    • java.util.Locale:对应一个特定的国家/区域、语言环境。
    • java.text.MessageFormat:用于将消息格式化

    为实现程序的国际化,必须提供程序所需要的资源文件。资源文件的内容由key-value对组成,资源文件的命名可以有3种格式:

    • basename_language_country.properties
    • basename_language.properties
    • basename_properties

    若资源文件包含非西方字符,则需要用JDK自带的工具来处理:native2ascii,这个工具的语法格式如下:

    native2ascii 资源文件名 目标资源文件名

    如:

    native2ascii mess_zh_XXX.proerties mess_zh_CN.proerties

    Locale类可获取各国区域环境(如:Locale.ENGLISH、Locale.CHINESE,这些常量返回一个Locale实例),也可以获取当前系统所使用的区域语言环境,也可以使用语言和区域来创建Locale对象,Java的本地语言使用的时国际化标准组织(ISO)所定义的编码,本地语言由小写的两个字母的代码表示,遵循ISO-639-1;国家代码由大写的两个字母的代码组成,遵循ISO-3166-1,下表为常用的代码:

    语言

    代码

    国家

    代码

    Chinese

    zh

    China

    CN

    English

    en

    United States

    US

    Japanese

    ja

    Japan

    JP

    可以使用Locale的静态方法 getAvailableLocales 来获取所支持的语言和国家,该方法返回一个Locale数组,该数组里包含了java所支持的语言和国家,代码如下:

    Locale[] availableLocales = Locale.getAvailableLocales();

            for (Locale l : availableLocales) {

                  System.out.println("Locale DisplayName=" + l.getDisplayName() + " Country=" + l.getCountry() + " Language="

    + l.getLanguage());

    }

    1. 数字格式

      数字和货币的格式时高度依赖与Locale的,Java类库提供了一个格式器(formatter)对象的集合,可以对java.text包中的数字值进行格式化和解析,可以通过下面的步骤来对特定的Locale的数字进行格式化:

    • 获取Locale对象
    • 使用工厂方法获取格式器对象,工厂方法时 NumberFormat 类的静态方法,接受一个Locale 类型的参数,有三个工厂方法:getNumberInstance(数字)、getCurrencyInstance(货币) getPercentInstance(百分比)
    • 使用这个格式器对象来完成格式化和解析工作

    格式化示例代码:

    Locale deLocale = new Locale("de", "DE");

    NumberFormat currFmt = NumberFormat.getCurrencyInstance(deLocale);

    double amt = 123878.34;

    String formatResult = currFmt.format(amt);

    System.out.println("amt=" + amt + " Format=" + formatResult);

    如果想要读取一个按照某个Locale的惯用法而输入或存储的数字,那边就需要使用 parse 方法。

    解析示例代码:

    Localede Locale=newLocale("de","DE");

    NumberFormat currFmt=NumberFormat.getCurrencyInstance(deLocale);

    Number input=currFmt.parse(formatResult);

    double parseAmt=input.doubleValue();

    System.out.println("FormatAmt="+formatResult+"ParseAmt="+parseAmt);

    1. 日期和时间

      当格式化日期和时间时,需要考虑4个与Locale相关的问题,例如:月份和星期应该用本地语言来表示;年月日的顺序要符合本地习惯;公历可能不是本地首选的日期表示方法;必须要考虑本地时区。Java 使用 DateFormat 类来处理这些问题,和 NumberFormat 类很类似,调用 DateFormat 类的静态方法,并传入 Locale 来实例化,还需要设置日期或时间的格式化值,DateFormat 有如下三个工厂方法:

      DateFormat.getDateInstance(dateStyle,loc);

      DateFormat.getTimeInstance(timeStyle,loc);

      DateFormat.getDateTimeInstance(dateStyle,timeStyle,loc);

      其日期和时间的风格使用 DateFormat 的静态常量来表示,常用的静态常量如下:

      DateFormat.DEFAULT

      DateFormat.FULL

      DateFormat.LONG

      DateFormat.MEDIUM

      DateFormat.SHORT

      示例代码如下:

      String fmt = "";

      Date nowDate = new Date();

      DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, zhLocale);

      fmt = dateFormat.format(nowDate);

      System.out.println("Short style date string " + fmt);

         

      dateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, zhLocale);

      fmt = dateFormat.format(nowDate);

      System.out.println("MEDIUM style time string " + fmt);

         

      dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, zhLocale);

      fmt = dateFormat.format(nowDate);

      System.out.println("FULL style date FULL style time string " + fmt);

      如果要解析一个用户输入的日期,可以使用 DateFormat 类的 parse 方法,但其输入的格式必须和创建 DateFormat 对象设置的风格一致,如果不一致则会抛出 IllegalArgumentException 异常,默认支持宽松的转换,比如日期 2017年2月30日,会被解析为 2017年3月2日,如果希望关闭宽松的转换,需要设置 lenient 标识,示例代码如下:

      dateFormat.setLenient(false);

      Date convertDate = dateFormat.parse(fmt);

      System.out.println("parse string " + fmt + " convertDate " + convertDate);

    2. 字符串排序

      Java 中的排序时用Unicode字符来决定顺序的,比如小写字母的Unicode值比大写的大,有重音符的字母的值甚至更大,这样将使结果失去意义,如果需要定义排序的强度,可以使用Locale对象来创建 Collator 类,该类继承了 Comparator接口,因此可以将该对象传递给 Collections.soft 方法来进行排序,示例代码如下:

      List<String> list = new LinkedList<>();

      list.add("America");

      list.add("Zulu");

      list.add("able");

      list.add("zebra");

         

      Collator collator = Collator.getInstance(zhLocale);

      // 设置排序强度

      collator.setStrength(Collator.PRIMARY);

      Collections.sort(list, collator);

      可以设置排序强度来选择不同的排序行为,字符间的差别可以被分为首要的(primary)、其次的(secndary)和再次的(tertiary)。比如,在英语中,A 和 Z 之间的差异被归类为首要的;A 和 Å (重音符)之间的差异是其次的;A 和 a 之间是再次的,可以使用方法 setStrength 来设置排序强度,Collator.PRIMARY 表示首要的、Collator.SECONDARY 表示其次的、 Collator.TERTIARY 表示再次的,而Collator.IDENTICAL 则表示不允许有任何差异。

      偶尔我们会碰到一个字符或字符序列在描述成Unicode时,可以有多种方式,例如,字母序列"ffi"可以用代码U+FB03描述成单个字符"拉丁小连字ffi",Unicode 标准对字符串定义了四种范式形式:D、KD、C和KC,其中 D 和 KD 时用于排序的,在范化形式 D 重,重音字符被分解为基字符和组合重音符;范化形式 KD 更进一步将兼容性字符也进行了分解,例如 ffi 连字符或商标符号TM,我们可以选择排序器所使用的范化程度:Collator.NO_DECOMPOSITION表示不对字符串做任何范化;Collator.CANONICAL_DECOMPOSITION 使用范化形式 D;Collator.FULL_DECOMPOSITION 使用范化形式 KD。可以使用方法 setDecomposition 来设置分解模式

    3. 消息格式化

      Java 类库中有一个 MessageFormat 类,用来格式化带变量的文本,就像这样:"今天 {0} 是个好日子,{1} 公司给我发了工资 {2} 元",括号中的数字是一个占位符,可以用实际的名字和值来替换他,使用静态的 MessageFormat.format 方法,该方法使用当前系统的 Locale 对值进行格式化,如果需要用指定的 Locale 来进行格式化,需要使用 MessageFormat 实例的 format 方法,示例代码如下:

      String mfString = "今天 {0} 是个好日子,{1} 公司给我发了工资 {2} 元";

      String formatString = MessageFormat.format(mfString, new Date(), "汇元1", 50000);

      System.out.println(formatString);

         

      MessageFormat mf = new MessageFormat(mfString, zhLocale);

      formatString = mf.format(new Object[]{new Date(), "汇元2", 50000});

      System.out.println(formatString);

      如果还想在指定占位符的同时设置类型和样式,可以按照如下格式:

      String mfString = "今天 {0,date,long} 是个好日子,{1} 公司给我发了工资 {2,number,currency} 元";

      mf = new MessageFormat(mfString, zhLocale);

      formatString = mf.format(new Object[]{new Date(), "汇元3", 50000});

      System.out.println(formatString);

      输出内容

      今天 2017年5月26日 是个好日子,汇元3 公司给我发了工资 ¥50,000.00 元

      占位符索引后面可以跟一个类型和样式,之间用逗号隔开,类型可以是number、time、date、choice,如果类型是 number 则样式有 integer、currency、percent;如果类型是 time 或 date,那么样式有 short、medium、long、full 或者是一个日期模式(yyyy-MM-dd);choice 表示希望消息跟随占位符的值而变化,选项格式是由一个序列对构成的,每个序列对包含一个下限和一个格式化字符串,下限和字符串使用#号分隔,对于对之间用 | 分隔,示例如下:

      mfString = "今天 {0,date,yyyy-MM-dd} 是个好日子,{1,choice,0#汇元|1#汇元1|2#汇元2} 公司给我发了工资 {2,number,currency} 元";

      mf.applyPattern(mfString);

      formatString = mf.format(new Object[]{new Date(), 1, 50000});

      System.out.println(formatString);

      可以使用 < 符号或 符号 来替换 # ,则表示值小于或者小于等于下限值,示例代码如下:

      // choice说明: 这个表示 小于 1000 并且 1001-5000 的使用"可怜" ,5001-50000 的使用"不够",50001 以上为"正好"

      mfString = "今天 {0,date,yyyy-MM-dd} 是个好日子,{1} 公司给我发了工资 {2,number,currency} 元,{2,choice,1000<可怜|5000<不够|50000<正好}";

      mf.applyPattern(mfString);

      formatString = mf.format(new Object[]{new Date(), "汇元4", 60000});

      System.out.println(formatString

      输入内容

      今天 2017-05-26 是个好日子,汇元4 公司给我发了工资 ¥60,000.00 元,正好

    4. 资源包

      当本地化一个应用时,可能会有大量的消息字符串、按钮标签和其他的东西需要被翻译,为了能灵活的完成这项任务,你会希望外部定义消息字符串,通常称之为资源(resource),翻译人员不需要接触程序源代码就可以很容易的编辑资源文件,在Java 中使用属性文件来设定字符串资源,并未其他类型的资源实现相应的类。

      当本地化一个应用时,会制造出很多资源包(resource bundle),每一个包都是一个属性文件或者一个描述了玉locale相关的项的类,对于每一个包,都要为所有你想要支持的locale提供相应的版本,并需要对这些包使用一种统一的命名规则,例如,为中国定义的资源放在一个名为"包名_zh_CN"的文件中,而为所有使用中文简体的国家所共享的资源则放在名为"包名_zh"的文件中,一般来说,使用

      包名_语言_国家

      来命名所有和国家相关的资源,使用

      包名_语言

      来命名所有和语言相关的资源,最后,作为后备,可以把默认资源放到一个没有后缀的文件中,可以使用下面的代码加载一个包:

      ResourceBundle resourceBundle = ResourceBundle.getBundle(bundleName, locale);

      getBundle 方法加载包的顺序如下:

      1. 包名_当前locale的语言_当前locale的国家_当前locale的变量
      2. 包名_当前locale的语言_当前locale的国家
      3. 包名_当前locale的语言
      4. 包名_默认locale的语言_默认locale的国家_默认locale的变量
      5. 包名_默认locale的语言_默认locale的国家
      6. 包名_默认locale的语言
      7. 包名
      8. 抛出 MissingResourceException 异常

      一旦getBundle 方法定位了一个包,比如,"包名_zh_CN" ,他还会继续查找"包名_zh""包名"这二个包,如果这些包也存在,他们在资源层次中就称为了"包名_zh_CN"的父包,以后查找资源的时候,如果在当前包中没有找到,就会去查找其父包。

      1. 属性文件

        属性文件是为了提供字符串资源常用的文件,每行存放一个键-值对的文本文件,比如,MyProgramStrings.properties 就是一个属性文件,存储属性文件都是ASCII文件,然后可以使用 native2ascii 工具来产生MyProgramStrings_语言_国家.properties 文件,示例如下:

        native2ascii MyProgramStrings.properties MyProgramStrings_zh_CN.properties

        要查找一个具体的字符串,可以调用:

        String resourceText= resourceBundle.getString("show.text");

        示例资源文件内容:

        show.text=u8fd9u4e2au662fu8d44u6e90u6587u4ef6u7684u5185u5bb9

      2. 包类

        为了提供字符串以外的资源,需要定义类,必须继承 ResourceBundle 类(简单的方法是继承 ListResourceBundle类),应该使用标准的命名规则来命名类,比如:

        MyProgramResource.java

        MyProgramResource_zh.java

        MyProgramResource_zh_CN.java

        可以使用与加载属性文件相同的 getBundle 方法来加载这个类,其 bundleName 资源包类的完整命名(包名和类名):

        ResourceBundle resourceBundle1Class =

                                        ResourceBundle.getBundle("locale.MyProgramResource", Locale.SIMPLIFIED_CHINESE);

        要查找一个字符串或其他类型资源可以调用:

        resourceText = resourceBundle1Class.getString("resourceClass.show.text");

        Object resourceObj = resourceBundle1Class.getObject("resourceClass.show.obj");

        如果一个Key同时存在属性文件和包类,则包类的优先。

           

  • 相关阅读:
    codeforces 1249D1/D2 Too Many Segments (贪心)
    codeforces 1244E Minimizing Difference (贪心)
    codeforces 1249C1 + 1249C2 (贪心)
    codeforces 1251D Salary Changing (二分+贪心)
    codeforces 1244C (思维 or 扩展欧几里得)
    P1040 加分二叉树(区间DP)
    POJ 1015 Jury Compromise (记录路径的背包问题)
    混合背包(单调队列优化多重背包)(模板)
    GAMES101系列笔记一 图形学概述与线性代数入门
    力扣514 自由之路
  • 原文地址:https://www.cnblogs.com/li3807/p/6917052.html
Copyright © 2020-2023  润新知