优化语言设置 (#4362)

This commit is contained in:
Glavo
2025-08-31 00:18:25 +08:00
committed by GitHub
parent 73531dbf60
commit b7361c8da4
11 changed files with 108 additions and 80 deletions

View File

@@ -38,6 +38,7 @@ import org.jackhuang.hmcl.ui.construct.ComponentList;
import org.jackhuang.hmcl.ui.construct.ComponentSublist; import org.jackhuang.hmcl.ui.construct.ComponentSublist;
import org.jackhuang.hmcl.ui.construct.MultiFileItem; import org.jackhuang.hmcl.ui.construct.MultiFileItem;
import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.i18n.Locales;
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale; import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
import java.util.Arrays; import java.util.Arrays;
@@ -177,8 +178,15 @@ public abstract class SettingsView extends StackPane {
BorderPane.setAlignment(left, Pos.CENTER_LEFT); BorderPane.setAlignment(left, Pos.CENTER_LEFT);
languagePane.setLeft(left); languagePane.setLeft(left);
SupportedLocale currentLocale = I18n.getLocale();
cboLanguage = new JFXComboBox<>(); cboLanguage = new JFXComboBox<>();
cboLanguage.setConverter(stringConverter(I18n::getName)); cboLanguage.setConverter(stringConverter(locale -> {
if (locale.isSameLanguage(currentLocale) || locale == Locales.DEFAULT)
return locale.getDisplayName(currentLocale);
else
return locale.getDisplayName(currentLocale) + " - " + locale.getDisplayName(locale);
}));
FXUtils.setLimitWidth(cboLanguage, 300); FXUtils.setLimitWidth(cboLanguage, 300);
languagePane.setRight(cboLanguage); languagePane.setRight(cboLanguage);

View File

@@ -38,18 +38,18 @@ public final class I18n {
resourceBundle = locale.getResourceBundle(); resourceBundle = locale.getResourceBundle();
} }
public static SupportedLocale getLocale() {
return locale;
}
public static boolean isUseChinese() { public static boolean isUseChinese() {
return locale.getLocale() == Locale.CHINA; return locale.getLocale().getLanguage().equals("zh");
} }
public static ResourceBundle getResourceBundle() { public static ResourceBundle getResourceBundle() {
return resourceBundle; return resourceBundle;
} }
public static String getName(SupportedLocale locale) {
return locale == Locales.DEFAULT ? resourceBundle.getString("lang.default") : locale.getResourceBundle().getString("lang");
}
public static String i18n(String key, Object... formatArgs) { public static String i18n(String key, Object... formatArgs) {
try { try {
return String.format(getResourceBundle().getString(key), formatArgs); return String.format(getResourceBundle().getString(key), formatArgs);

View File

@@ -32,46 +32,70 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public final class Locales { public final class Locales {
private Locales() { private Locales() {
} }
public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault()); public static final SupportedLocale DEFAULT = new SupportedLocale("def", Locale.getDefault()) {
@Override
public String getDisplayName(SupportedLocale inLocale) {
try {
return inLocale.getResourceBundle().getString("lang.default");
} catch (Throwable e) {
LOG.warning("Failed to get localized name for default locale", e);
return "Default";
}
}
};
/** /**
* English * English
*/ */
public static final SupportedLocale EN = new SupportedLocale(Locale.ROOT); public static final SupportedLocale EN = new SupportedLocale("en", Locale.ENGLISH);
/** /**
* Spanish * Spanish
*/ */
public static final SupportedLocale ES = new SupportedLocale(Locale.forLanguageTag("es")); public static final SupportedLocale ES = new SupportedLocale("es", Locale.forLanguageTag("es"));
/** /**
* Russian * Russian
*/ */
public static final SupportedLocale RU = new SupportedLocale(Locale.forLanguageTag("ru")); public static final SupportedLocale RU = new SupportedLocale("ru", Locale.forLanguageTag("ru"));
/** /**
* Japanese * Japanese
*/ */
public static final SupportedLocale JA = new SupportedLocale(Locale.JAPANESE); public static final SupportedLocale JA = new SupportedLocale("ja", Locale.JAPANESE);
/** /**
* Traditional Chinese * Traditional Chinese
*/ */
public static final SupportedLocale ZH = new SupportedLocale(Locale.TRADITIONAL_CHINESE); public static final SupportedLocale ZH_HANT = new SupportedLocale("zh", Locale.forLanguageTag("zh-Hant"));
/** /**
* Simplified Chinese * Simplified Chinese
*/ */
public static final SupportedLocale ZH_CN = new SupportedLocale(Locale.SIMPLIFIED_CHINESE); public static final SupportedLocale ZH_HANS = new SupportedLocale("zh_CN", Locale.forLanguageTag("zh-Hans"));
/** /**
* Wenyan (Classical Chinese) * Wenyan (Classical Chinese)
*/ */
public static final SupportedLocale WENYAN = new SupportedLocale(Locale.forLanguageTag("lzh")) { public static final SupportedLocale WENYAN = new SupportedLocale("lzh", Locale.forLanguageTag("lzh")) {
@Override
public String getDisplayName(SupportedLocale inLocale) {
if (isChinese(inLocale.locale))
return "文言";
String name = super.getDisplayName(inLocale);
return name.equals("lzh") || name.equals("Literary Chinese")
? "Classical Chinese"
: name;
}
@Override @Override
public String formatDateTime(TemporalAccessor time) { public String formatDateTime(TemporalAccessor time) {
return WenyanUtils.formatDateTime(time); return WenyanUtils.formatDateTime(time);
@@ -86,96 +110,94 @@ public final class Locales {
} }
}; };
public static final List<SupportedLocale> LOCALES = List.of(DEFAULT, EN, ES, JA, RU, ZH_CN, ZH, WENYAN); public static final List<SupportedLocale> LOCALES = List.of(DEFAULT, EN, ES, JA, RU, ZH_HANS, ZH_HANT, WENYAN);
public static SupportedLocale getLocaleByName(String name) { public static SupportedLocale getLocaleByName(String name) {
if (name == null) return DEFAULT; if (name == null) return DEFAULT;
switch (name.toLowerCase(Locale.ROOT)) {
case "en": for (SupportedLocale locale : LOCALES) {
return EN; if (locale.getName().equalsIgnoreCase(name))
case "es": return locale;
return ES;
case "ja":
return JA;
case "ru":
return RU;
case "zh":
return ZH;
case "zh_cn":
return ZH_CN;
case "lzh":
return WENYAN;
default:
return DEFAULT;
} }
return DEFAULT;
} }
public static String getNameByLocale(SupportedLocale locale) { public static boolean isEnglish(Locale locale) {
if (locale == EN) return "en"; return locale.getLanguage().equals("en") || locale.getLanguage().isEmpty();
else if (locale == ES) return "es"; }
else if (locale == RU) return "ru";
else if (locale == JA) return "ja"; public static boolean isChinese(Locale locale) {
else if (locale == ZH) return "zh"; return locale.getLanguage().equals("zh") || locale.getLanguage().equals("lzh");
else if (locale == ZH_CN) return "zh_CN";
else if (locale == WENYAN) return "lzh";
else if (locale == DEFAULT) return "def";
else throw new IllegalArgumentException("Unknown locale: " + locale);
} }
@JsonAdapter(SupportedLocale.TypeAdapter.class) @JsonAdapter(SupportedLocale.TypeAdapter.class)
public static class SupportedLocale { public static class SupportedLocale {
private final String name;
private final Locale locale; private final Locale locale;
private ResourceBundle resourceBundle; private ResourceBundle resourceBundle;
private DateTimeFormatter dateTimeFormatter; private DateTimeFormatter dateTimeFormatter;
SupportedLocale(Locale locale) { SupportedLocale(String name, Locale locale) {
this.name = name;
this.locale = locale; this.locale = locale;
} }
public String getName() {
return name;
}
public Locale getLocale() { public Locale getLocale() {
return locale; return locale;
} }
public String getDisplayName(SupportedLocale inLocale) {
if (inLocale.locale.getLanguage().equals("lzh"))
inLocale = ZH_HANT;
return locale.getDisplayName(inLocale.getLocale());
}
public ResourceBundle getResourceBundle() { public ResourceBundle getResourceBundle() {
ResourceBundle bundle = resourceBundle; ResourceBundle bundle = resourceBundle;
if (resourceBundle == null) { if (resourceBundle == null) {
if (this != DEFAULT && this.locale == DEFAULT.locale) { bundle = ResourceBundle.getBundle("assets.lang.I18N", locale, new ResourceBundle.Control() {
bundle = DEFAULT.getResourceBundle(); @Override
} else { public List<Locale> getCandidateLocales(String baseName, Locale locale) {
bundle = ResourceBundle.getBundle("assets.lang.I18N", locale, new ResourceBundle.Control() { if (locale.getLanguage().equals("zh")) {
@Override boolean simplified;
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
if (locale.getLanguage().equals("zh")) {
boolean simplified;
String script = locale.getScript(); String script = locale.getScript();
String region = locale.getCountry(); String region = locale.getCountry();
if (script.isEmpty()) if (script.isEmpty())
simplified = region.equals("CN") || region.equals("SG"); simplified = region.equals("CN") || region.equals("SG");
else else
simplified = script.equals("Hans"); simplified = script.equals("Hans");
if (simplified) { if (simplified) {
return List.of(
Locale.SIMPLIFIED_CHINESE,
Locale.CHINESE,
Locale.ROOT
);
}
}
if (locale.getLanguage().equals("lzh")) {
return List.of( return List.of(
locale, Locale.SIMPLIFIED_CHINESE,
Locale.CHINESE, Locale.CHINESE,
Locale.ROOT Locale.ROOT
); );
} }
return super.getCandidateLocales(baseName, locale);
} }
});
} if (locale.getLanguage().equals("lzh")) {
return List.of(
locale,
Locale.CHINESE,
Locale.ROOT
);
}
if (locale.getLanguage().equals("en")) {
return List.of(Locale.ROOT);
}
return super.getCandidateLocales(baseName, locale);
}
});
resourceBundle = bundle; resourceBundle = bundle;
} }
@@ -194,10 +216,15 @@ public final class Locales {
return version.getSelfVersion(); return version.getSelfVersion();
} }
public boolean isSameLanguage(SupportedLocale other) {
return this.getLocale().getLanguage().equals(other.getLocale().getLanguage())
|| isChinese(this.getLocale()) && isChinese(other.getLocale());
}
public static final class TypeAdapter extends com.google.gson.TypeAdapter<SupportedLocale> { public static final class TypeAdapter extends com.google.gson.TypeAdapter<SupportedLocale> {
@Override @Override
public void write(JsonWriter out, SupportedLocale value) throws IOException { public void write(JsonWriter out, SupportedLocale value) throws IOException {
out.value(getNameByLocale(value)); out.value(value.getName());
} }
@Override @Override

View File

@@ -766,7 +766,6 @@ java.installing=Installing Java
java.uninstall=Uninstall Java java.uninstall=Uninstall Java
java.uninstall.confirm=Are you sure you want to uninstall this Java? This action cannot be undone! java.uninstall.confirm=Are you sure you want to uninstall this Java? This action cannot be undone!
lang=English
lang.default=Use System Locales lang.default=Use System Locales
launch.advice=%s Do you still want to continue to launch? launch.advice=%s Do you still want to continue to launch?

View File

@@ -772,7 +772,6 @@ java.installing=Instalando Java
java.uninstall=Desinstalar Java java.uninstall=Desinstalar Java
java.uninstall.confirm=¿Está seguro de que desea desinstalar este Java? ¡Esta acción no se puede deshacer! java.uninstall.confirm=¿Está seguro de que desea desinstalar este Java? ¡Esta acción no se puede deshacer!
lang=Español
lang.default=Usar idioma del sistema lang.default=Usar idioma del sistema
launch.advice=%s ¿Todavía quieres continuar? launch.advice=%s ¿Todavía quieres continuar?

View File

@@ -492,7 +492,6 @@ java.install.warning.invalid_character=名前に不正な文字が含まれて
java.uninstall=このJavaをアンインストールする java.uninstall=このJavaをアンインストールする
java.uninstall.confirm=本当にこのJavaをアンインストールしますかこの操作は元に戻せません java.uninstall.confirm=本当にこのJavaをアンインストールしますかこの操作は元に戻せません
lang=日本語
lang.default=システム言語を使用する lang.default=システム言語を使用する
launch.advice.java.auto=現在選択されているJavaVMは、ゲームの要件を満たしていません。適切なJavaVMを選択することを許可しますかまたは、ゲーム設定で適切なJavaVMを選択することもできます。 launch.advice.java.auto=現在選択されているJavaVMは、ゲームの要件を満たしていません。適切なJavaVMを選択することを許可しますかまたは、ゲーム設定で適切なJavaVMを選択することもできます。

View File

@@ -510,7 +510,6 @@ java.installing=置爪哇
java.uninstall=移除此爪哇 java.uninstall=移除此爪哇
java.uninstall.confirm=君真欲移除此爪哇乎?此舉不可復! java.uninstall.confirm=君真欲移除此爪哇乎?此舉不可復!
lang=文言
lang.default=依械綱預定 lang.default=依械綱預定
launch.advice=%s 誠欲續啟? launch.advice=%s 誠欲續啟?

View File

@@ -768,7 +768,6 @@ java.installing=Установка Java
java.uninstall=Удалить Java java.uninstall=Удалить Java
java.uninstall.confirm=Вы уверены, что хотите удалить эту Java? Это действие нельзя отменить! java.uninstall.confirm=Вы уверены, что хотите удалить эту Java? Это действие нельзя отменить!
lang=Русский
lang.default=Использовать язык системы lang.default=Использовать язык системы
launch.advice=%s Вы все еще хотите продолжить запуск? launch.advice=%s Вы все еще хотите продолжить запуск?

View File

@@ -575,7 +575,6 @@ java.installing=安裝 Java
java.uninstall=移除此 Java java.uninstall=移除此 Java
java.uninstall.confirm=你確定要移除此 Java 嗎?此操作無法復原! java.uninstall.confirm=你確定要移除此 Java 嗎?此操作無法復原!
lang=繁體中文
lang.default=使用系統語言 lang.default=使用系統語言
launch.advice=%s 是否繼續啟動? launch.advice=%s 是否繼續啟動?

View File

@@ -585,7 +585,6 @@ java.installing=安装 Java
java.uninstall=卸载此 Java java.uninstall=卸载此 Java
java.uninstall.confirm=你确定要卸载此 Java 吗?此操作无法撤销! java.uninstall.confirm=你确定要卸载此 Java 吗?此操作无法撤销!
lang=简体中文
lang.default=跟随系统语言 lang.default=跟随系统语言
launch.advice=%s 是否继续启动? launch.advice=%s 是否继续启动?

View File

@@ -442,7 +442,7 @@ public class DefaultLauncher extends Launcher {
pair("${library_directory}", repository.getLibrariesDirectory(version).getAbsolutePath()), pair("${library_directory}", repository.getLibrariesDirectory(version).getAbsolutePath()),
pair("${classpath_separator}", File.pathSeparator), pair("${classpath_separator}", File.pathSeparator),
pair("${primary_jar}", repository.getVersionJar(version).getAbsolutePath()), pair("${primary_jar}", repository.getVersionJar(version).getAbsolutePath()),
pair("${language}", Locale.getDefault().toString()), pair("${language}", Locale.getDefault().toLanguageTag()),
// defined by HMCL // defined by HMCL
// libraries_directory stands for historical reasons here. We don't know the official launcher // libraries_directory stands for historical reasons here. We don't know the official launcher