java Locale.Builder setExtension(Locale.UNICODE_LOCALE_EXTENSION

标签 java locale

我一直在尝试让 Java 根据语言环境转换数字。 偶遇this post这在很大程度上帮助了我预先理解这一点,我设计了自己的方法将数字转换为特定的语言环境(根据关于这个主题的其他混淆讨论)

所以假设我有:

Locale arabicLocale = new Locale.Builder().setLanguage("ar").setRegion("SA")
 .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "nu-arab").build();

Locale thaiLocale = new Locale.Builder().setLanguage("th").setRegion("TH")
 .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "nu-thai").build();

Locale hinduLocale = new Locale.Builder().setLanguage("hi").setRegion("IN")
 .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "nu-hindu").build();

DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(arabicLocale);
NumberFormat numberFormat = NumberFormat.getNumberInstance(arabicLocale);
println" Arabic ${numberFormat.format(123.22)}"

dfs = DecimalFormatSymbols.getInstance(thaiLocale);
numberFormat = NumberFormat.getNumberInstance(thaiLocale);
println" Thai ${numberFormat.format(123.22)}"

dfs = DecimalFormatSymbols.getInstance(hinduLocale);
numberFormat = NumberFormat.getNumberInstance(hinduLocale);
println" Hindu ${numberFormat.format(123.22)}"

这会产生以下输出

 Arabic ١٢٣٫٢٢
 Thai ๑๒๓.๒๒
 Hindu १२३.२२

这篇文章的目的是尝试确定我如何查找或让代码本身指向正确的 Locale.UNICODE_LOCALE_EXTENSION 因为 th 和印度教的东西只是我的猜测工作结束,我无法理解如何为希伯来语中文日语做同样的事情。虽然我认为中文和日文可能使用阿拉伯数字系统,但我在这一点上可能是错误的。

无论如何,任何关于我如何自动捕获这一点数据或标准的帮助/指示,就像在一个页面中一样,该页面具有我可以从中生成枚举的所有定义,这将有很大帮助

我正在深入研究 LocaleExtensions

static {
        CALENDAR_JAPANESE = new LocaleExtensions("u-ca-japanese", Character.valueOf('u'), UnicodeLocaleExtension.CA_JAPANESE);
        NUMBER_THAI = new LocaleExtensions("u-nu-thai", Character.valueOf('u'), UnicodeLocaleExtension.NU_THAI);
    }

所以这现在更有意义了 nu-language = number ca-language = calendar

但是运行时:

Locale japLocale = new Locale.Builder().setLanguage("ja").setRegion("JP")
                    .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "nu-japanese").build();

我得到英文数字。

根据原问题,链接 https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry

希伯来语

Type: language
Subtag: he
Description: Hebrew
Added: 2005-10-16
Suppress-Script: Hebr

但是在下面尝试时:

Locale hebrewLocale = new Locale.Builder().setLanguage("he").setRegion("IL")
                    .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "nu-hebr").build();

我得到 123.22

要回答我自己的问题,您可以从此处构建一个枚举 http://www.oracle.com/technetwork/java/javase/java8locales-2095355.html

Greek (el) Greece (GR) (Grek)  el-GR
Hebrew (iw) Israel (IL) (Hebr) iw-IL

简而言之

对于希腊,取最后一个字段 el-GR unicodeEXtension=-u number=-nu 和最后一个小写 -hebr 为希腊提供 'el-GR-u-nu-grek' 或为希伯来文提供相同的 'iw-IL-u-n-hebr'

 Locale locale = new Locale.Builder().setLanguageTag('el-GR-u-nu-grek').build();

应该打印出希腊数字,但我看到它适用于某些国家/地区而不适用于其他国家/地区的英文数字。

最佳答案

回答我自己的问题,因为这是一个相当复杂的主题,目前还没有得到很好的解释。

简而言之,根据我的评论,您最好使用 icu4j 。因为这为国际数字/日期转换提供了更完整的解决方案。

