Java 中文官方教程 2022 版(二十九)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

BCP 47 扩展

原文:docs.oracle.com/javase/tutorial/i18n/locale/extensions.html

Java SE 7 版本符合 IETF BCP 47 标准,支持向Locale添加扩展。任何单个字符都可以用于表示扩展,但有两个预定义的扩展代码:'u'指定Unicode 语言环境扩展'x'指定私有使用扩展

Unicode 语言环境扩展由 Unicode通用语言环境数据存储库(CLDR)项目定义。它们用于指定非语言特定的信息,例如日历或货币。私有使用扩展可用于指定任何其他信息,例如平台(例如 Windows、UNIX 或 Linux)或发布信息(例如 6u23 或 JDK 7)。

扩展被指定为键/值对,其中键是单个字符(通常为'u''x')。格式良好的值具有以下格式:

SUBTAG ('-' SUBTAG)*

在这种格式中:

SUBTAG = [0-9a-zA-Z]{2,8}    *For key='u'*
SUBTAG = [0-9a-zA-Z]{1,8}    *For key='x'*

请注意,私有使用扩展允许单个字符值。但是,Unicode 语言环境扩展中的值至少需要 2 个字符。

扩展字符串不区分大小写,但Locale类将所有键和值映射为小写。

getExtensionKeys()方法返回Locale的(如果有的话)扩展键集。getExtension(key)方法返回指定键的值字符串(如果有)。

Unicode 语言环境扩展

如前所述,Unicode 语言环境扩展由'u'键代码或UNICODE_LOCALE_EXTENSION常量指定。值本身也由键/类型对指定。合法值在键/类型定义表中在Unicode网站上定义。键代码由两个字母字符指定。以下表列出了 Unicode 语言环境扩展键:

键代码描述
ca日历算法
co排序类型
ka排序参数
cu货币类型
nu数字类型
va通用变体类型

注意:

指定 Unicode 语言环境扩展,例如数字格式,并不保证底层平台的语言环境服务会遵守该请求。


以下表格显示了 Unicode 语言环境扩展的一些键/类型对示例。

键/类型对描述
ca-buddhist泰国佛教日历
co-pinyin拉丁文拼音排序
cu-usd美元
nu-jpanfin日本金融数字
tz-aldavEurope/Andorra

以下字符串代表德国语言区域设置,使用 Linux 平台的电话簿样式排序。此示例还包含一个名为"email"的属性。

de-DE-u-email-co-phonebk-x-linux

可以使用以下Locale方法访问有关 Unicode 区域扩展的信息。这些方法使用前面的德语区域示例进行描述。

  • getUnicodeLocaleKeys() – 返回 Unicode 区域键代码或空集(如果区域设置没有)。对德语示例,这将返回一个包含单个字符串"co"的集合。

  • getUnicodeLocaleType(String) – 返回与 Unicode 区域键代码关联的 Unicode 区域类型。对德语示例调用getUnicodeLocaleType("co")将返回字符串"phonebk"

  • getUnicodeLocaleAttributes() – 返回与此区域设置关联的 Unicode 区域设置属性集(如果有的话)。在德语示例中,这将返回一个包含单个字符串"email"的集合。

私有使用扩展

私有使用扩展,由'x'键代码或PRIVATE_USE_EXTENSION常量指定,可以是任何值,只要值格式正确即可。

以下是可能的私有使用扩展示例:

x-jdk-1-7
x-linux

可用区域设置的识别

原文:docs.oracle.com/javase/tutorial/i18n/locale/identify.html

您可以使用任何有效的语言和国家代码组合创建Locale,但这并不意味着您可以使用它。请记住,Locale对象只是一个标识符。您将Locale对象传递给其他对象,然后这些对象才会执行实际工作。这些其他对象,我们称之为区域敏感对象,不知道如何处理所有可能的Locale定义。

要找出区域敏感类识别的哪些类型的Locale定义,您可以调用getAvailableLocales方法。例如,要找出DateFormat类支持哪些Locale定义,您可以编写以下类似的例程:

import java.util.*;
import java.text.*;

public class Available {
    static public void main(String[] args) {
        Locale list[] = DateFormat.getAvailableLocales();
        for (Locale aLocale : list) {
            System.out.println(aLocale.toString());
        }
    }
}

请注意,toString返回的String包含由下划线分隔的语言和国家代码:

ar_EG
be_BY
bg_BG
ca_ES
cs_CZ
da_DK
de_DE
...

如果您想向最终用户显示Locale名称列表,您应该向他们显示比toString返回的语言和国家代码更容易理解的内容。相反,您可以调用Locale.getDisplayName方法,该方法检索Locale对象的本地化String。例如,当在前面的代码中用getDisplayName替换toString时,程序会打印以下行:

Arabic (Egypt)
Belarussian (Belarus)
Bulgarian (Bulgaria)
Catalan (Spain)
Czech (Czech Republic)
Danish (Denmark)
German (Germany)
...

根据 Java 平台的不同实现,您可能会看到不同的区域设置列表。

语言标签过滤和查找

原文:docs.oracle.com/javase/tutorial/i18n/locale/matching.html

Java 编程语言包含对语言标签、语言标签过滤和语言标签查找的国际化支持。这些功能由IETF BCP 47规定,其中包括RFC 5646“用于标识语言的标签”RFC 4647“语言标签的匹配”。本课程描述了 JDK 中提供这种支持的方式。

什么是语言标签?

语言标签是专门格式化的字符串,提供有关特定语言的信息。语言标签可能是简单的(如“en”表示英语)、复杂的(如“zh-cmn-Hans-CN”表示中国普通话,简体字,用于中国)或介于两者之间的(如“sr-Latn”,表示使用拉丁文写的塞尔维亚语)。语言标签由连字符分隔的“子标签”组成;这个术语在整个 API 文档中使用。

java.util.Locale类提供对语言标签的支持。Locale包含几个不同的字段:语言(如“en”表示英语,或“ja”表示日语)、脚本(如“Latn”表示拉丁文或“Cyrl”表示西里尔文)、国家(如“US”表示美国或“FR”表示法国)、变体(指示区域的某些变体)和扩展(提供单字符键到String值的映射,表示除语言标识之外的扩展)。要从语言标签String创建Locale对象,请调用Locale.forLanguageTag(String),将语言标签作为唯一参数传入。这样做会创建并返回一个新的Locale对象,供应用程序使用。

示例 1:

package languagetagdemo;

import java.util.Locale;

public class LanguageTagDemo {
     public static void main(String[] args) {
         Locale l = Locale.forLanguageTag("en-US");
     }
}

请注意,Locale API 只要求您的语言标签在语法上是格式良好的。它不执行任何额外的验证(例如检查标签是否在 IANA 语言子标签注册表中注册)。

什么是语言范围?

语言范围(由类java.util.Locale.LanguageRange表示)标识具有特定属性的语言标签集。语言范围分为基本和扩展两类,类似于语言标签,由连字符分隔的子标签组成。基本语言范围的示例包括“en”(英语)、“ja-JP”(日语,日本)和“”(特殊语言范围,匹配任何语言标签)。扩展语言范围的示例包括“-CH”(任何语言,瑞士)、“es-”(西班牙语,任何地区)和“zh-Hant-”(繁体中文,任何地区)。

此外,语言范围可以存储在语言优先级列表中,这使用户可以在加权列表中优先考虑他们的语言偏好。语言优先级列表通过将LanguageRange对象放入java.util.List中来表示,然后可以将其传递给接受ListLanguageRange对象的Locale方法。

创建语言范围

Locale.LanguageRange类提供了两种不同的构造函数来创建语言范围:

  • public Locale.LanguageRange(String range)

  • public Locale.LanguageRange(String range, double weight)

它们之间唯一的区别是第二个版本允许指定权重;如果将范围放入语言优先级列表中,则将考虑此权重。

Locale.LanguageRange还指定了一些常量,用于与这些构造函数一起使用:

  • public static final double MAX_WEIGHT

  • public static final double MIN_WEIGHT

MAX_WEIGHT常量保存值 1.0,表示它非常适合用户。MIN_WEIGHT常量保存值 0.0,表示它不适合。

示例 2:

package languagetagdemo;

import java.util.Locale;

public class LanguageTagDemo {

     public static void main(String[] args) {
         // Create Locale
         Locale l = Locale.forLanguageTag("en-US");

         // Define Some LanguageRange Objects
         Locale.LanguageRange range1 = new Locale.LanguageRange("en-US",Locale.LanguageRange.MAX_WEIGHT);
         Locale.LanguageRange range2 = new Locale.LanguageRange("en-GB*",0.5);
         Locale.LanguageRange range3 = new Locale.LanguageRange("fr-FR",Locale.LanguageRange.MIN_WEIGHT);
     }
}

示例 2 创建了三种语言范围:英语(美国)、英语(英国)和法语(法国)。这些范围被加权以表达用户的偏好,从最喜欢到最不喜欢的顺序。

创建语言优先级列表

您可以使用LanguageRange.parse(String)方法从语言范围列表创建语言优先级列表。此方法接受一个逗号分隔的语言范围列表,在给定范围中对每个语言范围执行语法检查,然后返回新创建的语言优先级列表。

有关“ranges”参数所需格式的详细信息,请参阅此方法的 API 规范。

示例 3:

package languagetagdemo;

import java.util.Locale;

import java.util.List;

public class LanguageTagDemo {

    public static void main(String[] args) {

        // Create Locale

        Locale l = Locale.forLanguageTag("en-US");

        // Create a Language Priority List

        String ranges = "en-US;q=1.0,en-GB;q=0.5,fr-FR;q=0.0";

        List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges)

    }
}

示例 3 创建了与示例 2 相同的三种语言范围,但将它们存储在一个String对象中,该对象被传递给parse(String)方法。返回的LanguageRange对象的List是语言优先级列表。

