将语言代码三个字符(ISO 639-2)转换为两个字符代码(ISO 639-1)

2022-04-02 00:00:00 locale android java

我正在开发一个使用文本到语音(TTS)引擎的Android应用程序。TTS组件以Locale对象列表的形式返回可用语言列表。

但每个Locale对象的Locale::getLanguageLocale::getISO3Language两个方法返回相同的3字符代码(ISO 639-2)。通常getLanguage()以2字符格式(ISO 639-1)返回语言代码,但对于特定设备,代码为3个字符。国家代码也是如此。但是,我需要两个字符格式的语言和国家代码(ISO 639-1)。

有人知道进行转换的方法吗?请注意,我需要一个具有两个字母格式的语言和国家/地区代码的相应Locale对象。


解决方案

tl;dr

作为一种解决办法,请根据ISO 639-1将每个已知的Locale映射到其两个字母的语言代码。

new LocaleLookup().lookupTwoLetterLanguageCode( Locale.CANADA_FRENCH )

fr

或可能只分析Locale::toString的文本。

Locale
.CANADA_FRENCH      
.toString()         // fr_CA
.split( "_" )       // Array: { "fr" , "CA" }
[ 0 ]               // Grab first element in array, "fr". 

fr

对于两个字母的国家/地区代码,请使用拆分字符串的第二部分。使用1而不是0的索引。

Locale
.CANADA_FRENCH      
.toString()         // fr_CA
.split( "_" )       // Array: { "fr" , "CA" }
[ 1 ]               // Grab first element in array, "CA". 

CA

错误?

Locale::getLanguage将返回3个字母的代码似乎是一个错误。Javadoc在其代码示例中使用了两个字母的代码。但不幸的是,Javadoc没有明确指定2个或3个字母。我建议您向OpenJDK项目提交一个请求,以澄清此Java代码。

解决方法

作为一种解决办法,您或许可以调用Locale.getISOLanguages来获取所有已知语言的两个字母代码的数组。然后把那些循环起来。对于每个对象,使用在Javadoc中看到的代码,传递两个字母的代码来限制Locale对象进行比较:

if (locale.getLanguage().equals(new Locale("he").getLanguage()))

从此版本中,您可以在区域设置和两个字母的代码之间使用您自己的Map

示例类

这是我第一次尝试这样的变通地图。

在构造函数中,我们获得所有已知区域设置和所有已知2字母ISO 639-1语言代码的列表。

接下来,我们执行嵌套循环。对于每个地区,我们循环所有两个字母的语言代码,直到找到匹配项。注意,我们做的是而不是进行字符串匹配。Javadoc警告我们ISO 639标准不是稳定的;代码正在更改。报价:

注意:ISO 639不是一个稳定的标准-某些语言的代码已更改。Locale的构造函数可以识别代码已更改的语言的新代码和旧代码,但此函数始终返回旧代码。如果要检查代码已更改的特定语言,请不要执行以下操作

if (locale.getLanguage().equals("he")) // BAD!

相反,请执行

if (locale.getLanguage().equals(new Locale("he").getLanguage())) // GOOD.

因此,我们的内部循环查看每个已知的两个字母的语言代码,并获取该语言的Locale对象。然后,if语句比较getLanguage的输出(A)外部循环的Locale和(B)内部循环生成的仅语言的Locale(由两个字母的代码生成)。在您情况下,您声称某个设备正在为我们对getLanguage的调用输出3个字母的代码值。但无论是2个字母还是3个字母,都无关紧要。我们只是在寻找匹配项。

实例化后,我们可以通过调用lookupTwoLetterLanguageCode方法向LocaleLookup实例请求匹配特定Locale的两个字母的代码。

LocaleLookup localeLookup = new LocaleLookup();
Locale locale = Locale.CANADA_FRENCH;
String code = localeLookup.lookupTwoLetterLanguageCode( locale );

System.out.println( "Locale: " + locale.toString() + " " + locale.getDisplayName( Locale.getDefault() ) + " | ISO 639-1 code: " + code );

区域设置:FR_CA法语(加拿大)|ISO 639-1代码:FR

我只是在猜测这一切。我没有仔细考虑过,也没有测试过任何这一点。所以买家-当心,这个解决方案值你花的每一分钱。祝你好运。

这是整个类,其中public static void main用作演示。

package work.basil.example;

import java.util.*;

public class LocaleLookup
{
    private Map < Locale, String > mapLocaleToTwoLetterLangCode;

    public LocaleLookup ( )
    {
        this.mapLocaleToTwoLetterLangCode = new HashMap <>( Locale.getAvailableLocales().length );
        this.makeMaps();
        System.out.println( "mapLocaleToTwoLetterLangCode = " + mapLocaleToTwoLetterLangCode );
    }

    private void makeMaps ( )
    {
        // Get all locales.
        Set < Locale > locales = Set.of( Locale.getAvailableLocales() );


        // Get all languages, per 2-letter code.
        Set < String > twoLetterLanguageCodes = Set.of( Locale.getISOLanguages() ); // Returns: An array of ISO 639 two-letter language codes.

        for ( Locale locale : locales )
        {
            for ( String twoLetterLanguageCode : twoLetterLanguageCodes )
            {
                if ( locale.getLanguage().equals( new Locale( twoLetterLanguageCode ).getLanguage() ) )
                {
                    this.mapLocaleToTwoLetterLangCode.put( locale , twoLetterLanguageCode );
                    break;
                }
            }
        }
//        System.out.println( "locales = " + locales );
//        System.out.println( "twoLetterLanguageCodes = " + twoLetterLanguageCodes );
    }

    public String lookupTwoLetterLanguageCode ( final Locale locale )
    {
        String code = this.mapLocaleToTwoLetterLangCode.get( locale );
        Objects.requireNonNull( code );
        return code;
    }


    public static void main ( String[] args )
    {
        LocaleLookup localeLookup = new LocaleLookup();
        Locale locale = Locale.CANADA_FRENCH;
        String code = localeLookup.lookupTwoLetterLanguageCode( locale );

        System.out.println( "Locale: " + locale.toString() + " " + locale.getDisplayName( Locale.getDefault() ) + " | ISO 639-1 code: " + code );
    }
}

这是我在Java 15预发布版本中生成的映射。注意可能是不正确的,因为我在预发布版本中看到了一些关于区域设置的错误。

相关文章