困难在于制定所需的所有标准,因为它似乎确实支持所有区域设置语言等,只是知道如何正确使用它的情况。

我将提供一个片段 - 此代码需要清理,但为您的 Java 应用程序提供了数字和日期转换的解决方案:

import groovy.transform.CompileStatic

/**
 * 
 * @author Vahid Hedayati
 * Looks complex but will explain
 *
 * ar-SA u = unicode nu = number arab = arabic
 *
 * https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
 * to get langauge code such as arab = subtag
 *
 * Rest explained here
 * http://stackoverflow.com/questions/43456068/java-locale-builder-setextensionlocale-unicode-locale-extension
 *
 *
 */
@CompileStatic
enum LocaleCalendarExtensions {
    SA('ar-SA-u-ca-arab'),
    AM('hy-AM-u-ca-arevmda'),
    CN('zh-TW-u-ca-hant'),
    CZ('cs-CZ-u-ca-latn'),
    DK('da-DK-u-ca-latn'),
    NL('nl-NL-u-ca-latn'),
    IE('ie-IE-u-ca-latn'),
    FR('fr-FR-u-ca-latn'),
    DE('de-DE-u-ca-latn'),
    GR('el-GR-u-ca-grek'),
    IL('iw-IL-u-ca-hebr'),
    IN('hi-IN-u-ca-hindu'),
    IT('it-IT-u-ca-latn'),
    JP('ja-JP-u-ca-jpan'),
    NO('nb-NO-u-ca-latn'),
    IR(''), //fa-IR-u-ca-fars'),
    PL('pl-PL-u-ca-latn'),
    PT('pt-PT-u-ca-latn'),
    RU('ru-RU-u-ca-cyrl'),
    ES('es-ES-u-ca-latn'),
    SE('sv-SE-u-ca-latn'),
    TH('th-TH-u-ca-thai'),
    TR('tr-TR-u-ca-latn'),
    PK(''),//ur-PK-u-ca-arab'),
    VN('vi-VN-u-ca-latn')

    String value

    LocaleCalendarExtensions(String val) {
        this.value = val
    }
    public String getValue(){
        return value
    }
    static LocaleCalendarExtensions byValue(String val) {
        values().find { it.value == val }
    }
    public static EnumSet<LocaleCalendarExtensions> getArabicSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(SA)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getJapanSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(JP)
        return ret_val
    }

    public static EnumSet<LocaleCalendarExtensions> getChinaSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(CN)
        return ret_val
    }

    public static EnumSet<LocaleCalendarExtensions> getFarsiSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )

        ret_val.add(IR)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getUrduSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(PK)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getAsianSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(JP)
        ret_val.add(CN)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getHebrewSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(IL)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getHinduSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(IN)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getThaiSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(TH)
        return ret_val
    }
    public static EnumSet<LocaleCalendarExtensions> getGreekSupport() {
        final EnumSet< LocaleCalendarExtensions > ret_val = EnumSet.noneOf( LocaleCalendarExtensions.class )
        ret_val.add(GR)
        return ret_val
    }
}

现在如何正确地将日期转换为国际语言环境:

 /**
     *
     * @param lang where lang code provider is ar en cn fr ur it is as per LocaleCalendarExtensions Enum main declarations
     * @param date given date
     * @param format definition in which case I have clause to deal with HH:mm and so on just read through below code
     * @return
     */
    public static String convertDate(String lang, java.util.Date date, String format) {
        StringBuilder output=new StringBuilder()
        if (lang != null && date) {
            def found = LocaleICUCalendarExtensions?.find{it.toString()==lang}
            if (found) {
                def found1 = LocaleExtensions?.valueOf(lang)
                com.ibm.icu.util.ULocale locale =  new com.ibm.icu.util.ULocale(found1.value)
                com.ibm.icu.util.Calendar calendar = com.ibm.icu.util.Calendar.getInstance(locale)
                calendar.setTime(date)
                com.ibm.icu.text.DateFormat df
                if (format == 'HH:mm') {
                    df = com.ibm.icu.text.DateFormat.getPatternInstance( com.ibm.icu.text.DateFormat.HOUR_MINUTE, locale)
                } else {
                    if (format=='dd MMM yyyy HH:mm:ss') {
                        df = com.ibm.icu.text.DateFormat.getDateInstance(DateFormat.FULL, locale)
                    } else if (format=='dd MMM') {
                        df = com.ibm.icu.text.DateFormat.getPatternInstance( com.ibm.icu.text.DateFormat.ABBR_MONTH_DAY, locale)
                    } else {
                        df = com.ibm.icu.text.DateFormat.getDateInstance(DateFormat.LONG, locale)
                    }
                    output << df.format(calendar)
                }

            }
        }
        return output.toString()
    }

