优化本地化支持 (#4525)
This commit is contained in:
@@ -142,9 +142,9 @@ public final class Locales {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Locale inJavaLocale = inLocale.getLocale();
|
Locale inJavaLocale = inLocale.getLocale();
|
||||||
if (LocaleUtils.isISO3Language(inJavaLocale.getLanguage())) {
|
if (inJavaLocale.getLanguage().length() > 2) {
|
||||||
String iso1 = LocaleUtils.getISO1Language(inJavaLocale);
|
String iso1 = LocaleUtils.getISO1Language(inJavaLocale);
|
||||||
if (LocaleUtils.isISO1Language(iso1)) {
|
if (iso1.length() <= 2) {
|
||||||
Locale.Builder builder = new Locale.Builder()
|
Locale.Builder builder = new Locale.Builder()
|
||||||
.setLocale(inJavaLocale)
|
.setLocale(inJavaLocale)
|
||||||
.setLanguage(iso1);
|
.setLanguage(iso1);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util.i18n;
|
package org.jackhuang.hmcl.util.i18n;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
@@ -31,7 +30,7 @@ import java.util.ResourceBundle;
|
|||||||
/// - For all Chinese locales, `zh-CN` is always added to the candidate list. If `zh-Hans` already exists in the candidate list,
|
/// - For all Chinese locales, `zh-CN` is always added to the candidate list. If `zh-Hans` already exists in the candidate list,
|
||||||
/// `zh-CN` is inserted before `zh`; otherwise, it is inserted after `zh`.
|
/// `zh-CN` is inserted before `zh`; otherwise, it is inserted after `zh`.
|
||||||
/// - For all Traditional Chinese locales, `zh-TW` is always added to the candidate list (before `zh`).
|
/// - For all Traditional Chinese locales, `zh-TW` is always added to the candidate list (before `zh`).
|
||||||
/// - For all [supported][LocaleUtils#toISO1Language(String)] ISO 639-3 language code (such as `eng`, `zho`, `lzh`, etc.),
|
/// - For all [supported][LocaleUtils#mapToISO1Language(String)] ISO 639-3 language code (such as `eng`, `zho`, `lzh`, etc.),
|
||||||
/// a candidate list with the language code replaced by the ISO 639-1 (Macro)language code is added to the end of the candidate list.
|
/// a candidate list with the language code replaced by the ISO 639-1 (Macro)language code is added to the end of the candidate list.
|
||||||
///
|
///
|
||||||
/// @author Glavo
|
/// @author Glavo
|
||||||
@@ -42,67 +41,8 @@ public class DefaultResourceBundleControl extends ResourceBundle.Control {
|
|||||||
public DefaultResourceBundleControl() {
|
public DefaultResourceBundleControl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Locale> ensureEditable(List<Locale> list) {
|
|
||||||
return list instanceof ArrayList<?>
|
|
||||||
? list
|
|
||||||
: new ArrayList<>(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
|
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
|
||||||
if (locale.getLanguage().isEmpty())
|
return LocaleUtils.getCandidateLocales(locale);
|
||||||
return getCandidateLocales(baseName, Locale.ENGLISH);
|
|
||||||
else if (LocaleUtils.isChinese(locale)) {
|
|
||||||
String script = locale.getScript();
|
|
||||||
|
|
||||||
if (script.isEmpty()) {
|
|
||||||
script = LocaleUtils.getScript(locale);
|
|
||||||
if (!script.isEmpty())
|
|
||||||
return getCandidateLocales(baseName, new Locale.Builder()
|
|
||||||
.setLocale(locale)
|
|
||||||
.setScript(script)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String language = locale.getLanguage();
|
|
||||||
|
|
||||||
List<Locale> locales = super.getCandidateLocales(baseName, locale);
|
|
||||||
|
|
||||||
// Is ISO 639-3 language tag
|
|
||||||
if (language.length() == 3) {
|
|
||||||
String iso1 = LocaleUtils.toISO1Language(locale.getLanguage());
|
|
||||||
|
|
||||||
if (iso1.length() == 2) {
|
|
||||||
locales = ensureEditable(locales);
|
|
||||||
locales.removeIf(it -> !it.getLanguage().equals(language));
|
|
||||||
|
|
||||||
locales.addAll(getCandidateLocales(baseName, new Locale.Builder()
|
|
||||||
.setLocale(locale)
|
|
||||||
.setLanguage(iso1)
|
|
||||||
.build()));
|
|
||||||
}
|
|
||||||
} else if (language.equals("zh")) {
|
|
||||||
if (locales.contains(LocaleUtils.LOCALE_ZH_HANT) && !locales.contains(Locale.TRADITIONAL_CHINESE)) {
|
|
||||||
locales = ensureEditable(locales);
|
|
||||||
int chineseIdx = locales.indexOf(Locale.CHINESE);
|
|
||||||
if (chineseIdx >= 0)
|
|
||||||
locales.add(chineseIdx, Locale.TRADITIONAL_CHINESE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!locales.contains(Locale.SIMPLIFIED_CHINESE)) {
|
|
||||||
int chineseIdx = locales.indexOf(Locale.CHINESE);
|
|
||||||
|
|
||||||
if (chineseIdx >= 0) {
|
|
||||||
locales = ensureEditable(locales);
|
|
||||||
if (locales.contains(LocaleUtils.LOCALE_ZH_HANS))
|
|
||||||
locales.add(chineseIdx, Locale.SIMPLIFIED_CHINESE);
|
|
||||||
else
|
|
||||||
locales.add(chineseIdx + 1, Locale.SIMPLIFIED_CHINESE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return locales;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
@@ -42,6 +44,16 @@ public final class LocaleUtils {
|
|||||||
|
|
||||||
public static final String DEFAULT_LANGUAGE_KEY = "default";
|
public static final String DEFAULT_LANGUAGE_KEY = "default";
|
||||||
|
|
||||||
|
private static Locale getInstance(String language, String script, String region,
|
||||||
|
String variant) {
|
||||||
|
Locale.Builder builder = new Locale.Builder();
|
||||||
|
if (!language.isEmpty()) builder.setLanguage(language);
|
||||||
|
if (!script.isEmpty()) builder.setScript(script);
|
||||||
|
if (!region.isEmpty()) builder.setRegion(region);
|
||||||
|
if (!variant.isEmpty()) builder.setVariant(variant);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a locale to the language key.
|
/// Convert a locale to the language key.
|
||||||
///
|
///
|
||||||
/// The language key is in the format of BCP 47 language tag.
|
/// The language key is in the format of BCP 47 language tag.
|
||||||
@@ -52,18 +64,24 @@ public final class LocaleUtils {
|
|||||||
: locale.stripExtensions().toLanguageTag();
|
: locale.stripExtensions().toLanguageTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isISO1Language(String language) {
|
|
||||||
return language.length() == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isISO3Language(String language) {
|
|
||||||
return language.length() == 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull String getISO1Language(Locale locale) {
|
public static @NotNull String getISO1Language(Locale locale) {
|
||||||
String language = locale.getLanguage();
|
String language = locale.getLanguage();
|
||||||
if (language.isEmpty()) return "en";
|
if (language.isEmpty()) return "en";
|
||||||
return isISO3Language(language) ? toISO1Language(language) : language;
|
if (language.length() <= 2)
|
||||||
|
return language;
|
||||||
|
|
||||||
|
String lang = language;
|
||||||
|
while (lang != null) {
|
||||||
|
if (lang.length() <= 2)
|
||||||
|
return lang;
|
||||||
|
else {
|
||||||
|
String iso1 = mapToISO1Language(lang);
|
||||||
|
if (iso1 != null)
|
||||||
|
return iso1;
|
||||||
|
}
|
||||||
|
lang = getParentLanguage(lang);
|
||||||
|
}
|
||||||
|
return language;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the script of the locale. If the script is empty and the language is Chinese,
|
/// Get the script of the locale. If the script is empty and the language is Chinese,
|
||||||
@@ -83,10 +101,108 @@ public final class LocaleUtils {
|
|||||||
return locale.getScript();
|
return locale.getScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ConcurrentMap<Locale, List<Locale>> CANDIDATE_LOCALES = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static @NotNull List<Locale> getCandidateLocales(Locale locale) {
|
public static @NotNull List<Locale> getCandidateLocales(Locale locale) {
|
||||||
return DefaultResourceBundleControl.INSTANCE.getCandidateLocales("", locale);
|
return CANDIDATE_LOCALES.computeIfAbsent(locale, LocaleUtils::createCandidateLocaleList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
private static List<Locale> createCandidateLocaleList(Locale locale) {
|
||||||
|
String language = locale.getLanguage();
|
||||||
|
if (language.isEmpty())
|
||||||
|
return List.of(Locale.ENGLISH, Locale.ROOT);
|
||||||
|
|
||||||
|
String script = getScript(locale);
|
||||||
|
String region = locale.getCountry();
|
||||||
|
List<String> variants = locale.getVariant().isEmpty()
|
||||||
|
? List.of()
|
||||||
|
: List.of(locale.getVariant().split("[_\\-]"));
|
||||||
|
|
||||||
|
ArrayList<Locale> result = new ArrayList<>();
|
||||||
|
do {
|
||||||
|
List<String> languages;
|
||||||
|
|
||||||
|
if (language.isEmpty()) {
|
||||||
|
result.add(Locale.ROOT);
|
||||||
|
break;
|
||||||
|
} else if (language.length() <= 2) {
|
||||||
|
languages = List.of(language);
|
||||||
|
} else {
|
||||||
|
String iso1Language = mapToISO1Language(language);
|
||||||
|
languages = iso1Language != null
|
||||||
|
? List.of(language, iso1Language)
|
||||||
|
: List.of(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
addCandidateLocales(result, languages, script, region, variants);
|
||||||
|
} while ((language = getParentLanguage(language)) != null);
|
||||||
|
|
||||||
|
return List.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addCandidateLocales(ArrayList<Locale> list,
|
||||||
|
List<String> languages,
|
||||||
|
String script,
|
||||||
|
String region,
|
||||||
|
List<String> variants) {
|
||||||
|
if (!variants.isEmpty()) {
|
||||||
|
for (String v : variants) {
|
||||||
|
for (String language : languages) {
|
||||||
|
list.add(getInstance(language, script, region, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!region.isEmpty()) {
|
||||||
|
for (String language : languages) {
|
||||||
|
list.add(getInstance(language, script, region, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!script.isEmpty()) {
|
||||||
|
for (String language : languages) {
|
||||||
|
list.add(getInstance(language, script, "", ""));
|
||||||
|
}
|
||||||
|
if (!variants.isEmpty()) {
|
||||||
|
for (String v : variants) {
|
||||||
|
for (String language : languages) {
|
||||||
|
list.add(getInstance(language, "", region, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!region.isEmpty()) {
|
||||||
|
for (String language : languages) {
|
||||||
|
list.add(getInstance(language, "", region, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String language : languages) {
|
||||||
|
list.add(getInstance(language, "", "", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languages.contains("zh")) {
|
||||||
|
if (list.contains(LocaleUtils.LOCALE_ZH_HANT) && !list.contains(Locale.TRADITIONAL_CHINESE)) {
|
||||||
|
int chineseIdx = list.indexOf(Locale.CHINESE);
|
||||||
|
if (chineseIdx >= 0)
|
||||||
|
list.add(chineseIdx, Locale.TRADITIONAL_CHINESE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list.contains(Locale.SIMPLIFIED_CHINESE)) {
|
||||||
|
int chineseIdx = list.indexOf(Locale.CHINESE);
|
||||||
|
|
||||||
|
if (chineseIdx >= 0) {
|
||||||
|
if (list.contains(LocaleUtils.LOCALE_ZH_HANS))
|
||||||
|
list.add(chineseIdx, Locale.SIMPLIFIED_CHINESE);
|
||||||
|
else
|
||||||
|
list.add(chineseIdx + 1, Locale.SIMPLIFIED_CHINESE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
|
||||||
public static <T> @Nullable T getByCandidateLocales(Map<String, T> map, List<Locale> candidateLocales) {
|
public static <T> @Nullable T getByCandidateLocales(Map<String, T> map, List<Locale> candidateLocales) {
|
||||||
for (Locale locale : candidateLocales) {
|
for (Locale locale : candidateLocales) {
|
||||||
String key = toLanguageKey(locale);
|
String key = toLanguageKey(locale);
|
||||||
@@ -178,17 +294,25 @@ public final class LocaleUtils {
|
|||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
/// Try to convert ISO 639-3 language codes to ISO 639-1 language codes.
|
/// Map ISO 639-3 language codes to ISO 639-1 language codes.
|
||||||
public static String toISO1Language(String languageTag) {
|
public static @Nullable String mapToISO1Language(String iso3Language) {
|
||||||
return switch (languageTag) {
|
return switch (iso3Language) {
|
||||||
case "eng" -> "en";
|
case "eng" -> "en";
|
||||||
case "spa" -> "es";
|
case "spa" -> "es";
|
||||||
case "jpa" -> "ja";
|
case "jpa" -> "ja";
|
||||||
case "rus" -> "ru";
|
case "rus" -> "ru";
|
||||||
case "ukr" -> "uk";
|
case "ukr" -> "uk";
|
||||||
case "zho", "cmn", "lzh", "cdo", "cjy", "cpx", "czh",
|
case "zho" -> "zh";
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable String getParentLanguage(String language) {
|
||||||
|
return switch (language) {
|
||||||
|
case "cmn", "lzh", "cdo", "cjy", "cpx", "czh",
|
||||||
"gan", "hak", "hsn", "mnp", "nan", "wuu", "yue" -> "zh";
|
"gan", "hak", "hsn", "mnp", "nan", "wuu", "yue" -> "zh";
|
||||||
default -> languageTag;
|
case "" -> null;
|
||||||
|
default -> "";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public final class LocaleUtilsTest {
|
|||||||
assertCandidateLocales("zh-Latn", List.of("zh-Latn", "zh", "zh-CN", "und"));
|
assertCandidateLocales("zh-Latn", List.of("zh-Latn", "zh", "zh-CN", "und"));
|
||||||
assertCandidateLocales("zh-Latn-CN", List.of("zh-Latn-CN", "zh-Latn", "zh-CN", "zh", "und"));
|
assertCandidateLocales("zh-Latn-CN", List.of("zh-Latn-CN", "zh-Latn", "zh-CN", "zh", "und"));
|
||||||
assertCandidateLocales("zh-pinyin", List.of("zh-Latn-pinyin", "zh-Latn", "zh-pinyin", "zh", "zh-CN", "und"));
|
assertCandidateLocales("zh-pinyin", List.of("zh-Latn-pinyin", "zh-Latn", "zh-pinyin", "zh", "zh-CN", "und"));
|
||||||
|
assertCandidateLocales("zho", List.of("zho-Hans", "zh-Hans", "zho", "zh-CN", "zh", "und"));
|
||||||
assertCandidateLocales("lzh", List.of("lzh-Hant", "lzh", "zh-Hant", "zh-TW", "zh", "zh-CN", "und"));
|
assertCandidateLocales("lzh", List.of("lzh-Hant", "lzh", "zh-Hant", "zh-TW", "zh", "zh-CN", "und"));
|
||||||
assertCandidateLocales("lzh-Hant", List.of("lzh-Hant", "lzh", "zh-Hant", "zh-TW", "zh", "zh-CN", "und"));
|
assertCandidateLocales("lzh-Hant", List.of("lzh-Hant", "lzh", "zh-Hant", "zh-TW", "zh", "zh-CN", "und"));
|
||||||
assertCandidateLocales("lzh-Hans", List.of("lzh-Hans", "lzh", "zh-Hans", "zh-CN", "zh", "und"));
|
assertCandidateLocales("lzh-Hans", List.of("lzh-Hans", "lzh", "zh-Hans", "zh-CN", "zh", "und"));
|
||||||
@@ -72,12 +73,12 @@ public final class LocaleUtilsTest {
|
|||||||
assertCandidateLocales("ja", List.of("ja", "und"));
|
assertCandidateLocales("ja", List.of("ja", "und"));
|
||||||
assertCandidateLocales("ja-JP", List.of("ja-JP", "ja", "und"));
|
assertCandidateLocales("ja-JP", List.of("ja-JP", "ja", "und"));
|
||||||
assertCandidateLocales("jpa", List.of("jpa", "ja", "und"));
|
assertCandidateLocales("jpa", List.of("jpa", "ja", "und"));
|
||||||
assertCandidateLocales("jpa-JP", List.of("jpa-JP", "jpa", "ja-JP", "ja", "und"));
|
assertCandidateLocales("jpa-JP", List.of("jpa-JP", "ja-JP", "jpa", "ja", "und"));
|
||||||
|
|
||||||
assertCandidateLocales("en", List.of("en", "und"));
|
assertCandidateLocales("en", List.of("en", "und"));
|
||||||
assertCandidateLocales("en-US", List.of("en-US", "en", "und"));
|
assertCandidateLocales("en-US", List.of("en-US", "en", "und"));
|
||||||
assertCandidateLocales("eng", List.of("eng", "en", "und"));
|
assertCandidateLocales("eng", List.of("eng", "en", "und"));
|
||||||
assertCandidateLocales("eng-US", List.of("eng-US", "eng", "en-US", "en", "und"));
|
assertCandidateLocales("eng-US", List.of("eng-US", "en-US", "eng", "en", "und"));
|
||||||
|
|
||||||
assertCandidateLocales("es", List.of("es", "und"));
|
assertCandidateLocales("es", List.of("es", "und"));
|
||||||
assertCandidateLocales("spa", List.of("spa", "es", "und"));
|
assertCandidateLocales("spa", List.of("spa", "es", "und"));
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ HMCL 为多种语言提供本地化支持。
|
|||||||
|
|
||||||
目前,HMCL 为这些语言提供支持:
|
目前,HMCL 为这些语言提供支持:
|
||||||
|
|
||||||
| 语言 | 语言标签 | 首选本地化文件后缀 | 首选本地化键 | 支持状态 | 志愿者 |
|
| 语言 | 语言标签 | 首选本地化文件后缀 | 首选本地化键 | [游戏语言文件](https://minecraft.wiki/w/Language) | 支持状态 | 志愿者 |
|
||||||
|---------|-----------|------------|-----------|--------|-------------------------------------------|
|
|---------|-----------|------------|-----------|---------------------------------------------|--------|-------------------------------------------|
|
||||||
| 英语 | `en` | (空) | `default` | **主要** | [Glavo](https://github.com/3gf8jv4dv) |
|
| 英语 | `en` | (空) | `default` | `en_us` | **主要** | [Glavo](https://github.com/Glavo) |
|
||||||
| 中文 (简体) | `zh-Hans` | `_zh` | `zh` | **主要** | [Glavo](https://github.com/3gf8jv4dv) |
|
| 中文 (简体) | `zh-Hans` | `_zh` | `zh` | `zh_cn` | **主要** | [Glavo](https://github.com/Glavo) |
|
||||||
| 中文 (繁体) | `zh-Hant` | `_zh_Hant` | `zh-Hant` | **主要** | [Glavo](https://github.com/3gf8jv4dv) |
|
| 中文 (繁体) | `zh-Hant` | `_zh_Hant` | `zh-Hant` | `zh_tw` <br/> `zh_hk` | **主要** | [Glavo](https://github.com/Glavo) |
|
||||||
| 中文 (文言) | `lzh` | `_lzh` | `lzh` | 次要 | |
|
| 中文 (文言) | `lzh` | `_lzh` | `lzh` | `lzh` | 次要 | |
|
||||||
| 日语 | `ja` | `_ja` | `ja` | 次要 | |
|
| 日语 | `ja` | `_ja` | `ja` | `ja_jp` | 次要 | |
|
||||||
| 西班牙语 | `es` | `_es` | `es` | 次要 | [3gf8jv4dv](https://github.com/3gf8jv4dv) |
|
| 西班牙语 | `es` | `_es` | `es` | `es_es` | 次要 | [3gf8jv4dv](https://github.com/3gf8jv4dv) |
|
||||||
| 俄语 | `ru` | `_ru` | `ru` | 次要 | [3gf8jv4dv](https://github.com/3gf8jv4dv) |
|
| 俄语 | `ru` | `_ru` | `ru` | `ru_ru` | 次要 | [3gf8jv4dv](https://github.com/3gf8jv4dv) |
|
||||||
| 乌克兰语 | `uk` | `_uk` | `uk` | 次要 | |
|
| 乌克兰语 | `uk` | `_uk` | `uk` | `uk_ua` | 次要 | |
|
||||||
|
|
||||||
HMCL 会要求所有 Pull Request 在更新文档和本地化资源时同步更新所有**主要**支持的语言对应的资源。
|
HMCL 会要求所有 Pull Request 在更新文档和本地化资源时同步更新所有**主要**支持的语言对应的资源。
|
||||||
如果 PR 作者对相关语言并不了解,那么可以直接在评论中提出翻译请求,
|
如果 PR 作者对相关语言并不了解,那么可以直接在评论中提出翻译请求,
|
||||||
@@ -136,8 +136,8 @@ HMCL 的维护者会替你完成其他步骤。
|
|||||||
例如,如果当前环境的语言标签为 `eng-US`,那么 HMCL 会根据以下列表的顺序搜索对应的本地化资源:
|
例如,如果当前环境的语言标签为 `eng-US`,那么 HMCL 会根据以下列表的顺序搜索对应的本地化资源:
|
||||||
|
|
||||||
1. `eng-US`
|
1. `eng-US`
|
||||||
2. `eng`
|
2. `en-US`
|
||||||
3. `en-US`
|
3. `eng`
|
||||||
4. `en`
|
4. `en`
|
||||||
5. `und`
|
5. `und`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user