كيفية تحويل معرف Microsoft Locale (LCID) إلى رمز اللغة أو كائن لغة في Java

StackOverflow https://stackoverflow.com/questions/1192361

  •  19-09-2019
  •  | 
  •  

سؤال

أحتاج إلى ترجمة معرف لغة مايكروسوفت, ، مثل 1033 (بالنسبة لنا الإنجليزية)، في أي ISO 639 رمز اللغة أو مباشرة في جافا لغة نموذج. (تحرير: أو حتى ببساطة في اللغة / البلد / المنطقة "في جدول Microsoft.)

هل هذا ممكن، وما هي أسهل طريقة؟ يفضل استخدام المكتبات القياسية JDK فقط، بالطبع، ولكن إذا لم يكن ذلك ممكنا، مع مكتبة الطرف الثالث.

هل كانت مفيدة؟

المحلول 2

كما بدأت تبدو وكأنها لا يوجد حل جاوة جاهز للقيام بهذا التعيين، فقد استغرقنا ~ 20 دقيقة لفة شيء خاص بنا، على الأقل في الوقت الحالي.

أخذنا المعلومات من فم الحصان، أي http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx., ، والنسخ التي تم لصقها (من خلال Excel) في ملف. مثل هذا:

1078 = Afrikaans - South Africa
1052 = Albanian - Albania
1118 = Amharic - Ethiopia
1025 = Arabic - Saudi Arabia
5121 = Arabic - Algeria 
...

(يمكنك تنزيل الملف هنا إذا كان لديك احتياجات مماثلة.)

ثم هناك فئة بسيطة جدا يقرأ المعلومات من ملف .properties في خريطة، ولديها طريقة للقيام بالتحويل.

Map<String, String> lcidToDescription;

public String getDescription(String lcid) { ... }

ونعم، هذا لا يوجد في الواقع ل رمز اللغة أو كائن لغة (وهذا ما طلبته في الأصل)، ولكن وصف مايكروسوفت "لغة البلد / المنطقة". اتضح أن هذا كان كافيا لضررنا الحالية.

إخلاء المسئولية: هذه حقا هي طريقة ضئيلة، "وهمية" للقيام بذلك بنفسك في Java، ومن الواضح أن الحفاظ على (والحفاظ على) نسخة من معلومات رسم الخرائط LCID في CodeBase الخاص بك ليست أنيقة جدا. (من ناحية أخرى، لن أرغب في إشراك جرة مكتبة ضخمة أو القيام بأي شيء معقدة للغاية لهذه الخرائط البسيطة. لذلك على الرغم من هذه الإجابة، لا تتردد في نشر المزيد من الحلول الأنيقة أو المكتبات الموجودة إذا كنت تعرف أي شيء من هذا القبيل.

نصائح أخرى

يمكنك استخدام getlocaleinfo. للقيام بذلك (على افتراض أنك كنت تعمل على Windows (WIN2K +)).

يوضح رمز 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;
}

لن تتخذ الكثير من العمل لتحويل هذا إلى جني يتصل. (نصيحة: انبعاث الثوابت كأحياء للعثور على قيمهم.)

نموذج رمز JNA:

استخدام JNI أكثر نشاطا قليلا، ولكنه يمكن التحكم فيه لمهمة تافهة نسبيا.

على الأقل، أود أن أنظر إلى مكالمات أصلية لبناء قاعدة بيانات التحويل الخاصة بك. لست متأكدا مما إذا كان Windows لديه وسيلة لتعداد LCIDs، ولكن لا بد أن يكون هناك شيء في .NET. كشيء بناء على مستوى المبنى، هذا ليس عبئا كبيرا. أريد تجنب الصيانة اليدوية للقائمة.

سيقوم الشفرة التالية بإنشاء رسم خرائط برمجيا بين رموز 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;
    }
}

تبعيات ماجن:

    <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>

الاستعمال:

System.out.println("Language: " + new Locales().fromLcid(1033).getDisplayLanguage());

سوف طباعة "اللغة: الإنجليزية".

معنى، LCID 1033 خرائط للغة الإنجليزية.

ملاحظة: هذا يولد فقط التعيينات للمحلات المتوفرة في وقت التشغيل الخاص بك JVM. وهذا يعني، سوف تحصل فقط على مجموعة فرعية من جميع اللغات الممكنة. ومع ذلك، لا أعتقد أنه من الممكن تقنيا إرساء اللغات التي لا تدعمها JVM، لذلك ربما يكون هذا أفضل ما يمكننا القيام به ...

كان أول ضربة على جوجل ل "جافا LCID" هو هذا Javadoc:

gnu.java.awt.font.opentype.NameDecoder

Static Private 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.

لست متأكدا من أين أذهب حول تنزيل هذه المكتبة، لكنه غنو، لذلك لا ينبغي أن يكون من الصعب العثور عليه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top