要将数字转换为另一个国家/地区的编号系统:

/**
     * Converts number to given locale
     * @param lang
     * @param number
     * @return
     */
    public static String convertNumber(String lang, number) {
        String output=''
        if (lang != null) {
            boolean arabic = (LocaleCalendarExtensions.arabicSupport.find { it.toString() == lang } ? true : false)
            boolean china = (LocaleCalendarExtensions.chinaSupport.find { it.toString() == lang } ? true : false)
            boolean japan = (LocaleCalendarExtensions.japanSupport.find { it.toString() == lang } ? true : false)
            boolean farsi = (LocaleCalendarExtensions.farsiSupport.find { it.toString() == lang } ? true : false)
            boolean urdu = (LocaleCalendarExtensions.urduSupport.find { it.toString() == lang } ? true : false)
            boolean hebrew = (LocaleCalendarExtensions.hebrewSupport.find { it.toString() == lang } ? true : false)
            boolean greek = (LocaleCalendarExtensions.greekSupport.find { it.toString() == lang } ? true : false)
            boolean hindu = (LocaleCalendarExtensions.hinduSupport.find { it.toString() == lang } ? true : false)
            boolean thai = (LocaleCalendarExtensions.thaiSupport.find { it.toString() == lang } ? true : false)
            if (arabic || hindu | thai || farsi||urdu) {
                def found = LocaleExtensions?.valueOf(lang)
                if (found) {
                    Locale locale = new Locale.Builder().setLanguageTag(found.value).build();
                    DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);
                    NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
                    def numbers
                    if (number.toString().indexOf('.')>-1) {
                        numbers=number as Double
                    } else {
                        numbers=number as Long
                    }
                    output = (numberFormat?.format(numbers)) ?:''
                }
            }
            if (japan|china||hebrew||greek) {
                // to extend look up types here
                //http://www.atetric.com/atetric/javadoc/com.ibm.icu/icu4j/49.1/src-html/com/ibm/icu/util/ULocale.html
                //http://icu-project.org/~yoshito/jacoco_57.1/com.ibm.icu.util/ULocale.java.html
                com.ibm.icu.util.ULocale locale

                if (japan) {
                    locale= new com.ibm.icu.util.ULocale("ja_JP_JP")//ja_JP_JP  //
                }
                if (china) {
                    locale= new com.ibm.icu.util.ULocale("zh_Hans")//zh_CN_TRADITIONAL@collation=pinyin;
                }
                if (hebrew) {
                    locale= new com.ibm.icu.util.ULocale("he_IL")

                }
                if (greek) {
                    locale= new com.ibm.icu.util.ULocale("el_GR")
                }
                com.ibm.icu.text.NumberFormat nf = com.ibm.icu.text.NumberFormat.getInstance(locale)
                def numbers
                if (number.toString().indexOf('.')>-1) {
                    numbers=number as Double
                } else {
                    numbers=number as Long
                }
                output = nf.format(numbers)
            }
        }
        return output ?: number.toString()
    }