过滤语言标签

语言标签过滤是将一组语言标签与用户的语言优先级列表进行匹配的过程。过滤的结果将是所有匹配结果的完整列表。Locale类定义了两个过滤方法,它们返回一个Locale对象的列表。它们的签名如下:

在这两种方法中,第一个参数指定了用户的语言优先级列表,如前一节所述。

第二个参数指定要匹配的Locale对象的Collection。匹配本身将根据 RFC 4647 指定的规则进行。

第三个参数(如果提供)指定要使用的“过滤模式”。Locale.FilteringMode枚举提供了许多不同的值可供选择,例如AUTOSELECT_FILTERING(用于基本语言范围过滤)或EXTENDED_FILTERING(用于扩展语言范围过滤)。

示例 4 演示了语言标记过滤。

示例 4:

package languagetagdemo;

import java.util.Locale;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;

public class LanguageTagDemo {

    public static void main(String[] args) {

        // Create a collection of Locale objects to filter
        Collection<Locale> locales = new ArrayList<>();
        locales.add(Locale.forLanguageTag("en-GB"));
        locales.add(Locale.forLanguageTag("ja"));
        locales.add(Locale.forLanguageTag("zh-cmn-Hans-CN"));
        locales.add(Locale.forLanguageTag("en-US"));

        // Express the user's preferences with a Language Priority List
        String ranges = "en-US;q=1.0,en-GB;q=0.5,fr-FR;q=0.0";
        List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges);

        // Now filter the Locale objects, returning any matches
        List<Locale> results = Locale.filter(languageRanges,locales);

        // Print out the matches
        for(Locale l : results){
        System.out.println(l.toString());
        }
    }
}

该程序的输出是:

en_US

en_GB

返回的列表根据用户的语言优先级列表中指定的权重进行排序。

Locale类还定义了filterTags方法,用于将语言标记过滤为String对象。

方法签名如下:

示例 5 提供了与示例 4 相同的搜索,但使用了String对象而不是Locale对象。

示例 5:

package languagetagdemo;

import java.util.Locale;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;

public class LanguageTagDemo {

    public static void main(String[] args) {

        // Create a collection of String objects to match against
        Collection<String> tags = new ArrayList<>();
        tags.add("en-GB");
        tags.add("ja");
        tags.add("zh-cmn-Hans-CN");
        tags.add("en-US");

        // Express user's preferences with a Language Priority List
        String ranges = "en-US;q=1.0,en-GB;q=0.5,fr-FR;q=0.0";
        List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges);

        // Now search the locales for the best match
        List<String> results = Locale.filterTags(languageRanges,tags);

        // Print out the matches
        for(String s : results){
            System.out.println(s);
        }
    }
} 

与以前一样,搜索将匹配并返回“en-US”和“en-GB”(按顺序)。

执行语言标记查找

与语言标记过滤相反,语言标记查找是将语言范围与语言标记集匹配并返回最佳匹配的语言标记的过程。RFC4647 规定:“查找从可用标记列表中产生最佳匹配用户偏好的单个结果,因此在需要单个项目的情况下很有用(并且只能返回一个项目)。例如,如果一个过程要将可读的错误消息插入协议头部,它可能会根据用户的语言优先级列表选择文本。由于该过程只能返回一个项目,因此必须选择一个项目并返回某个项目,即使内容的语言标记都不匹配用户提供的语言优先级列表。”

示例 6:

package languagetagdemo;

import java.util.Locale;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;

public class LanguageTagDemo {

    public static void main(String[] args) {

        // Create a collection of Locale objects to search
        Collection<Locale> locales = new ArrayList<>();
        locales.add(Locale.forLanguageTag("en-GB"));
        locales.add(Locale.forLanguageTag("ja"));
        locales.add(Locale.forLanguageTag("zh-cmn-Hans-CN"));
        locales.add(Locale.forLanguageTag("en-US"));

        // Express the user's preferences with a Language Priority List
        String ranges = "en-US;q=1.0,en-GB;q=0.5,fr-FR;q=0.0";
        List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges);

        // Find the BEST match, and return just one result
        Locale result = Locale.lookup(languageRanges,locales);
        System.out.println(result.toString());
    }
}

与过滤示例相比,在示例 6 中的查找演示返回最佳匹配的一个对象(在这种情况下是en-US)。为了完整起见,示例 7 展示了如何使用String对象执行相同的查找。

示例 7:

package languagetagdemo;

import java.util.Locale;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;

public class LanguageTagDemo {

    public static void main(String[] args) {
        // Create a collection of String objects to match against
        Collection<String> tags = new ArrayList<>();
        tags.add("en-GB");
        tags.add("ja");
        tags.add("zh-cmn-Hans-CN");
        tags.add("en-US");

        // Express user's preferences with a Language Priority List
        String ranges = "en-US;q=1.0,en-GB;q=0.5,fr-FR;q=0.0";
        List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges);

        // Find the BEST match, and return just one result
        String result = Locale.lookupTag(languageRanges, tags);
        System.out.println(result);
    }
} 

这个示例返回与用户的语言优先级列表最匹配的单个对象。

区域的范围

原文:docs.oracle.com/javase/tutorial/i18n/locale/scope.html

Java 平台不要求您在整个程序中使用相同的Locale。如果愿意,您可以为程序中的每个区域敏感对象分配不同的Locale。这种灵活性使您能够开发多语言应用程序,可以在多种语言中显示信息。

然而,大多数应用程序不是多语言的,它们的区域敏感对象依赖于默认的Locale。当 Java 虚拟机启动时由其设置,默认的Locale对应于主机平台的区域设置。要确定您的 Java 虚拟机的默认Locale,请调用Locale.getDefault方法。


注意:

可以独立设置两种用途的默认区域设置:format设置用于格式化资源,display设置用于菜单和对话框。在 Java SE 7 版本中引入的Locale.getDefault(Locale.Category)方法接受一个Locale.Category参数。将FORMAT枚举传递给getDefault(Locale.Category)方法会返回用于格式化资源的默认区域设置。类似地,传递DISPLAY枚举会返回 UI 使用的默认区域设置。相应的setDefault(Locale.Category, Locale)方法允许设置所需类别的区域设置。无参数的getDefault方法返回DISPLAY的默认值。

在 Windows 平台上,这些默认值根据 Windows 控制面板中的“标准和格式”和“显示语言”设置进行初始化。


你不应该通过编程方式设置默认的Locale,因为它被所有区域敏感类共享。

分布式计算引发了一些有趣的问题。例如,假设您正在设计一个应用服务器,该服务器将接收来自各个国家的客户端的请求。如果每个客户端的Locale不同,那么服务器的Locale应该是什么?也许服务器是多线程的,每个线程设置为服务的客户端的Locale。或者也许服务器和客户端之间传递的所有数据都应该是与区域设置无关的。

你应该采取哪种设计方法?如果可能的话,服务器和客户端之间传递的数据应该是与地区无关的。这样可以简化服务器的设计,让客户端负责以地区敏感的方式显示数据。然而,如果服务器必须以特定地区的形式存储数据,这种方法就行不通了。例如,服务器可能会在不同的数据库列中存储相同数据的西班牙语、英语和法语版本。在这种情况下,服务器可能希望查询客户端的Locale,因为Locale可能在上次请求之后发生了变化。

区域敏感服务 SPI

原文:docs.oracle.com/javase/tutorial/i18n/locale/services.html

此功能使得可以插入依赖于区域的数据和服务。通过这种方式,第三方能够提供 java.textjava.util 包中大多数区域敏感类的实现。

SPIs (服务提供者接口) 的实现基于由服务提供者实现的抽象类和 Java 接口。在运行时,Java 类加载机制用于动态定位和加载实现 SPI 的类。

您可以使用区域敏感的服务 SPI 来提供以下区域敏感的实现:

  • BreakIterator 对象

  • Collator 对象

  • Locale 类的语言代码、国家代码和变体名称

  • 时区名称

  • 货币符号

  • DateFormat 对象

  • DateFormatSymbol 对象

  • NumberFormat 对象

  • DecimalFormatSymbols 对象

相应的 SPI 包含在 java.text.spijava.util.spi 包中:

java.util.spijava.text.spi

|

  • CurrencyNameProvider

  • LocaleServiceProvider

  • TimeZoneNameProvider

  • CalendarDataProvider

|

  • BreakIteratorProvider

  • CollatorProvider

  • DateFormatProvider

  • DateFormatSymbolsProvider

  • DecimalFormatSymbolsProvider

  • NumberFormatProvider

|

例如,如果您想为新的区域提供一个 NumberFormat 对象,您必须实现 java.text.spi.NumberFormatProvider 类。您需要扩展此类并实现其方法:

  • getCurrencyInstance(Locale locale)

  • getIntegerInstance(Locale locale)

  • getNumberInstance(Locale locale)

  • getPercentInstance(Locale locale)

Locale loc = new Locale("da", "DK");
NumberFormat nf = NumberFormatProvider.getNumberInstance(loc);

这些方法首先检查 Java 运行时环境是否支持请求的区域;如果支持,则使用该支持。否则,方法调用已安装的提供程序的适当接口的 getAvailableLocales() 方法,以找到支持请求的区域的提供程序。

课程:隔离特定于区域设置的数据

原文:docs.oracle.com/javase/tutorial/i18n/resbundle/index.html

特定于区域设置的数据必须根据最终用户语言和地区的惯例进行定制。用户界面显示的文本是最明显的区域设置数据示例。例如,在美国的应用程序中,取消按钮将在德国显示为 “Abbrechen” 按钮。在其他国家,此按钮将具有其他标签。显然,您不希望硬编码此按钮标签。如果您可以自动获取给定 Locale 的正确标签,那不是很好吗?幸运的是,只要将特定于区域设置的对象隔离在 ResourceBundle 中,您就可以做到这一点。

