mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-13 13:56:55 -04:00
优化自动切换游戏语言功能 (#4353)
This commit is contained in:
parent
30f47704f1
commit
86085031f6
@ -21,16 +21,18 @@ import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.launch.DefaultLauncher;
|
||||
import org.jackhuang.hmcl.launch.ProcessListener;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
@ -59,63 +61,83 @@ public final class HMCLGameLauncher extends DefaultLauncher {
|
||||
}
|
||||
|
||||
private void generateOptionsTxt() {
|
||||
File optionsFile = new File(repository.getRunDirectory(version.getId()), "options.txt");
|
||||
File configFolder = new File(repository.getRunDirectory(version.getId()), "config");
|
||||
|
||||
if (optionsFile.exists()) {
|
||||
if (config().isDisableAutoGameOptions())
|
||||
return;
|
||||
}
|
||||
|
||||
if (configFolder.isDirectory()) {
|
||||
if (findFiles(configFolder, "options.txt")) {
|
||||
return;
|
||||
Path runDir = repository.getRunDirectory(version.getId()).toPath();
|
||||
Path optionsFile = runDir.resolve("options.txt");
|
||||
Path configFolder = runDir.resolve("config");
|
||||
|
||||
if (Files.exists(optionsFile))
|
||||
return;
|
||||
|
||||
if (Files.isDirectory(configFolder)) {
|
||||
try (Stream<Path> stream = Files.walk(configFolder, 2, FileVisitOption.FOLLOW_LINKS)) {
|
||||
if (stream.anyMatch(file -> "options.txt".equals(FileUtils.getName(file))))
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to visit config folder", e);
|
||||
}
|
||||
}
|
||||
|
||||
Locale locale = Locale.getDefault();
|
||||
if (Locales.isEnglish(locale))
|
||||
return;
|
||||
|
||||
/*
|
||||
1.0- :没有语言选项,遇到这些版本时不设置
|
||||
1.1 ~ 5 :zh_CN 时正常,zh_cn 时崩溃(最后两位字母必须大写,否则将会 NPE 崩溃)
|
||||
1.6 ~ 10 :zh_CN 时正常,zh_cn 时自动切换为英文
|
||||
1.11 ~ 12:zh_cn 时正常,zh_CN 时虽然显示了中文但语言设置会错误地显示选择英文
|
||||
1.13+ :zh_cn 时正常,zh_CN 时自动切换为英文
|
||||
* 1.0 : No language option, do not set for these versions
|
||||
* 1.1 ~ 1.5 : zh_CN works fine, zh_cn will crash (the last two letters must be uppercase, otherwise it will cause an NPE crash)
|
||||
* 1.6 ~ 1.10 : zh_CN works fine, zh_cn will automatically switch to English
|
||||
* 1.11 ~ 1.12 : zh_cn works fine, zh_CN will display Chinese but the language setting will incorrectly show English as selected
|
||||
* 1.13+ : zh_cn works fine, zh_CN will automatically switch to English
|
||||
*/
|
||||
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(version));
|
||||
if (gameVersion.compareTo("1.1") < 0)
|
||||
return;
|
||||
|
||||
String lang;
|
||||
|
||||
if (I18n.isUseChinese()) {
|
||||
lang = "zh_CN";
|
||||
} else if (Locale.getDefault().getLanguage().equals("lzh")) {
|
||||
lang = "lzh";
|
||||
} else {
|
||||
String lang = normalizedLanguageTag(locale, gameVersion);
|
||||
if (lang.isEmpty())
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameVersion.compareTo("1.11") >= 0) {
|
||||
if (gameVersion.compareTo("1.11") >= 0)
|
||||
lang = lang.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.writeText(optionsFile, String.format("lang:%s\n", lang));
|
||||
Files.createDirectories(optionsFile.getParent());
|
||||
Files.writeString(optionsFile, String.format("lang:%s\n", lang));
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Unable to generate options.txt", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean findFiles(File folder, String fileName) {
|
||||
File[] fs = folder.listFiles();
|
||||
if (fs != null) {
|
||||
for (File f : fs) {
|
||||
if (f.isDirectory())
|
||||
if (f.listFiles((dir, name) -> name.equals(fileName)) != null)
|
||||
return true;
|
||||
if (f.getName().equals(fileName))
|
||||
return true;
|
||||
}
|
||||
private static String normalizedLanguageTag(Locale locale, GameVersionNumber gameVersion) {
|
||||
String language = locale.getLanguage();
|
||||
String region = locale.getCountry();
|
||||
|
||||
switch (language) {
|
||||
case "zh":
|
||||
case "cmn":
|
||||
if (Locales.isSimplifiedChinese(locale))
|
||||
return "zh_CN";
|
||||
if (gameVersion.compareTo("1.16") >= 0
|
||||
&& (region.equals("HK") || region.equals("MO")))
|
||||
return "zh_HK";
|
||||
return "zh_TW";
|
||||
case "ru":
|
||||
return "ru_RU";
|
||||
case "uk":
|
||||
return "uk_UA";
|
||||
case "es":
|
||||
return "es_ES";
|
||||
case "ja":
|
||||
return "ja_JP";
|
||||
case "lzh":
|
||||
return gameVersion.compareTo("1.16") >= 0
|
||||
? "lzh"
|
||||
: "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -186,6 +186,9 @@ public final class Config implements Observable {
|
||||
@SerializedName("addedLittleSkin")
|
||||
private BooleanProperty addedLittleSkin = new SimpleBooleanProperty(false);
|
||||
|
||||
@SerializedName("disableAutoGameOptions")
|
||||
private BooleanProperty disableAutoGameOptions = new SimpleBooleanProperty(false);
|
||||
|
||||
@SerializedName("promptedVersion")
|
||||
private StringProperty promptedVersion = new SimpleStringProperty();
|
||||
|
||||
@ -631,6 +634,18 @@ public final class Config implements Observable {
|
||||
this.addedLittleSkin.set(addedLittleSkin);
|
||||
}
|
||||
|
||||
public BooleanProperty disableAutoGameOptionsProperty() {
|
||||
return disableAutoGameOptions;
|
||||
}
|
||||
|
||||
public boolean isDisableAutoGameOptions() {
|
||||
return disableAutoGameOptions.get();
|
||||
}
|
||||
|
||||
public void setDisableAutoGameOptions(boolean disableAutoGameOptions) {
|
||||
this.disableAutoGameOptions.set(disableAutoGameOptions);
|
||||
}
|
||||
|
||||
public int getConfigVersion() {
|
||||
return configVersion.get();
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ public final class SettingsPage extends SettingsView {
|
||||
// ==== Languages ====
|
||||
cboLanguage.getItems().setAll(Locales.LOCALES);
|
||||
selectedItemPropertyFor(cboLanguage).bindBidirectional(config().localizationProperty());
|
||||
|
||||
disableAutoGameOptionsPane.selectedProperty().bindBidirectional(config().disableAutoGameOptionsProperty());
|
||||
// ====
|
||||
|
||||
fileCommonLocation.selectedDataProperty().bindBidirectional(config().commonDirTypeProperty());
|
||||
|
@ -37,6 +37,7 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentSublist;
|
||||
import org.jackhuang.hmcl.ui.construct.MultiFileItem;
|
||||
import org.jackhuang.hmcl.ui.construct.OptionToggleButton;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
|
||||
@ -50,6 +51,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public abstract class SettingsView extends StackPane {
|
||||
protected final JFXComboBox<SupportedLocale> cboLanguage;
|
||||
protected final OptionToggleButton disableAutoGameOptionsPane;
|
||||
protected final MultiFileItem<EnumCommonDirectory> fileCommonLocation;
|
||||
protected final ComponentSublist fileCommonLocationSublist;
|
||||
protected final Label lblUpdate;
|
||||
@ -193,6 +195,13 @@ public abstract class SettingsView extends StackPane {
|
||||
settingsPane.getContent().add(languagePane);
|
||||
}
|
||||
|
||||
{
|
||||
disableAutoGameOptionsPane = new OptionToggleButton();
|
||||
disableAutoGameOptionsPane.setTitle(i18n("settings.launcher.disable_auto_game_options"));
|
||||
|
||||
settingsPane.getContent().add(disableAutoGameOptionsPane);
|
||||
}
|
||||
|
||||
{
|
||||
BorderPane debugPane = new BorderPane();
|
||||
|
||||
|
@ -76,15 +76,15 @@ public final class Locales {
|
||||
public static final SupportedLocale JA = new SupportedLocale("ja", Locale.JAPANESE);
|
||||
|
||||
/**
|
||||
* Traditional Chinese
|
||||
*/
|
||||
public static final SupportedLocale ZH_HANT = new SupportedLocale("zh", Locale.forLanguageTag("zh-Hant"));
|
||||
|
||||
/**
|
||||
* Simplified Chinese
|
||||
* Chinese (Simplified)
|
||||
*/
|
||||
public static final SupportedLocale ZH_HANS = new SupportedLocale("zh_CN", Locale.forLanguageTag("zh-Hans"));
|
||||
|
||||
/**
|
||||
* Chinese (Traditional)
|
||||
*/
|
||||
public static final SupportedLocale ZH_HANT = new SupportedLocale("zh", Locale.forLanguageTag("zh-Hant"));
|
||||
|
||||
/**
|
||||
* Wenyan (Classical Chinese)
|
||||
*/
|
||||
@ -133,7 +133,27 @@ public final class Locales {
|
||||
}
|
||||
|
||||
public static boolean isChinese(Locale locale) {
|
||||
return locale.getLanguage().equals("zh") || locale.getLanguage().equals("lzh");
|
||||
switch (locale.getLanguage()) {
|
||||
case "zh":
|
||||
case "lzh":
|
||||
case "cmn":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSimplifiedChinese(Locale locale) {
|
||||
if (locale.getLanguage().equals("zh") || locale.getLanguage().equals("cmn")) {
|
||||
String script = locale.getScript();
|
||||
if (script.isEmpty()) {
|
||||
String region = locale.getCountry();
|
||||
return region.isEmpty() || region.equals("CN") || region.equals("SG") || region.equals("MY");
|
||||
} else
|
||||
return script.equals("Hans");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonAdapter(SupportedLocale.TypeAdapter.class)
|
||||
@ -166,26 +186,15 @@ public final class Locales {
|
||||
ResourceBundle bundle = resourceBundle;
|
||||
|
||||
if (resourceBundle == null) {
|
||||
bundle = ResourceBundle.getBundle("assets.lang.I18N", locale, new ResourceBundle.Control() {
|
||||
resourceBundle = bundle = ResourceBundle.getBundle("assets.lang.I18N", locale, new ResourceBundle.Control() {
|
||||
@Override
|
||||
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
|
||||
if (locale.getLanguage().equals("zh")) {
|
||||
boolean simplified;
|
||||
|
||||
String script = locale.getScript();
|
||||
String region = locale.getCountry();
|
||||
if (script.isEmpty())
|
||||
simplified = region.equals("CN") || region.equals("SG");
|
||||
else
|
||||
simplified = script.equals("Hans");
|
||||
|
||||
if (simplified) {
|
||||
return List.of(
|
||||
Locale.SIMPLIFIED_CHINESE,
|
||||
Locale.CHINESE,
|
||||
Locale.ROOT
|
||||
);
|
||||
}
|
||||
if (isSimplifiedChinese(locale)) {
|
||||
return List.of(
|
||||
Locale.SIMPLIFIED_CHINESE,
|
||||
Locale.CHINESE,
|
||||
Locale.ROOT
|
||||
);
|
||||
}
|
||||
|
||||
if (locale.getLanguage().equals("lzh")) {
|
||||
@ -203,7 +212,6 @@ public final class Locales {
|
||||
return super.getCandidateLocales(baseName, locale);
|
||||
}
|
||||
});
|
||||
resourceBundle = bundle;
|
||||
}
|
||||
|
||||
return bundle;
|
||||
|
@ -1338,6 +1338,7 @@ settings.launcher=Launcher Settings
|
||||
settings.launcher.appearance=Appearance
|
||||
settings.launcher.common_path.tooltip=HMCL will put all game assets and dependencies here. If there are existing libraries in the game directory, then HMCL will prefer to use them first.
|
||||
settings.launcher.debug=Debug
|
||||
settings.launcher.disable_auto_game_options=Do not switch game language
|
||||
settings.launcher.download=Download
|
||||
settings.launcher.download.threads=Threads
|
||||
settings.launcher.download.threads.auto=Automatically Determine
|
||||
|
@ -1057,6 +1057,7 @@ settings.launcher=啟設
|
||||
settings.launcher.appearance=驛式
|
||||
settings.launcher.common_path.tooltip=啟者將諸遊戲資源及相依庫檔聚於此。若遊戲目錄自有,則不用公庫。
|
||||
settings.launcher.debug=勘
|
||||
settings.launcher.disable_auto_game_options=不易戲文
|
||||
settings.launcher.download=載
|
||||
settings.launcher.download.threads=執緒數
|
||||
settings.launcher.download.threads.auto=從宜
|
||||
|
@ -1128,6 +1128,7 @@ settings.launcher=啟動器設定
|
||||
settings.launcher.appearance=外觀
|
||||
settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及相依元件庫檔案放於此集中管理。如果遊戲目錄內有現成的將不會使用公共庫檔案。
|
||||
settings.launcher.debug=除錯
|
||||
settings.launcher.disable_auto_game_options=不自動切換遊戲語言
|
||||
settings.launcher.download=下載
|
||||
settings.launcher.download.threads=執行緒數
|
||||
settings.launcher.download.threads.auto=自動選取執行緒數
|
||||
|
@ -1138,6 +1138,7 @@ settings.launcher=启动器设置
|
||||
settings.launcher.appearance=外观
|
||||
settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库文件存放于此集中管理。如果游戏文件夹内有现成的将不会使用公共库文件。
|
||||
settings.launcher.debug=调试
|
||||
settings.launcher.disable_auto_game_options=不自动切换游戏语言
|
||||
settings.launcher.download=下载
|
||||
settings.launcher.download.threads=线程数
|
||||
settings.launcher.download.threads.auto=自动选择线程数
|
||||
|
Loading…
x
Reference in New Issue
Block a user