现在,如果您使用的是 grails,您可以创建一个 taglib 并覆盖 formatDate 和 formatNumber 定义以使用上面的代码:

 /**
     * override default date formatter if translation translate
     */

    def formatDate={attrs->
        String foundRecord
        if (attrs.locale) {
            String lang = attrs.locale.country
            foundRecord = NumberHelper.convertDate(lang, attrs.date, attrs.format)
        }
        if (!foundRecord || foundRecord=='null') {
            out << g.formatDate(attrs)
        } else {
            out << foundRecord
        }
    }


/**
 * Override default formatNumber and translate number if possible otherwsie run default
 */
def formatNumber={attrs->
    def foundRecord
    if (attrs.locale && attrs.number) {
        String lang = attrs.locale.country
        foundRecord = NumberHelper.convertNumber(lang, attrs.number)
    }
    if (!foundRecord) {
        out << g.formatNumber(attrs)
    } else {
        out << "${foundRecord}"
    }
}

上面的 ICU4J 枚举:

import groovy.transform.CompileStatic


@CompileStatic
enum LocaleICUCalendarExtensions {
    SA('ar_SA@calendar=islamic'),
AM('hy_AM@calendar=armenian'),
CN('zh_Hans@calendar=chinese'),
CZ('cs_CZ@calendar=latin'),
DK('da_DK@calendar=latin'),
NL('nl_NL@calendar=latin'),
IE('ie_IE@calendar=latin'),
FR('fr_FR@calendar=latin'),
DE('de_DE@calendar=latin'),
GR('el_GR@calendar=greek'),
IL('iw_IL@calendar=hebrew'),
IN('hi_IN@calendar=hindu'),
IT('it_IT@calendar=Latin'),
JP('ja_JP_TRADITIONAL@calendar=japanese'),
NO('nb_NO@calendar=latin'),
IR('fa_IR@calendar=persian'),
PL('pl_PL@calendar=latin'),
PT('pt_PT@calendar=latin'),
RU('ru_RU@calendar=cyrillic'),
ES('es_ES@calendar=latin'),
SE('sv_SE@calendar=latin'),
TH('th_TH_TRADITIONAL@calendar=buddhist'),
TR('tr_TR@calendar=latin'),
PK('ur_PK@calendar=pakistan'),
VN('vi_VN@calendar=latin')

    String value

    LocaleICUCalendarExtensions(String val) {
        this.value = val
    }
    public String getValue(){
        return value
    }
    static LocaleICUCalendarExtensions byValue(String val) {
        values().find { it.value == val }
    }
    public static EnumSet<LocaleICUCalendarExtensions> getArabicSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(SA)
        return ret_val
    }
    public static EnumSet<LocaleICUCalendarExtensions> getJapanSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(JP)
        return ret_val
    }

    public static EnumSet<LocaleICUCalendarExtensions> getChinaSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(CN)
        return ret_val
    }

    public static EnumSet<LocaleICUCalendarExtensions> getFarsiSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )

        ret_val.add(IR)
        return ret_val
    }
    public static EnumSet<LocaleICUCalendarExtensions> getUrduSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(PK)
        return ret_val
    }

    public static EnumSet<LocaleICUCalendarExtensions> getHebrewSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(IL)
        return ret_val
    }
    public static EnumSet<LocaleICUCalendarExtensions> getHinduSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(IN)
        return ret_val
    }
    public static EnumSet<LocaleICUCalendarExtensions> getThaiSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(TH)
        return ret_val
    }
    public static EnumSet<LocaleICUCalendarExtensions> getGreekSupport() {
        final EnumSet< LocaleICUCalendarExtensions > ret_val = EnumSet.noneOf( LocaleICUCalendarExtensions.class )
        ret_val.add(GR)
        return ret_val
    }
}

LocaleExtensions 枚举有点像 LocaleCalendar 只是有 nu 而不是 ca

import groovy.transform.CompileStatic

/**
 * Looks complex but will explain
 *
 * ar-SA u = unicode nu = number arab = arabic
 *
 * https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
 * to get langauge code such as arab = subtag
 *
 * Rest explained here
 * http://stackoverflow.com/questions/43456068/java-locale-builder-setextensionlocale-unicode-locale-extension
 *
 *
 */