在本课程中,您将学习如何创建和访问 ResourceBundle 对象。如果您急于查看一些编码示例,请继续查看本课程中的最后两节。然后您可以回到前两节获取有关 ResourceBundle 对象的一些概念信息。

关于 ResourceBundle 类

ResourceBundle 对象包含特定于区域设置的对象。当您需要特定于区域设置的对象时,您可以从 ResourceBundle 中获取,它会返回与最终用户的 Locale 匹配的对象。本节解释了 ResourceBundleLocale 的关系,并描述了 ResourceBundle 的子类。

准备使用 ResourceBundle

在创建 ResourceBundle 对象之前,您应该进行一些规划。首先,识别程序中特定于区域设置的对象。然后将它们组织成类别,并根据不同的类别存储在不同的 ResourceBundle 对象中。

使用属性文件支持 ResourceBundle

如果您的应用程序包含需要翻译成各种语言的 String 对象,您可以将这些 String 对象存储在 PropertyResourceBundle 中,该对象由一组属性文件支持。由于属性文件是简单的文本文件,可以由翻译人员创建和维护。您无需更改源代码。在本节中,您将学习如何设置支持 PropertyResourceBundle 的属性文件。

使用 ListResourceBundle

ListResourceBundle 类是 ResourceBundle 的子类,使用列表管理特定于区域设置的对象。ListResourceBundle 由一个类文件支持,这意味着每次需要支持额外的 Locale 时,您必须编写和编译一个新的源文件。但是,ListResourceBundle 对象很有用,因为与属性文件不同,它们可以存储任何类型的特定于区域设置的对象。通过逐步执行示例程序,本节演示了如何使用 ListResourceBundle

自定义 ResourceBundle 加载

本节代表了改进ResourceBundle.getBundle工厂灵活性的新功能。ResourceBundle.Control类与加载资源包的工厂方法合作。这允许将资源包加载过程的每个重要步骤及其缓存控制视为单独的方法。

关于 ResourceBundle 类

原文:docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html

如何 ResourceBundle 与 Locale 相关联

从概念上讲,每个 ResourceBundle 都是一组相关的子类,共享相同的基本名称。以下列表显示了一组相关的子类。ButtonLabel 是基本名称。基本名称后面的字符表示 Locale 的语言代码、国家代码和变体。例如,ButtonLabel_en_GB 匹配由英语语言代码 (en) 和英国国家代码 (GB) 指定的 Locale

ButtonLabel
ButtonLabel_de
ButtonLabel_en_GB
ButtonLabel_fr_CA_UNIX

要选择适当的 ResourceBundle,请调用 ResourceBundle.getBundle 方法。以下示例选择与法语语言、加拿大国家和 UNIX 平台匹配的 ButtonLabel ResourceBundle

Locale currentLocale = new Locale("fr", "CA", "UNIX");
ResourceBundle introLabels = ResourceBundle.getBundle(
                                 "ButtonLabel", currentLocale);

如果指定 LocaleResourceBundle 类不存在,getBundle 将尝试找到最接近的匹配项。例如,如果期望的类是 ButtonLabel_fr_CA_UNIX,默认 Localeen_USgetBundle 将按以下顺序查找类:

ButtonLabel_fr_CA_UNIX
ButtonLabel_fr_CA
ButtonLabel_fr
ButtonLabel_en_US
ButtonLabel_en
ButtonLabel

请注意,在选择基类(ButtonLabel)之前,getBundle 会根据默认 Locale 查找类。如果 getBundle 在前述类列表中找不到匹配项,则会抛出 MissingResourceException。为避免抛出此异常,您应始终提供没有后缀的基类。

ListResourceBundle 和 PropertyResourceBundle 子类

抽象类 ResourceBundle 有两个子类:PropertyResourceBundleListResourceBundle

PropertyResourceBundle 由属性文件支持。属性文件是包含可翻译文本的纯文本文件。属性文件不是 Java 源代码的一部分,它们只能包含 String 对象的值。如果需要存储其他类型的对象,请改用 ListResourceBundle。章节 使用属性文件支持 ResourceBundle 展示了如何使用 PropertyResourceBundle

ListResourceBundle 类使用方便的列表管理资源。每个 ListResourceBundle 都由一个类文件支持。您可以在 ListResourceBundle 中存储任何特定于区域设置的对象。要为其他 Locale 添加支持,您需要创建另一个源文件并将其编译为类文件。章节 使用 ListResourceBundle 中有一个您可能会发现有用的编码示例。

ResourceBundle 类是灵活的。如果您首先将特定于区域设置的 String 对象放入 PropertyResourceBundle 中,然后稍后决定改用 ListResourceBundle,则对您的代码没有影响。例如,对 getBundle 的以下调用将检索适当 LocaleResourceBundle,无论 ButtonLabel 是由类支持还是由属性文件支持:

ResourceBundle introLabels = ResourceBundle.getBundle(
                                 "ButtonLabel", currentLocale);

键-值对

ResourceBundle对象包含一组键值对。当您想要从ResourceBundle中检索值时,您需要指定键,该键必须是一个String。该值是特定于区域设置的对象。以下示例中的键是OkKeyCancelKey字符串:

class ButtonLabel_en extends ListResourceBundle {
    // English version
    public Object[][] getContents() {
        return contents;
    }
    static final Object[][] contents = {
        {"OkKey", "OK"},
        {"CancelKey", "Cancel"},
    };
}

要从ResourceBundle中检索OK String,您需要在调用getString时指定适当的键:

String okLabel = ButtonLabel.getString("OkKey");

属性文件包含键值对。键位于等号的左侧,值位于右侧。每对位于单独的一行。值只能表示String对象。以下示例显示了名为ButtonLabel.properties的属性文件的内容:

OkKey = OK
CancelKey = Cancel

准备使用 ResourceBundle

原文:docs.oracle.com/javase/tutorial/i18n/resbundle/prepare.html

识别与地区相关的对象

如果你的应用程序有用户界面,它包含许多与地区相关的对象。要开始,你应该查看源代码,寻找随Locale变化的对象。你的列表可能包括从以下类实例化的对象:

  • 字符串

  • 图像

  • 颜色

  • AudioClip

你会注意到这个列表不包含代表数字、日期、时间或货币的对象。这些对象的显示格式随Locale变化,但对象本身不会变化。例如,你根据Locale格式化一个Date,但无论Locale如何,你都使用相同的Date对象。你不需要将这些对象隔离在ResourceBundle中,而是使用特殊的区域敏感格式化类对它们进行格式化。你将在日期和时间部分的格式化课程中学习如何做到这一点。

通常情况下,存储在ResourceBundle中的对象是预定义的,并随产品一起提供。这些对象在程序运行时不会被修改。例如,你应该将Menu标签存储在ResourceBundle中,因为它是与地区相关的,在程序会话期间不会更改。然而,你不应该将用户在TextField中输入的String对象隔离在ResourceBundle中。这样的String数据可能每天都会有所变化。它是特定于程序会话的,而不是程序运行的Locale

通常,你需要在ResourceBundle中隔离的大多数对象都是String对象。然而,并非所有的String对象都是与地区相关的。例如,如果一个String是进程间通信使用的协议元素,它就不需要本地化,因为最终用户永远不会看到它。

是否本地化某些String对象的决定并不总是明确的。日志文件是一个很好的例子。如果一个日志文件由一个程序编写并由另一个程序读取,那么两个程序都将使用日志文件作为通信的缓冲区。假设最终用户偶尔检查此日志文件的内容。那么日志文件应该本地化吗?另一方面,如果最终用户很少检查日志文件,则翻译的成本可能不值得。你是否本地化此日志文件的决定取决于许多因素:程序设计、易用性、翻译成本和可支持性。

组织 ResourceBundle 对象

你可以根据包含的对象的类别组织你的ResourceBundle对象。例如,你可能希望将订单输入窗口的所有 GUI 标签加载到一个名为OrderLabelsBundleResourceBundle中。使用多个ResourceBundle对象有几个优点:

  • 你的代码更易于阅读和维护。

  • 你将避免巨大的ResourceBundle对象,这可能需要太长时间加载到内存中。

  • 当需要时,您可以通过仅加载每个ResourceBundle来减少内存使用量。

使用属性文件支持 ResourceBundle

原文:docs.oracle.com/javase/tutorial/i18n/resbundle/propfile.html

本节将逐步介绍一个名为PropertiesDemo的示例程序。

1. 创建默认属性文件

属性文件是一个简单的文本文件。您可以使用几乎任何文本编辑器创建和维护属性文件。

您应该始终创建一个默认属性文件。此文件的名称以您的ResourceBundle的基本名称开头,并以.properties后缀结尾。在PropertiesDemo程序中,基本名称为LabelsBundle。因此,默认属性文件称为LabelsBundle.properties。此文件包含以下行:

# This is the default LabelsBundle.properties file
s1 = computer
s2 = disk
s3 = monitor
s4 = keyboard

