如何在 Java 中将 Microsoft Locale ID (LCID) 转换为语言代码或 Locale 对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1192361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to convert Microsoft Locale ID (LCID) into language code or Locale object in Java
提问by Jonik
I need to translate a Microsoft locale ID, such as 1033 (for US English), into either an ISO 639 language codeor directly into a Java Localeinstance. (Edit: or even simply into the "Language - Country/Region" in Microsoft's table.)
我需要将Microsoft 区域设置 ID(例如 1033(用于美国英语))转换为ISO 639 语言代码或直接转换为 Java区域设置实例。(编辑:或者甚至只是进入 Microsoft 表格中的“语言 - 国家/地区”。)
Is this possible, and what's the easiest way? Preferably using only JDK standard libraries, of course, but if that's not possible, with a 3rd party library.
这可能吗,最简单的方法是什么?当然,最好仅使用 JDK 标准库,但如果不可能,则使用 3rd 方库。
采纳答案by Jonik
As it started to look like there is no ready Java solution to do this mapping, we took the ~20 minutes to roll something of our own, at least for now.
由于看起来没有现成的 Java 解决方案来进行这种映射,我们花了大约 20 分钟的时间来推出我们自己的东西,至少现在是这样。
We took the information from the horse's mouth, i.e. http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx, and copy-pasted it (through Excel) into a .properties file like this:
我们从马口中获取信息,即http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx,并将其(通过 Excel)复制粘贴到一个 .properties 文件中,如下所示:
1078 = Afrikaans - South Africa
1052 = Albanian - Albania
1118 = Amharic - Ethiopia
1025 = Arabic - Saudi Arabia
5121 = Arabic - Algeria
...
(You can download the file hereif you have similar needs.)
(如果您有类似需求,可以在此处下载该文件。)
Then there's a very simple class that reads the information from the .properties file into a map, and has a method for doing the conversion.
然后是一个非常简单的类,它可以将 .properties 文件中的信息读取到映射中,并具有进行转换的方法。
Map<String, String> lcidToDescription;
public String getDescription(String lcid) { ... }
And yes, this doesn't actually map to language codeor Locale object(which is what I originally asked), but to Microsoft's "Language - Country/Region" description. It turned out this was sufficient for our current need.
是的,这实际上并不映射到语言代码或Locale 对象(这是我最初问的),而是映射到Microsoft 的“语言 - 国家/地区”描述。事实证明,这足以满足我们目前的需求。
Disclaimer: this really is a minimalistic, "dummy" way of doing it yourself in Java, and obviously keeping (and maintaining) a copy of the LCID mapping information in your own codebase is not very elegant. (On the other hand, neither would I want to include a huge library jar or do anything overly complicated just for this simple mapping.) So despite this answer, feel free to post more elegant solutions or existing librariesif you know of anything like that.
免责声明:这确实是在 Java 中自己做的一种简约的“虚拟”方式,显然在您自己的代码库中保留(和维护)LCID 映射信息的副本不是很优雅。(另一方面,我既不想包含一个巨大的库 jar 也不想仅仅为了这个简单的映射做任何过于复杂的事情。)所以尽管有这个答案,如果你知道任何类似的事情,请随时发布更优雅的解决方案或现有的库.
回答by skaffman
The was the first hit on googlefor "Java LCID" is this javadoc:
谷歌上第一个“Java LCID”是这个javadoc:
gnu.java.awt.font.opentype.NameDecoder
private static java.util.Locale getWindowsLocale(int lcid)
Maps a Windows LCID into a Java Locale. Parameters: lcid - the Windows language ID whose Java locale is to be retrieved. Returns: an suitable Locale, or null if the mapping cannot be performed.
私有静态 java.util.Locale getWindowsLocale(int lcid)
Maps a Windows LCID into a Java Locale. Parameters: lcid - the Windows language ID whose Java locale is to be retrieved. Returns: an suitable Locale, or null if the mapping cannot be performed.
I'm not sure where to go about downloading this library, but it's GNU, so it shouldn't be too hard to find.
我不确定从哪里下载这个库,但它是 GNU,所以应该不难找到。
回答by McDowell
You could use GetLocaleInfoto do this (assuming you were running on Windows (win2k+)).
您可以使用GetLocaleInfo来执行此操作(假设您在 Windows (win2k+) 上运行)。
This C++ code demonstrates how to use the function:
此 C++ 代码演示了如何使用该函数:
#include "windows.h"
int main()
{
HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
if(INVALID_HANDLE_VALUE == stdout) return 1;
LCID Locale = 0x0c01; //Arabic - Egypt
int nchars = GetLocaleInfoW(Locale, LOCALE_SISO639LANGNAME, NULL, 0);
wchar_t* LanguageCode = new wchar_t[nchars];
GetLocaleInfoW(Locale, LOCALE_SISO639LANGNAME, LanguageCode, nchars);
WriteConsoleW(stdout, LanguageCode, nchars, NULL, NULL);
delete[] LanguageCode;
return 0;
}
It would not take much work to turn this into a JNAcall. (Tip: emit constants as ints to find their values.)
将其转换为JNA调用不需要太多工作。(提示:将常量作为整数发出以找到它们的值。)
Sample JNA code:
示例 JNA 代码:
Using JNI is a bit more involved, but is manageable for a relatively trivial task.
使用 JNI 有点复杂,但对于一个相对微不足道的任务来说是可以管理的。
At the very least, I would look into using native calls to build your conversion database. I'm not sure if Windows has a way to enumerate the LCIDs, but there's bound to be something in .Net. As a build-level thing, this isn't a huge burden. I would want to avoid manual maintenance of the list.
至少,我会考虑使用本机调用来构建您的转换数据库。我不确定 Windows 是否有办法枚举 LCID,但 .Net 中肯定会有一些东西。作为构建级别的事情,这不是一个巨大的负担。我想避免手动维护列表。
回答by Gili
The following code will programmatically create a mapping between Microsoft LCID codes and Java Locales, making it easier to keep the mapping up-to-date:
以下代码将以编程方式在 Microsoft LCID 代码和 Java 语言环境之间创建映射,从而更容易保持映射最新:
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* @author Gili Tzabari
*/
public final class Locales
{
/**
* Maps a Microsoft LCID to a Java Locale.
*/
private final Map<Integer, Locale> lcidToLocale = new HashMap<>(LcidToLocaleMapping.NUM_LOCALES);
public Locales()
{
// Try loading the mapping from cache
File file = new File("lcid-to-locale.properties");
Properties properties = new Properties();
try (FileInputStream in = new FileInputStream(file))
{
properties.load(in);
for (Object key: properties.keySet())
{
String keyString = key.toString();
Integer lcid = Integer.parseInt(keyString);
String languageTag = properties.getProperty(keyString);
lcidToLocale.put(lcid, Locale.forLanguageTag(languageTag));
}
return;
}
catch (IOException unused)
{
// Cache does not exist or is invalid, regenerate...
lcidToLocale.clear();
}
LcidToLocaleMapping mapping;
try
{
mapping = new LcidToLocaleMapping();
}
catch (IOException e)
{
// Unrecoverable runtime failure
throw new AssertionError(e);
}
for (Locale locale: Locale.getAvailableLocales())
{
if (locale == Locale.ROOT)
{
// Special case that doesn't map to a real locale
continue;
}
String language = locale.getDisplayLanguage(Locale.ENGLISH);
String country = locale.getDisplayCountry(Locale.ENGLISH);
country = mapping.getCountryAlias(country);
String script = locale.getDisplayScript();
for (Integer lcid: mapping.listLcidFor(language, country, script))
{
lcidToLocale.put(lcid, locale);
properties.put(lcid.toString(), locale.toLanguageTag());
}
}
// Cache the mapping
try (FileOutputStream out = new FileOutputStream(file))
{
properties.store(out, "LCID to Locale mapping");
}
catch (IOException e)
{
// Unrecoverable runtime failure
throw new AssertionError(e);
}
}
/**
* @param lcid a Microsoft LCID code
* @return a Java locale
* @see https://msdn.microsoft.com/en-us/library/cc223140.aspx
*/
public Locale fromLcid(int lcid)
{
return lcidToLocale.get(lcid);
}
}
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.bitbucket.cowwoc.preconditions.Preconditions;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Generates a mapping between Microsoft LCIDs and Java Locales.
* <p>
* @see http://stackoverflow.com/a/32324060/14731
* @author Gili Tzabari
*/
final class LcidToLocaleMapping
{
private static final int NUM_COUNTRIES = 194;
private static final int NUM_LANGUAGES = 13;
private static final int NUM_SCRIPTS = 5;
/**
* The number of locales we are expecting. This value is only used for performance optimization.
*/
public static final int NUM_LOCALES = 238;
private static final List<String> EXPECTED_HEADERS = ImmutableList.of("lcid", "language", "location");
// [language] - [comment] ([script])
private static final Pattern languagePattern = Pattern.compile("^(.+?)(?: - (.*?))?(?: \((.+)\))?$");
/**
* Maps a country to a list of entries.
*/
private static final SetMultimap<String, Mapping> COUNTRY_TO_ENTRIES = HashMultimap.create(NUM_COUNTRIES,
NUM_LOCALES / NUM_COUNTRIES);
/**
* Maps a language to a list of entries.
*/
private static final SetMultimap<String, Mapping> LANGUAGE_TO_ENTRIES = HashMultimap.create(NUM_LANGUAGES,
NUM_LOCALES / NUM_LANGUAGES);
/**
* Maps a language script to a list of entries.
*/
private static final SetMultimap<String, Mapping> SCRIPT_TO_ENTRIES = HashMultimap.create(NUM_SCRIPTS,
NUM_LOCALES / NUM_SCRIPTS);
/**
* Maps a Locale country name to a LCID country name.
*/
private static final Map<String, String> countryAlias = ImmutableMap.<String, String>builder().
put("United Arab Emirates", "U.A.E.").
build();
/**
* A mapping between a country, language, script and LCID.
*/
private static final class Mapping
{
public final String country;
public final String language;
public final String script;
public final int lcid;
Mapping(String country, String language, String script, int lcid)
{
Preconditions.requireThat(country, "country").isNotNull();
Preconditions.requireThat(language, "language").isNotNull().isNotEmpty();
Preconditions.requireThat(script, "script").isNotNull();
this.country = country;
this.language = language;
this.script = script;
this.lcid = lcid;
}
@Override
public int hashCode()
{
return country.hashCode() + language.hashCode() + script.hashCode() + lcid;
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof Locales))
return false;
Mapping other = (Mapping) obj;
return country.equals(other.country) && language.equals(other.language) && script.equals(other.script) &&
lcid == other.lcid;
}
}
private final Logger log = LoggerFactory.getLogger(LcidToLocaleMapping.class);
/**
* Creates a new LCID to Locale mapping.
* <p>
* @throws IOException if an I/O error occurs while reading the LCID table
*/
LcidToLocaleMapping() throws IOException
{
Document doc = Jsoup.connect("https://msdn.microsoft.com/en-us/library/cc223140.aspx").get();
Element mainBody = doc.getElementById("mainBody");
Elements elements = mainBody.select("table");
assert (elements.size() == 1): elements;
for (Element table: elements)
{
boolean firstRow = true;
for (Element row: table.select("tr"))
{
if (firstRow)
{
// Make sure that columns are ordered as expected
List<String> headers = new ArrayList<>(3);
Elements columns = row.select("th");
for (Element column: columns)
headers.add(column.text().toLowerCase());
assert (headers.equals(EXPECTED_HEADERS)): headers;
firstRow = false;
continue;
}
Elements columns = row.select("td");
assert (columns.size() == 3): columns;
Integer lcid = Integer.parseInt(columns.get(0).text(), 16);
Matcher languageMatcher = languagePattern.matcher(columns.get(1).text());
if (!languageMatcher.find())
throw new AssertionError();
String language = languageMatcher.group(1);
String script = languageMatcher.group(2);
if (script == null)
script = "";
String country = columns.get(2).text();
Mapping mapping = new Mapping(country, language, script, lcid);
COUNTRY_TO_ENTRIES.put(country, mapping);
LANGUAGE_TO_ENTRIES.put(language, mapping);
if (!script.isEmpty())
SCRIPT_TO_ENTRIES.put(script, mapping);
}
}
}
/**
* Returns the LCID codes associated with a [country, language, script] combination.
* <p>
* @param language a language
* @param country a country (empty string if any country should match)
* @param script a language script (empty string if any script should match)
* @return an empty list if no matches are found
* @throws NullPointerException if any of the arguments are null
* @throws IllegalArgumentException if language is empty
*/
public Collection<Integer> listLcidFor(String language, String country, String script)
throws NullPointerException, IllegalArgumentException
{
Preconditions.requireThat(language, "language").isNotNull().isNotEmpty();
Preconditions.requireThat(country, "country").isNotNull();
Preconditions.requireThat(script, "script").isNotNull();
Set<Mapping> result = LANGUAGE_TO_ENTRIES.get(language);
if (result == null)
{
log.warn("Language '" + language + "' had no corresponding LCID");
return Collections.emptyList();
}
if (!country.isEmpty())
{
Set<Mapping> entries = COUNTRY_TO_ENTRIES.get(country);
result = Sets.intersection(result, entries);
}
if (!script.isEmpty())
{
Set<Mapping> entries = SCRIPT_TO_ENTRIES.get(script);
result = Sets.intersection(result, entries);
}
return result.stream().map(entry -> entry.lcid).collect(Collectors.toList());
}
/**
* @param name the locale country name
* @return the LCID country name
*/
public String getCountryAlias(String name)
{
String result = countryAlias.get(name);
if (result == null)
return name;
return result;
}
}
Maven dependencies:
Maven 依赖项:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.bitbucket.cowwoc</groupId>
<artifactId>preconditions</artifactId>
<version>1.25</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>
Usage:
用法:
System.out.println("Language: " + new Locales().fromLcid(1033).getDisplayLanguage());
will print "Language: English".
将打印“语言:英语”。
Meaning, LCID 1033 maps to the English language.
意思是,LCID 1033 映射到英语。
NOTE: This only generates mappings for locales available on your runtime JVM. Meaning, you will only get a subset of all possible Locales. That said, I don't think it is technically possible to instantiate Locales that your JVM doesn't support, so this is probably the best we can do...
注意:这只会为您的运行时 JVM 上可用的语言环境生成映射。意思是,你只会得到所有可能的语言环境的一个子集。也就是说,我认为在技术上不可能实例化您的 JVM 不支持的区域设置,所以这可能是我们能做的最好的......