@CompileStatic
enum LocaleExtensions {
    SA('ar-SA-u-nu-arab'),
    AM('hy-AM-u-nu-arevmda'),
    CN('zh-TW-u-nu-arab'), //'zh-TW-u-nu-hant'
    CZ('cs-CZ-u-nu-latn'),
    DK('da-DK-u-nu-latn'),
    NL('nl-NL-u-nu-latn'),
    IE('ie-IE-u-nu-latn'),
    FR('fr-FR-u-nu-latn'),
    DE('de-DE-u-nu-latn'),
    GR('el-GR-u-nu-grek'),
    IL('iw-IL-u-nu-hebr'),
    IN('hi-IN-u-nu-hindu'),
    IT('it-IT-u-nu-latn'),
    JP('ja-JP-u-nu-arab'),
    NO('nb-NO-u-nu-latn'),
    IR('fa-IR-u-nu-arab'),
    PL('pl-PL-u-nu-latn'),
    PT('pt-PT-u-nu-latn'),
    RU('ru-RU-u-nu-cyrl'),
    ES('es-ES-u-nu-latn'),
    SE('sv-SE-u-nu-latn'),
    TH('th-TH-u-nu-thai'),
    TR('tr-TR-u-nu-latn'),
    PK('ur-PK-u-nu-arab'),
    VN('vi-VN-u-nu-latn')

    String value

    LocaleExtensions(String val) {
        this.value = val
    }
    public String getValue(){
        return value
    }
    static LocaleExtensions byValue(String val) {
        values().find { it.value == val }
    }
    public static EnumSet<LocaleExtensions> getArabicSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(SA)
        //TODO
        ret_val.add(JP)
        ret_val.add(CN)
        return ret_val
    }
    public static EnumSet<LocaleExtensions> getFarsiSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(PK)
        ret_val.add(IR)
        return ret_val
    }

    public static EnumSet<LocaleExtensions> getAsianSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(JP)
        ret_val.add(CN)
        return ret_val
    }
    public static EnumSet<LocaleExtensions> getHebrewSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(IL)
        return ret_val
    }
    public static EnumSet<LocaleExtensions> getHinduSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(IN)
        return ret_val
    }
    public static EnumSet<LocaleExtensions> getThaiSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(TH)
        return ret_val
    }
    public static EnumSet<LocaleExtensions> getGreekSupport() {
        final EnumSet< LocaleExtensions > ret_val = EnumSet.noneOf( LocaleExtensions.class )
        ret_val.add(GR)
        return ret_val
    }
}

这显然涵盖了一系列语言环境,用 Bernard Manning 的 bootifuly 的话来说它对我来说工作正常

日期已分为默认支持的 java 与 icu4j,然后当理解得更好时,所有转移到 icu4j 我认为编号系统仍在使用一半和一半,可能也可以切换到 icu4j。

无论如何,答案是一列脱轨的火车,从它开始的地方到现在,它正在将数字字符转换为数字字符,例如从拉丁语到阿拉伯语,这很难理解工作,因为在阿拉伯语中给出了 2016 年的拉丁年在沙特阿拉伯,等效且正确的年份应该是 1354 年,或者在泰国,我认为是 2056 年

关于java Locale.Builder setExtension(Locale.UNICODE_LOCALE_EXTENSION,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43456068/

相关文章:

java - 如何动态填充JTree?

java - Spring、Hibernate 和 JPA : Calling persist on entitymanager does not seem to commit to database

java - 在以下情况下无法获得列表集合的工作

linux - 修复 awk 命令中数据与本地不匹配的问题

java - 在微调器中显示 TTS 可用语言

java.text.ParseException : Unparseable date: "28.дек.2018"

ios - 如何对特定于语言环境的本地通知进行单元测试

c# - 具有与 Java(JNI) 和 C#(C++/CLI) 接口(interface)的可移植 C++ 域层的架构注意事项

java - 返回 1+1/2+1/3+...+1/n 级数的和

Django 1.9 sr_Latn 语言环境不起作用