请注意,在前面的文件中,注释行以井号(#)开头。其他行包含键值对。键位于等号的左侧,值位于右侧。例如,s2是对应于值disk的键。键是任意的。我们可以将s2命名为其他名称,比如msg5diskID。但一旦定义,键就不应更改,因为它在源代码中被引用。值可以更改。实际上,当您的本地化人员创建新的属性文件以适应其他语言时,他们将把值翻译成各种语言。

2. 根据需要创建其他属性文件

要支持额外的Locale,您的本地化人员将创建一个包含翻译值的新属性文件。不需要更改源代码,因为您的程序引用键,而不是值。

例如,要添加对德语的支持,您的本地化人员将翻译LabelsBundle.properties中的值,并将其放入名为LabelsBundle_de.properties的文件中。请注意,此文件的名称与默认文件的名称相同,以基本名称LabelsBundle开头,并以.properties后缀结尾。但是,由于此文件用于特定的Locale,因此基本名称后面跟着语言代码(de)。LabelsBundle_de.properties的内容如下:

# This is the LabelsBundle_de.properties file
s1 = Computer
s2 = Platte
s3 = Monitor
s4 = Tastatur

PropertiesDemo示例程序附带三个属性文件:

LabelsBundle.properties
LabelsBundle_de.properties
LabelsBundle_fr.properties

3. 指定 Locale

PropertiesDemo程序创建Locale对象如下:

Locale[] supportedLocales = {
    Locale.FRENCH,
    Locale.GERMAN,
    Locale.ENGLISH
};

这些Locale对象应该与前两个步骤中创建的属性文件相匹配。例如,Locale.FRENCH对象对应于LabelsBundle_fr.properties文件。Locale.ENGLISH没有匹配的LabelsBundle_en.properties文件,因此将使用默认文件。

4. 创建 ResourceBundle

此步骤展示了Locale、属性文件和ResourceBundle之间的关系。要创建ResourceBundle,请调用getBundle方法,指定基本名称和Locale

ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", currentLocale);

getBundle方法首先查找与基本名称和Locale匹配的类文件。如果找不到类文件,则会检查属性文件。在PropertiesDemo程序中,我们使用属性文件而不是类文件来支持ResourceBundle。当getBundle方法找到正确的属性文件时,它会返回一个包含属性文件中键值对的PropertyResourceBundle对象。

5. 获取本地化文本

要从ResourceBundle中检索翻译后的值,请按照以下方式调用getString方法:

String value = labels.getString(key);

getString返回的String对应指定的键。只要为指定的Locale存在属性文件,该String就是正确的语言。

6. 遍历所有键

这一步是可选的。在调试程序时,您可能希望获取ResourceBundle中所有键的值。getKeys方法返回ResourceBundle中所有键的Enumeration。您可以遍历Enumeration并使用getString方法获取每个值。以下代码片段来自PropertiesDemo程序,展示了如何实现这一点:

ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", currentLocale);
Enumeration bundleKeys = labels.getKeys();

while (bundleKeys.hasMoreElements()) {
    String key = (String)bundleKeys.nextElement();
    String value = labels.getString(key);
    System.out.println("key = " + key + ", " + "value = " + value);
}

7. 运行演示程序

运行PropertiesDemo程序会生成以下输出。前三行显示了对各种Locale对象调用getString返回的值。当使用getKeys方法遍历键时,程序会显示最后四行。

Locale = fr, key = s2, value = Disque dur
Locale = de, key = s2, value = Platte
Locale = en, key = s2, value = disk

key = s4, value = Clavier
key = s3, value = Moniteur
key = s2, value = Disque dur
key = s1, value = Ordinateur

使用 ListResourceBundle

原文:docs.oracle.com/javase/tutorial/i18n/resbundle/list.html

本节演示了使用 ListResourceBundle 对象的示例程序 ListDemo。接下来的文本解释了创建 ListDemo 程序所涉及的每个步骤,以及支持它的 ListResourceBundle 子类。

1. 创建 ListResourceBundle 子类

ListResourceBundle 由类文件支持。因此,第一步是为每个支持的 Locale 创建一个类文件。在 ListDemo 程序中,ListResourceBundle 的基本名称是 StatsBundle。由于 ListDemo 支持三个 Locale 对象,因此需要以下三个类文件:

StatsBundle_en_CA.class
StatsBundle_fr_FR.class
StatsBundle_ja_JP.class

为日本定义的 StatsBundle 类在接下来的源代码中定义。请注意,类名是通过将语言和国家代码附加到 ListResourceBundle 的基本名称构建的。在类内部,二维 contents 数组使用键值对进行初始化。键是每对中的第一个元素:GDPPopulationLiteracy。键必须是 String 对象,并且在 StatsBundle 集合中的每个类中必须相同。值可以是任何类型的对象。在此示例中,值是两个 Integer 对象和一个 Double 对象。

import java.util.*;
public class StatsBundle_ja_JP extends ListResourceBundle {
    public Object[][] getContents() {
        return contents;
    }

    private Object[][] contents = {
        { "GDP", new Integer(21300) },
        { "Population", new Integer(125449703) },
        { "Literacy", new Double(0.99) },
    };
}

2. 指定 Locale

ListDemo 程序如下定义 Locale 对象:

Locale[] supportedLocales = {
    new Locale("en", "CA"),
    new Locale("ja", "JP"),
    new Locale("fr", "FR")
};

每个 Locale 对象对应于一个 StatsBundle 类。例如,用 jaJP 代码定义的日语 LocaleStatsBundle_ja_JP.class 匹配。

3. 创建 ResourceBundle

要创建 ListResourceBundle,请调用 getBundle 方法。以下代码行指定了类的基本名称(StatsBundle)和 Locale

ResourceBundle stats = ResourceBundle.getBundle("StatsBundle", currentLocale);

getBundle 方法搜索以 StatsBundle 开头,后跟指定 Locale 的语言和国家代码的类。如果 currentLocale 是用 jaJP 代码创建的,getBundle 将返回与类 StatsBundle_ja_JP 对应的 ListResourceBundle,例如。

4. 获取本地化对象

现在程序有了适当 LocaleListResourceBundle,它可以通过其键获取本地化对象。以下代码行通过使用 Literacy 键参数调用 getObject 来检索识字率。由于 getObject 返回一个对象,请将其转换为 Double

Double lit = (Double)stats.getObject("Literacy");

5. 运行演示程序

ListDemo 程序打印了使用 getBundle 方法获取的数据:

Locale = en_CA
GDP = 24400
Population = 28802671
Literacy = 0.97

Locale = ja_JP
GDP = 21300
Population = 125449703
Literacy = 0.99

Locale = fr_FR
GDP = 20200
Population = 58317450
Literacy = 0.99

自定义资源包加载

原文:docs.oracle.com/javase/tutorial/i18n/resbundle/control.html

在本课程的前面,您已经学会了如何创建和访问ResourceBundle类的对象。本节扩展了您的知识,并解释了如何利用ResourceBundle.Control类的功能。

ResourceBundle.Control被创建用于指定如何定位和实例化资源包。它定义了一组回调方法,这些方法在ResourceBundle.getBundle工厂方法在加载资源包过程中调用。

与之前描述的ResourceBundle.getBundle方法不同,此ResourceBundle.getBundle方法使用指定的基本名称、默认区域设置和指定的控制定义资源包。

public static final ResourceBundle getBundle(
    String baseName,
    ResourceBundle.Control cont
    // ...

指定的控制提供了资源包加载过程的信息。

下面的示例程序RBControl.java说明了如何为中文区域定义自己的搜索路径。

1. 创建properties文件。

正如之前所述,您可以从类或properties文件中加载资源。这些文件包含以下区域的描述:

  • RBControl.properties – 全局

  • RBControl_zh.properties – 仅语言:简体中文

  • RBControl_zh_cn.properties – 仅区域:中国

  • RBControl_zh_hk.properties – 仅区域:香港

  • RBControl_zh_tw.properties – 台湾

在此示例中,应用程序为香港地区创建了一个新的区域设置。

2. 创建一个ResourceBundle实例。

与上一节中的示例一样,此应用程序通过调用getBundle方法创建了一个ResourceBundle实例:

private static void test(Locale locale) {
    ResourceBundle rb = ResourceBundle.getBundle(
                            "RBControl",
                            locale,
                            new ResourceBundle.Control() {
                                    // ...
                            }
                        );

getBundle方法搜索具有 RBControl 前缀的properties文件。然而,此方法包含一个Control参数,用于驱动搜索中文区域的过程。

3. 调用getCandidateLocales方法

getCandidateLocales方法返回一个候选区域的Locales对象列表,用于基本名称和区域设置。

new ResourceBundle.Control() {
    @Override
    public List<Locale> getCandidateLocales(
                            String baseName,
                            Locale locale) {
                // ...                                        
    }
}

默认实现返回以下Locale对象的列表:Locale(语言, 国家)。

然而,此方法被覆盖以实现以下特定行为:

if (baseName == null)
    throw new NullPointerException();

if (locale.equals(new Locale("zh", "HK"))) {
    return Arrays.asList(
               locale,
               Locale.TAIWAN,
               // no Locale.CHINESE here
               Locale.ROOT);
} else if (locale.equals(Locale.TAIWAN)) {
    return Arrays.asList(
               locale,
               // no Locale.CHINESE here
               Locale.ROOT);
}

注意,候选区域序列的最后一个元素必须是根区域。

4. 调用test

为以下四种不同的区域设置调用test类:

public static void main(String[] args) {
    test(Locale.CHINA);
    test(new Locale("zh", "HK"));
    test(Locale.TAIWAN);
    test(Locale.CANADA);
}

5. 运行示例程序

你将看到程序的输出如下:

locale: zh_CN
        region: China
        language: Simplified Chinese
locale: zh_HK
        region: Hong Kong
        language: Traditional Chinese
locale: zh_TW
        region: Taiwan
        language: Traditional Chinese
locale: en_CA
        region: global
        language: English

请注意,新创建的区域被分配为香港地区,因为在适当的properties文件中指定了。繁体中文被分配为台湾区域的语言。

ResourceBundle.Control类的另外两个有趣的方法没有在RBControl示例中使用,但值得一提。getTimeToLive方法用于确定资源包在缓存中可以存在多长时间。如果缓存中资源包的时间限制已过期,则调用needsReload方法来确定是否需要重新加载资源包。

教训:格式化

原文:docs.oracle.com/javase/tutorial/i18n/format/index.html

本课程解释了如何格式化数字、货币、日期、时间和文本消息。因为最终用户可以看到这些数据元素,它们的格式必须符合各种文化习俗。遵循本课程中的示例将教会您如何:

  • 以区域设置敏感的方式格式化数据元素

  • 保持您的代码与区域设置无关

  • 避免为特定区域编写格式化程序的需要

数字和货币

本节解释了如何使用NumberFormatDecimalFormatDecimalFormatSymbols类。

日期和时间


版本说明: 本日期和时间部分使用了java.util包中的日期和时间 API。在 JDK 8 发布的java.timeAPI 中,提供了一个全面的日期和时间模型,相比java.util类有显著的改进。java.timeAPI 在日期时间教程中有详细描述。旧版日期时间代码页面可能会引起特别关注。


本节重点介绍了DateFormatSimpleDateFormatDateFormatSymbols类。

消息

本节展示了MessageFormatChoiceFormat类如何帮助您解决在格式化文本消息时可能遇到的一些问题。

数字和货币

原文:docs.oracle.com/javase/tutorial/i18n/format/numberintro.html

程序以与语言环境无关的方式存储和操作数字。在显示或打印数字之前,程序必须将其转换为符合语言环境的String格式。例如,在法国,数字 123456.78 应格式化为 123 456,78,在德国应显示为 123.456,78。在本节中,您将学习如何使您的程序独立于语言环境的小数点、千位分隔符和其他格式化属性的约定。

使用预定义格式

使用NumberFormat类提供的工厂方法,您可以获得针对数字、货币和百分比的特定于语言环境的格式。

使用模式进行格式化

使用DecimalFormat类,您可以使用String模式指定数字的格式。DecimalFormatSymbols类允许您修改格式化符号,如小数分隔符和减号。

使用预定义格式

原文:docs.oracle.com/javase/tutorial/i18n/format/numberFormat.html

通过调用NumberFormat类提供的方法,你可以根据Locale格式化数字、货币和百分比。接下来的内容演示了如何使用一个名为NumberFormatDemo.java的示例程序进行格式化技术。

数字

你可以使用NumberFormat方法来格式化原始类型数字,比如double,以及它们对应的包装对象,比如Double

以下代码示例根据Locale格式化一个Double。调用getNumberInstance方法返回一个特定于语言环境的NumberFormat实例。format方法接受Double作为参数,并以String形式返回格式化的数字。

static public void displayNumber(Locale currentLocale) {

    Integer quantity = new Integer(123456);
    Double amount = new Double(345987.246);
    NumberFormat numberFormatter;
    String quantityOut;
    String amountOut;

    numberFormatter = NumberFormat.getNumberInstance(currentLocale);
    quantityOut = numberFormatter.format(quantity);
    amountOut = numberFormatter.format(amount);
    System.out.println(quantityOut + "   " + currentLocale.toString());
    System.out.println(amountOut + "   " + currentLocale.toString());
}

这个例子打印如下内容;它展示了相同数字的格式如何随着Locale的不同而变化:

123 456   fr_FR
345 987,246   fr_FR
123.456   de_DE
345.987,246   de_DE
123,456   en_US
345,987.246   en_US

使用阿拉伯数字以外的数字形状

默认情况下,当文本包含数字值时,这些值将使用阿拉伯数字显示。如果希望使用其他 Unicode 数字形状,请使用java.awt.font.NumericShaper类。NumericShaper API 使您能够以任何 Unicode 数字形状显示作为 ASCII 值内部表示的数字值。有关更多信息,请参见将拉丁数字转换为其他 Unicode 数字。

此外,一些语言环境具有变体代码,指定使用 Unicode 数字形状代替阿拉伯数字,比如泰语的语言环境。有关更多信息,请参见创建语言环境中的变体代码部分。

货币

如果您正在编写业务应用程序,可能需要格式化和显示货币。您可以像格式化数字一样格式化货币,只是调用getCurrencyInstance来创建格式化程序。当调用format方法时,它会返回一个包含格式化数字和适当货币符号的String

此代码示例展示了如何以区域特定的方式格式化货币:

static public void displayCurrency( Locale currentLocale) {

    Double currencyAmount = new Double(9876543.21);
    Currency currentCurrency = Currency.getInstance(currentLocale);
    NumberFormat currencyFormatter = 
        NumberFormat.getCurrencyInstance(currentLocale);

    System.out.println(
        currentLocale.getDisplayName() + ", " +
        currentCurrency.getDisplayName() + ": " +
        currencyFormatter.format(currencyAmount));
}

由上述代码生成的输出如下所示:

French (France), Euro: 9 876 543,21German (Germany), Euro: 9.876.543,21English (United States), US Dollar: $9,876,543.21

乍一看,这个输出可能看起来对您来说是错误的,因为数字值都是相同的。当然,9 876 543,21 €不等同于$9,876,543.21。然而,请记住,NumberFormat类不知道汇率。属于NumberFormat类的方法格式化货币,但不会转换它们。

请注意,Currency类设计为任何给定货币都不会有多个Currency实例。因此,没有公共构造函数。如前面的代码示例所示,您可以使用getInstance方法获取Currency实例。

示例InternationalizedMortgageCalculator.java还演示了如何使用Currency类。(请注意,此示例不会转换货币值。)以下使用 en-US 区域设置:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下使用 en-UK 区域设置:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例InternationalizedMortgageCalculator.java需要以下资源文件:

  • resources/Resources.properties

  • resources/Resources_ar.properties

  • resources/Resources_fr.properties

Currency类包含其他方法来检索与货币相关的信息:

  • getAvailableCurrencies:返回 JDK 中所有可用的货币

  • getCurrencyCode:返回Currency实例的 ISO 4217 数字代码

  • getSymbol:返回Currency实例的符号。您可以选择性地将Locale对象作为参数。考虑以下摘录:

    Locale enGBLocale = 
        new Locale.Builder().setLanguage("en").setRegion("GB").build();
    
    Locale enUSLocale =
        new Locale.Builder().setLanguage("en").setRegion("US").build();
    
    Currency currencyInstance = Currency.getInstance(enUSLocale);
    
    System.out.println(
        "Symbol for US Dollar, en-US locale: " +
        currencyInstance.getSymbol(enUSLocale));
    
    System.out.println(
        "Symbol for US Dollar, en-UK locale: " +
        currencyInstance.getSymbol(enGBLocale));
    
    

    该摘录打印如下内容:

    Symbol for US Dollar, en-US locale: $
    Symbol for US Dollar, en-UK locale: USD
    
    

    此摘录演示了货币符号可以根据区域设置而变化。

  • getDisplayName:返回Currency实例的显示名称。与getSymbol方法类似,您可以选择性地指定一个Locale对象。

ISO 4217 货币代码的可扩展支持

ISO 4217是国际标准化组织发布的标准。它指定三个字母代码(以及等效的三位数字代码)来表示货币和资金。此标准由外部机构维护,并独立于 Java SE 平台发布。

假设一个国家采用了不同的货币,并且 ISO 4217 维护机构发布了一个货币更新。要实现此更新并在运行时取代默认货币,请创建一个名为*<JAVA_HOME>*/lib/currency.properties的属性文件。此文件包含ISO 3166国家代码和 ISO 4217 货币数据的键/值对。值部分包括三个逗号分隔的 ISO 4217 货币值:字母代码、数字代码和小单位。任何以井号字符(#)开头的行都被视为注释行。例如:

# Sample currency property for Canada
CA=CAD,124,2

CAD代表加拿大元;124是加拿大元的数字代码;2是小单位,表示货币需要表示分数货币的小数位数。例如,以下属性文件将把默认的加拿大货币替换为没有比加拿大元更小单位的加拿大元:

CA=CAD,124,0

百分比

您还可以使用NumberFormat类的方法来格式化百分比。要获取特定于区域设置的格式化程序,请调用getPercentInstance方法。使用这个格式化程序,例如小数分数 0.75 将显示为 75%。

以下代码示例显示了如何格式化百分比。

static public void displayPercent(Locale currentLocale) {

    Double percent = new Double(0.75);
    NumberFormat percentFormatter;
    String percentOut;

    percentFormatter = NumberFormat.getPercentInstance(currentLocale);
    percentOut = percentFormatter.format(percent);
    System.out.println(percentOut + "   " + currentLocale.toString());
}

这个示例打印如下内容:

75 %   fr_FR
75%   de_DE
75%   en_US

自定义格式

原文:docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html

你可以使用DecimalFormat类将十进制数格式化为特定于区域设置的字符串。这个类允许你控制前导和尾随零的显示,前缀和后缀,分组(千位)分隔符和小数分隔符。如果你想要更改格式化符号,比如小数分隔符,你可以与DecimalFormat类一起使用DecimalFormatSymbols。这些类在数字格式化方面提供了很大的灵活性,但可能会使你的代码变得更加复杂。

以下文本使用示例演示了DecimalFormatDecimalFormatSymbols类。本材料中的代码示例来自一个名为DecimalFormatDemo的示例程序。

构建模式

你可以通过模式String指定DecimalFormat的格式属性。模式决定了格式化后的数字是什么样子的。有关模式语法的完整描述,请参见数字格式模式语法。

接下来的示例通过将模式String传递给DecimalFormat构造函数来创建一个格式化器。format方法接受一个double值作为参数,并以String形式返回格式化后的数字:

DecimalFormat myFormatter = new DecimalFormat(pattern);
String output = myFormatter.format(value);
System.out.println(value + " " + pattern + " " + output);

前面代码的输出描述在以下表中。value是要格式化的数字,一个doublepattern是指定格式属性的Stringoutput是一个String,表示格式化后的数字。

DecimalFormatDemo程序的输出

valuepatternoutput解释
123456.789###,###.###123,456.789井号(#)表示一个数字,逗号是分组分隔符的占位符,句点是小数分隔符的占位符。
123456.789###.##123456.79value小数点右侧有三位数字,但pattern只有两位。format方法通过四舍五入处理这个问题。
123.78000000.000000123.780pattern指定了前导和尾随零,因为使用 0 字符而不是井号(#)。
12345.67$###,###.###$12,345.67pattern中的第一个字符是美元符号($)。请注意,它紧跟在格式化的output中最左边的数字之前。
12345.67\u00A5###,###.###¥12,345.67pattern指定了日元(¥)的货币符号,Unicode 值为 00A5。

区域敏感格式化

前面的示例创建了一个默认LocaleDecimalFormat对象。如果你想要一个非默认LocaleDecimalFormat对象,你可以实例化一个NumberFormat,然后将其转换为DecimalFormat。以下是一个示例:

NumberFormat nf = NumberFormat.getNumberInstance(loc);
DecimalFormat df = (DecimalFormat)nf;
df.applyPattern(pattern);
String output = df.format(value);
System.out.println(pattern + " " + output + " " + loc.toString());

运行上一个代码示例会产生以下输出。格式化的数字,位于第二列,随Locale而变化:

###,###.###      123,456.789     en_US
###,###.###      123.456,789     de_DE
###,###.###      123 456,789     fr_FR

到目前为止,这里讨论的格式化模式遵循美国英语的惯例。例如,在模式###,###.##中,逗号是千位分隔符,句点代表小数点。这种惯例是可以接受的,前提是您的最终用户不会接触到它。然而,一些应用程序,如电子表格和报表生成器,允许最终用户定义自己的格式化模式。对于这些应用程序,最终用户指定的格式化模式应使用本地化符号。在这些情况下,您需要在DecimalFormat对象上调用applyLocalizedPattern方法。

更改格式化符号

您可以使用DecimalFormatSymbols类来更改format方法生成的格式化数字中出现的符号。这些符号包括小数分隔符、分组分隔符、减号和百分号等。

下一个示例演示了DecimalFormatSymbols类,通过对数字应用奇怪的格式来实现。这种不寻常的格式是通过调用setDecimalSeparatorsetGroupingSeparatorsetGroupingSize方法得到的。

DecimalFormatSymbols unusualSymbols = new DecimalFormatSymbols(currentLocale);
unusualSymbols.setDecimalSeparator('|');
unusualSymbols.setGroupingSeparator('^');

String strange = "#,##0.###";
DecimalFormat weirdFormatter = new DecimalFormat(strange, unusualSymbols);
weirdFormatter.setGroupingSize(4);

String bizarre = weirdFormatter.format(12345.678);
System.out.println(bizarre);

运行时,此示例会以奇怪的格式打印数字:

1²³⁴⁵|678

数字格式模式语法

您可以按照以下 BNF 图表指定的规则设计自己的数字格式模式:

pattern    := subpattern{;subpattern}
subpattern := {prefix}integer{.fraction}{suffix}
prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
integer    := '#'* '0'* '0'
fraction   := '0'* '#'*

在前面的图表中使用的符号在以下表格中有解释:

符号描述
X*X 的 0 或多个实例
(X &#124; Y)X 或 Y 中的任意一个
X..Y从 X 到 Y 的任意字符,包括 X 和 Y
S - TS 中的字符,但不包括 T 中的字符
{X}X 是可选的

在前面的 BNF 图表中,第一个子模式指定了正数的格式。第二个子模式是可选的,指定了负数的格式。

尽管在 BNF 图表中没有说明,但逗号可能出现在整数部分内。

在子模式中,您可以使用特殊符号指定格式。这些符号在以下表格中描述:

符号描述
0一个数字
#一个数字,零表示不存在
.小数分隔符的占位符
,用于分组分隔符的占位符
E用于指数格式中的尾数和指数的分隔符
;分隔格式
-默认负数前缀
%乘以 100 并显示为百分比
?乘以 1000 并显示为千分数
¤货币符号;替换为货币符号;如果双倍,则替换为国际货币符号;如果在模式中存在,则使用货币小数分隔符而不是小数分隔符
X前缀或后缀中可以使用任何其他字符
用于引用前缀或后缀中的特殊字符

日期和时间

原文:docs.oracle.com/javase/tutorial/i18n/format/dateintro.html


版本说明: 此日期和时间部分使用java.util包中的日期和时间 API。JDK 8 发布的java.time API 提供了一个全面的日期和时间模型,相比java.util类有显著改进。java.time API 在日期时间教程中有描述。旧版日期时间代码页面可能会引起特别关注。


Date对象表示日期和时间。您无法显示或打印Date对象,而不先将其转换为适当格式的String。什么是“适当”的格式呢?首先,格式应符合最终用户的Locale的约定。例如,德国人认为20.4.09是一个有效的日期,但美国人期望同一日期显示为4/20/09。其次,格式应包含必要的信息。例如,一个测量网络性能的程序可能报告经过的毫秒数。在线约会日历可能不会显示毫秒,但会显示星期几。

本节介绍如何以各种方式和符合区域设置的方式格式化日期和时间。如果您遵循这些技术,您的程序将以适当的Locale显示日期和时间,但您的源代码将保持独立于任何特定的Locale

使用预定义格式

DateFormat类提供了具有区域特定性和易于使用的预定义格式样式。

自定义格式

使用SimpleDateFormat类,您可以创建定制的、区域特定的格式。

更改日期格式符号

使用DateFormatSymbols类,您可以更改表示月份名称、星期几和其他格式化元素的符号。

使用预定义格式

原文:docs.oracle.com/javase/tutorial/i18n/format/dateFormat.html


版本说明: 本日期和时间部分使用了java.util包中的日期和时间 API。在 JDK 8 发布的java.timeAPI 中,提供了一个全面的日期和时间模型,相比java.util类有显著的改进。java.timeAPI 在日期时间教程中有详细描述。旧日期时间代码页面可能会引起特别关注。


DateFormat类允许您以区域敏感的方式使用预定义样式格式化日期和时间。接下来的部分演示了如何使用名为DateFormatDemo.java的程序与DateFormat类一起使用。

日期

使用DateFormat类格式化日期是一个两步过程。首先,使用getDateInstance方法创建格式化器。其次,调用format方法,返回包含格式化日期的String。以下示例通过调用这两个方法格式化今天的日期:

Date today;
String dateOut;
DateFormat dateFormatter;

dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, currentLocale);
today = new Date();
dateOut = dateFormatter.format(today);

System.out.println(dateOut + " " + currentLocale.toString());

以下是此代码生成的输出。请注意日期的格式会随着Locale的不同而变化。由于DateFormat是区域敏感的,它会为每个Locale处理格式化细节。

30 juin 2009     fr_FR
30.06.2009       de_DE
Jun 30, 2009     en_US

前面的代码示例指定了默认格式样式。默认样式只是DateFormat类提供的预定义格式样式之一,如下所示:

  • 默认

  • 中等

  • 完整

以下表显示了美国和法国区域的每种样式的日期格式:

示例日期格式

样式美国区域法国区域
默认2009 年 6 月 30 日2009 年 6 月 30 日
6/30/0930/06/09
中等2009 年 6 月 30 日2009 年 6 月 30 日
2009 年 6 月 30 日2009 年 6 月 30 日
完整2009 年 6 月 30 日星期二2009 年 6 月 30 日星期二

时间

Date对象代表日期和时间。使用DateFormat类格式化时间与格式化日期类似,只是你需要使用getTimeInstance方法创建格式化器,如下所示:

DateFormat timeFormatter =
    DateFormat.getTimeInstance(DateFormat.DEFAULT, currentLocale);

下表显示了美国和德国区域的各种预定义格式样式:

示例时间格式

样式美国区域德国区域
默认上午 7:03:477:03:47
上午 7:0307:03
中等上午 7:03:4707:03:07
上午 7:03:47 PDT07:03:45 PDT
完整上午 7:03:47 PDT下午 7.03 PDT

日期和时间都包括

要在同一String中显示日期和时间,使用getDateTimeInstance方法创建格式化器。第一个参数是日期样式,第二个是时间样式。第三个参数是Locale。以下是一个快速示例:

DateFormat formatter = DateFormat.getDateTimeInstance(
                           DateFormat.LONG, 
                           DateFormat.LONG, 
                           currentLocale);

以下表显示了美国和法国区域的日期和时间格式样式:

日期和时间格式示例

样式美国区域设置法国区域设置
DEFAULTJun 30, 2009 7:03:47 AM30 juin 2009 07:03:47
SHORT6/30/09 7:03 AM30/06/09 07:03
MEDIUMJun 30, 2009 7:03:47 AM30 juin 2009 07:03:47
LONGJune 30, 2009 7:03:47 AM PDT30 juin 2009 07:03:47 PDT
FULLTuesday, June 30, 2009 7:03:47 AM PDTmardi 30 juin 2009 07 h 03 PDT

自定义格式

原文:docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html


版本说明: 此日期和时间部分使用java.util包中的日期和时间 API。在 JDK 8 发布的java.timeAPI 提供了一个全面的日期和时间模型,相比java.util类有显著改进。java.timeAPI 在日期时间教程中有详细描述。旧版日期时间代码页面可能会引起特别关注。


前一节,使用预定义格式,描述了DateFormat类提供的格式样式。在大多数情况下,这些预定义格式是足够的。但是,如果你想创建自定义格式,可以使用SimpleDateFormat类。

接下来的代码示例演示了SimpleDateFormat类的方法。你可以在名为SimpleDateFormatDemo的文件中找到示例的完整源代码。

关于模式

当你创建一个SimpleDateFormat对象时,你指定一个模式String。模式String的内容决定了日期和时间的格式。有关模式语法的完整描述,请参见日期格式模式语法中的表格。

以下代码根据传递给SimpleDateFormat构造函数的模式String格式化日期和时间。format方法返回的String包含要显示的格式化日期和时间。

Date today;
String output;
SimpleDateFormat formatter;

formatter = new SimpleDateFormat(pattern, currentLocale);
today = new Date();
output = formatter.format(today);
System.out.println(pattern + " " + output);

以下表格显示了在指定美国Locale时前面代码示例生成的输出:

自定义日期和时间格式

模式输出
dd.MM.yy30.06.09
yyyy.MM.dd G ‘at’ hh:mm:ss z2009.06.30 公元 at 08:29:36 PDT
EEE, MMM d, ''yy周二, 六月 30, '09
h:mm a8:29 PM
H:mm8:29
H:mm:ss:SSS8:28:36:249
K:mm a,z8:29 AM,PDT
yyyy.MMMMM.dd GGG hh:mm aaa2009.六月.30 公元 08:29 AM

模式和区域设置

SimpleDateFormat类是区域敏感的。如果实例化SimpleDateFormat而不带Locale参数,它将根据默认Locale格式化日期和时间。模式和Locale都决定了格式。对于相同的模式,如果Locale不同,SimpleDateFormat可能会以不同方式格式化日期和时间。

在接下来的示例代码中,模式是硬编码在创建SimpleDateFormat对象的语句中的:

Date today;
String result;
SimpleDateFormat formatter;

formatter = new SimpleDateFormat("EEE d MMM yy", currentLocale);
today = new Date();
result = formatter.format(today);
System.out.println("Locale: " + currentLocale.toString());
System.out.println("Result: " + result);

currentLocale设置为不同值时,前面的代码示例生成以下输出:

Locale: fr_FR
Result: mar. 30 juin 09
Locale: de_DE
Result: Di 30 Jun 09
Locale: en_US
Result: Tue 30 Jun 09

日期格式模式语法

你可以根据以下表格中的符号列表设计自己的日期和时间格式模式:

符号含义显示示例
G时代指示符文本公元
y年份数字2009
M年份中的月份文本 & 数字七月 & 07
d月中的日期数字10
h上午/下午的小时数 (1-12)数字12
H一天中的小时数 (0-23)数字0
m小时中的分钟数数字30
s分钟中的秒数数字55
S毫秒数字978
E星期几文本星期二
D一年中的日期数字189
F月中的星期几数字2 (七月第二个星期三)
w年中周数数字27
W月中周数数字2
a上午/下午标记文本下午
k一天中的小时数 (1-24)数字24
K上午/下午的小时数 (0-11)数字0
z时区文本太平洋标准时间
转义文本分隔符(无)
单引号字面值

非字母字符被视为引用文本。也就是说,即使它们没有被单引号括起来,它们也会出现在格式化文本中。

您指定的符号字母数量还决定了格式。例如,如果“zz”模式结果为“PDT”,那么“zzzz”模式会生成“太平洋夏令时间”。以下表总结了这些规则:

展示符号数量结果
文本1 - 3缩写形式,如果存在的话
文本>= 4完整形式
数字需要的最小数字位数较短的数字用零填充(例如,如果’y’的计数为 2,则年份将被截断为 2 位数)
文本 & 数字1 - 2数字形式
文本 & 数字3文本形式

更改日期格式符号

原文:docs.oracle.com/javase/tutorial/i18n/format/dateFormatSymbols.html


版本说明: 此日期和时间部分使用java.util包中的日期和时间 API。在 JDK 8 发布的java.time API 提供了一个全面的日期和时间模型,相比java.util类有显著改进。java.time API 在日期时间教程中有详细描述。传统日期时间代码页面可能会引起特别关注。


SimpleDateFormat类的format方法返回由数字和符号组成的String。例如,在String “Friday, April 10, 2009"中,符号是"Friday"和"April”。如果SimpleDateFormat封装的符号不符合您的需求,您可以使用DateFormatSymbols进行更改。您可以更改代表月份、星期几和时区等的符号。以下表列出了允许您修改符号的DateFormatSymbols方法:

DateFormatSymbol 方法

设置方法方法修改的符号示例
setAmPmStrings下午
setEras公元
setMonths十二月
setShortMonths十二月
setShortWeekdays星期二
setWeekdays星期二
setZoneStringsPST

以下示例调用setShortWeekdays将星期几的简称从小写更改为大写字符。此示例的完整源代码在DateFormatSymbolsDemo中。setShortWeekdays的数组参数中的第一个元素是一个空String。因此,数组是基于一的而不是零的。SimpleDateFormat构造函数接受修改后的DateFormatSymbols对象作为参数。以下是源代码:

Date today;
String result;
SimpleDateFormat formatter;
DateFormatSymbols symbols;
String[] defaultDays;
String[] modifiedDays;

symbols = new DateFormatSymbols( new Locale("en", "US"));
defaultDays = symbols.getShortWeekdays();

for (int i = 0; i < defaultDays.length; i++) {
    System.out.print(defaultDays[i] + " ");
}
System.out.println();

String[] capitalDays = {
    "", "SUN", "MON",
    "TUE", "WED", "THU",
    "FRI", "SAT"
};
symbols.setShortWeekdays(capitalDays);

modifiedDays = symbols.getShortWeekdays();
for (int i = 0; i < modifiedDays.length; i++) {
    System.out.print(modifiedDays[i] + " ");
}
System.out.println();
System.out.println();

formatter = new SimpleDateFormat("E", symbols);
today = new Date();
result = formatter.format(today);
System.out.println("Today's day of the week: " + result);

以上代码生成以下输出:

 Sun Mon Tue Wed Thu Fri Sat 
 SUN MON TUE WED THU FRI SAT 

Today's day of the week: MON

消息

原文:docs.oracle.com/javase/tutorial/i18n/format/messageintro.html

我们都喜欢使用让我们了解正在发生什么的程序。通常,让我们保持了解的程序通过显示状态和错误消息来实现。当然,这些消息需要被翻译,以便全球的最终用户能够理解。隔离特定区域数据部分讨论了可翻译的文本消息。通常,在将消息String移入ResourceBundle后,你就完成了。然而,如果在消息中嵌入了变量数据,你需要采取一些额外的步骤来准备翻译。

复合消息包含变量数据。在下面的复合消息列表中,变量数据被划线标出:

The disk named MyDisk contains 300 files.
The current balance of account #34-09-222 is $2,745.72.
405,390 people have visited your website since January 1, 2009.
Delete all files older than 120 days.

你可能会尝试通过连接短语和变量来构建前述列表中的最后一条消息,如下所示:

double numDays;
ResourceBundle msgBundle;
// ...
String message = msgBundle.getString(
                     "deleteolder" +
                     numDays.toString() +
                     msgBundle.getString("days"));

这种方法在英语中效果很好,但对于动词出现在句子末尾的语言来说,这种方法不适用。因为这条消息的词序是硬编码的,你的本地化人员将无法为所有语言创建语法正确的翻译。

如果需要使用复合消息,如何使你的程序可本地化?你可以通过使用MessageFormat类来实现,这是本节的主题。


注意:

复合消息很难翻译,因为消息文本是分散的。如果使用复合消息,本地化将需要更长的时间和更多的成本。因此,只有在必要时才应使用复合消息。


处理复合消息

复合消息可能包含多种类型的变量:日期、时间、字符串、数字、货币和百分比。为了以与区域无关的方式格式化复合消息,你需要构建一个模式,然后应用到MessageFormat对象上。

处理复数形式

如果可能同时存在复数和单数形式的词汇,消息中的词汇通常会有所变化。通过ChoiceFormat类,你可以将数字映射到一个词或短语,从而构建语法正确的消息。

处理复合消息

原文:docs.oracle.com/javase/tutorial/i18n/format/messageFormat.html

复合消息可能包含几种类型的变量:日期、时间、字符串、数字、货币和百分比。为了以与语言环境无关的方式格式化复合消息,您构造一个模式,将其应用于 MessageFormat 对象,并将此模式存储在 ResourceBundle 中。

通过逐步执行示例程序,本节演示了如何国际化复合消息。示例程序使用了 MessageFormat 类。此程序的完整源代码在名为 MessageFormatDemo.java 的文件中。德语区域设置属性在名为 MessageBundle_de_DE.properties 的文件中。

1. 识别消息中的变量

假设您想要国际化以下消息:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,我们已经划线标出了变量数据,并确定了将表示这些数据的对象的类型。

2. 在 ResourceBundle 中隔离消息模式

将消息存储在名为 MessageBundleResourceBundle 中,如下所示:

ResourceBundle messages =
   ResourceBundle.getBundle("MessageBundle", currentLocale);

这个 ResourceBundle 是由每个 Locale 的属性文件支持的。由于 ResourceBundle 名称为 MessageBundle,因此美国英语的属性文件名为 MessageBundle_en_US.properties。此文件的内容如下:

template = At {2,time,short} on {2,date,long}, \
    we detected {1,number,integer} spaceships on \
    the planet {0}.
planet = Mars

属性文件的第一行包含消息模式。如果您将此模式与步骤 1 中显示的消息文本进行比较,您将看到在消息文本中的每个变量都由大括号括起的参数替换。每个参数以称为参数编号的数字开头,该数字与保存参数值的 Object 数组中的元素的索引相匹配。请注意,在模式中,参数编号没有特定顺序。您可以将参数放置在模式的任何位置。唯一的要求是参数编号在参数值数组中有一个匹配的元素。

下一步讨论了参数值数组,但首先让我们看一下模式中的每个参数。以下表格提供了有关参数的一些详细信息:

MessageBundle_en_US.properties 中为 template 参数提供参数

参数描述
{2,time,short}一个 Date 对象的时间部分。short 样式指定了 DateFormat.SHORT 格式化样式。
{2,date,long}一个Date对象的日期部分。相同的Date对象用于日期和时间变量。在参数的Object数组中,保存Date对象的元素的索引为 2。 (这在下一步中描述。)
{1,number,integer}一个带有integer数字样式的Number对象。
{0}planet键对应的ResourceBundle中的String

对于参数语法的完整描述,请参阅MessageFormat类的 API 文档。

3. 设置消息参数

以下代码行为模式中的每个参数分配值。messageArguments数组中元素的索引与模式中的参数编号相匹配。例如,索引为 1 的Integer元素对应于模式中的{1,number,integer}参数。因为必须进行翻译,所以元素 0 处的String对象将使用getString方法从ResourceBundle中获取。以下是定义消息参数数组的代码:

Object[] messageArguments = {
    messages.getString("planet"),
    new Integer(7),
    new Date()
};

4. 创建格式化程序

接下来,创建一个MessageFormat对象。您设置Locale,因为消息包含应以区域敏感的方式格式化的DateNumber对象。

MessageFormat formatter = new MessageFormat("");
formatter.setLocale(currentLocale);

5. 使用模式和参数格式化消息

这一步展示了模式、消息参数和格式化程序如何协同工作。首先,使用getString方法从ResourceBundle中获取模式String。模式的关键是template。使用applyPattern方法将模式String传递给格式化程序。然后通过调用format方法使用消息参数的数组格式化消息。format方法返回的String已经准备好显示。所有这些只需两行代码就可以完成:

formatter.applyPattern(messages.getString("template"));
String output = formatter.format(messageArguments);

6. 运行演示程序

演示程序打印了英语和德语区域设置的翻译消息,并正确格式化了日期和时间变量。请注意,英语和德语动词(“detected"和"entdeckt”)相对于变量的位置不同:

currentLocale = en_US
At 10:16 AM on July 31, 2009, we detected 7
spaceships on the planet Mars.
currentLocale = de_DE
Um 10:16 am 31\. Juli 2009 haben wir 7 Raumschiffe
auf dem Planeten Mars entdeckt.

处理复数形式

原文:docs.oracle.com/javase/tutorial/i18n/format/choiceFormat.html

如果消息中的单词既可能是复数形式又可能是单数形式,则可能会有所变化。使用ChoiceFormat类,您可以将数字映射到一个单词或短语,从而构造语法正确的消息。

在英语中,单词的复数和单数形式通常是不同的。当您构造涉及数量的消息时,这可能会带来问题。例如,如果您的消息报告磁盘上的文件数量,则可能存在以下变化:

There are no files on XDisk.
There is one file on XDisk.
There are 2 files on XDisk.

解决这个问题的最快方法是创建一个像这样的MessageFormat模式:

There are {0,number} file(s) on {1}.

不幸的是,前面的模式导致了不正确的语法:

There are 1 file(s) on XDisk.

只要使用ChoiceFormat类,你就可以做得更好。在本节中,您将通过一个名为ChoiceFormatDemo的示例程序逐步学习如何处理消息中的复数。该程序还使用了在前一节中讨论的MessageFormat类,即处理复合消息。

1. 定义消息模式

首先,识别消息中的变量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后,用参数替换消息中的变量,创建一个可以应用于MessageFormat对象的模式:

There {0} on {1}.

磁盘名称的参数,由{1}表示,处理起来相当简单。您只需像处理MessageFormat模式中的任何其他String变量一样对待它。此参数匹配参数值数组中索引为 1 的元素。(参见步骤 7。)

处理参数{0}更加复杂,原因有几个:

  • 此参数替换的短语随文件数量的变化而变化。为了在运行时构造这个短语,您需要将文件数量映射到特定的String。例如,数字 1 将映射到包含短语is one fileStringChoiceFormat类允许您执行必要的映射。

  • 如果磁盘包含多个文件,则短语中包含一个整数。MessageFormat类允许您将数字插入到短语中。

2. 创建一个 ResourceBundle

因为消息文本必须被翻译,所以将其隔离在一个ResourceBundle中:

ResourceBundle bundle = ResourceBundle.getBundle(
    "ChoiceBundle", currentLocale);

示例程序使用属性文件支持ResourceBundleChoiceBundle_en_US.properties包含以下内容:

pattern = There {0} on {1}.
noFiles = are no files
oneFile = is one file
multipleFiles = are {2} files

此属性文件的内容显示了消息将如何构建和格式化。第一行包含了MessageFormat的模式。(参见步骤 1。)其他行包含了将替换模式中参数{0}的短语。multipleFiles 键的短语包含了参数{2},该参数将被一个数字替换。

这是属性文件的法语版本,ChoiceBundle_fr_FR.properties

pattern = Il {0} sur {1}.
noFiles = n'y a pas de fichiers
oneFile = y a un fichier
multipleFiles = y a {2} fichiers

3. 创建消息格式化器

在此步骤中,您实例化MessageFormat并设置其Locale

MessageFormat messageForm = new MessageFormat("");
messageForm.setLocale(currentLocale);

4. 创建选择格式化器

ChoiceFormat对象允许您根据double数字选择特定的Stringdouble数字的范围,以及它们映射到的String对象,都在数组中指定:

double[] fileLimits = {0,1,2};
String [] fileStrings = {
    bundle.getString("noFiles"),
    bundle.getString("oneFile"),
    bundle.getString("multipleFiles")
};

ChoiceFormatdouble数组中的每个元素映射到具有相同索引的String数组中的元素。在示例代码中,0 映射到调用bundle.getString("noFiles")返回的String。巧合的是,索引与fileLimits数组中的值相同。如果代码将fileLimits[0]设置为七,ChoiceFormat将把数字 7 映射到fileStrings[0]

在实例化ChoiceFormat时,您需要指定doubleString数组:

ChoiceFormat choiceForm = new ChoiceFormat(fileLimits, fileStrings);

5. 应用模式

还记得您在步骤 1 中构建的模式吗?现在是从ResourceBundle中检索模式并应用到MessageFormat对象的时候了:

String pattern = bundle.getString("pattern");
messageForm.applyPattern(pattern);

6. 分配格式

在此步骤中,您将在步骤 4 中创建的ChoiceFormat对象分配给MessageFormat对象:

Format[] formats = {choiceForm, null, NumberFormat.getInstance()};
messageForm.setFormats(formats);

setFormats方法将Format对象分配给消息模式中的参数。在调用setFormats方法之前,必须调用applyPattern方法。以下表格显示了Format数组的元素如何对应于消息模式中的参数:

ChoiceFormatDemo 程序的Format数组

数组元素模式参数
choiceForm{0}
null{1}
NumberFormat.getInstance(){2}

7. 设置参数并格式化消息

在运行时,程序将变量分配给传递给MessageFormat对象的参数数组。数组中的元素对应于模式中的参数。例如,messageArgument[1]映射到模式参数{1},其中包含磁盘名称的String。在上一步中,程序将ChoiceFormat对象分配给模式的参数{0}。因此,分配给messageArgument[0]的数字确定ChoiceFormat对象选择哪个String。如果messageArgument[0]大于或等于 2,则包含短语are {2} filesString将替换模式中的参数{0}。分配给messageArgument[2]的数字将替换模式参数{2}的位置。以下是尝试此操作的代码:

Object[] messageArguments = {null, "XDisk", null};

for (int numFiles = 0; numFiles < 4; numFiles++) {
    messageArguments[0] = new Integer(numFiles);
    messageArguments[2] = new Integer(numFiles);
    String result = messageForm.format(messageArguments);
    System.out.println(result);
}

8. 运行演示程序

将程序显示的消息与第 2 步的ResourceBundle中的短语进行比较。注意ChoiceFormat对象选择了正确的短语,MessageFormat对象用于构建适当的消息。ChoiceFormatDemo程序的输出如下:

currentLocale = en_US
There are no files on XDisk.
There is one file on XDisk.
There are 2 files on XDisk.
There are 3 files on XDisk.

currentLocale = fr_FR
Il n'y a pas des fichiers sur XDisk.
Il y a un fichier sur XDisk.
Il y a 2 fichiers sur XDisk.
Il y a 3 fichiers sur XDisk.

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您好,作为AI语言模型,我可以用中文回答这个问题。 Java基础案例教程第二课后习题答案,应该是指Java编程基础知识的教程类书籍的第二中,课后练习中的答案。如果您是在学习这本教材,可以在课后习题的答案中得到自己的巩固和检验。如果您还没有这本教材,可以到网上或实体书店购买。祝您学习愉快! ### 回答2: 很抱歉,作为一名AI语言模型,我目前没有本地存储Java基础案例教程第二的课后习题答案。但是,作为一名语言模型,在帮助您回答问题的同时能够为您提供一些有用的信息和建议。 针对Java编程初学者,建议在掌握Java基础语法的基础上多注重实践和案例练习,可以通过参考相关书籍的习题答案来加深对于Java编程的理解和运用。 在解答Java基础案例教程第二的课后习题时,同样也需要注重思路的拓展和代码的细节分析。建议在每章节的学习中把握好Java语法的基础知识,同时结合实际案例进行练习,并多借助计算机软件等工具进行实验测试。 总的来说,学习Java编程需要掌握不断实践、不断练习的方法,只有通过实际操作和多次练习,才能真正理解和掌握Java编程语言的核心思想和特点。 ### 回答3: 很抱歉,我无法提供该书的课后习题答案。答案应由学生自行完成,以便他们可以通过练习和挑战来深入学习Java编程语言的基础知识。此外,完成习题也有助于学生独立思考和解决问题的能力。建议学生可以借助书中的例题、代码样例和文档资料来辅助完成习题,并可在完成后向老师或同学请教、交流和讨论,从而更好地掌握Java编程技能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值