优化 I18n (#2948)

* update

* update

* update

* update

* update
This commit is contained in:
Glavo 2024-03-23 14:11:52 +08:00 committed by GitHub
parent 8b94fe5b0b
commit 94ccee0b76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 67 additions and 84 deletions

View File

@ -25,8 +25,6 @@ import javafx.scene.control.ButtonType;
import javafx.scene.input.Clipboard; import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat; import javafx.scene.input.DataFormat;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.setting.ConfigHolder;
import org.jackhuang.hmcl.setting.SambaException; import org.jackhuang.hmcl.setting.SambaException;
import org.jackhuang.hmcl.task.AsyncTaskExecutor; import org.jackhuang.hmcl.task.AsyncTaskExecutor;
@ -52,10 +50,8 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Stream;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.Logging.LOG;
@ -70,8 +66,6 @@ public final class Launcher extends Application {
CookieHandler.setDefault(COOKIE_MANAGER); CookieHandler.setDefault(COOKIE_MANAGER);
register();
LOG.info("JavaFX Version: " + System.getProperty("javafx.runtime.version")); LOG.info("JavaFX Version: " + System.getProperty("javafx.runtime.version"));
try { try {
Object pipeline = Class.forName("com.sun.prism.GraphicsPipeline").getMethod("getPipeline").invoke(null); Object pipeline = Class.forName("com.sun.prism.GraphicsPipeline").getMethod("getPipeline").invoke(null);
@ -127,20 +121,6 @@ public final class Launcher extends Application {
} }
} }
private static void register() {
RemoteMod.registerEmptyRemoteMod(new RemoteMod("", "", i18n("mods.broken_dependency.title"), i18n("mods.broken_dependency.desc"), new ArrayList<>(), "", "/assets/img/icon@8x.png", new RemoteMod.IMod() {
@Override
public List<RemoteMod> loadDependencies(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}
@Override
public Stream<RemoteMod.Version> loadVersions(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}
}));
}
private static ButtonType showAlert(AlertType alertType, String contentText, ButtonType... buttons) { private static ButtonType showAlert(AlertType alertType, String contentText, ButtonType... buttons) {
return new Alert(alertType, contentText, buttons).showAndWait().orElse(null); return new Alert(alertType, contentText, buttons).showAndWait().orElse(null);
} }

View File

@ -29,7 +29,6 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@ -72,7 +71,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
} }
} }
if (I18n.getCurrentLocale().getLocale() != Locale.CHINA) { if (!I18n.isUseChinese()) {
return; return;
} }

View File

@ -177,7 +177,7 @@ public abstract class SettingsView extends StackPane {
languagePane.setLeft(left); languagePane.setLeft(left);
cboLanguage = new JFXComboBox<>(); cboLanguage = new JFXComboBox<>();
cboLanguage.setConverter(stringConverter(locale -> locale.getName(I18n.getCurrentLocale().getResourceBundle()))); cboLanguage.setConverter(stringConverter(I18n::getName));
FXUtils.setLimitWidth(cboLanguage, 300); FXUtils.setLimitWidth(cboLanguage, 300);
languagePane.setRight(cboLanguage); languagePane.setRight(cboLanguage);

View File

@ -477,7 +477,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
protected void updateControl(RemoteMod dataItem, boolean empty) { protected void updateControl(RemoteMod dataItem, boolean empty) {
if (empty) return; if (empty) return;
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(getSkinnable().repository.getType()).getModByCurseForgeId(dataItem.getSlug()); ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(getSkinnable().repository.getType()).getModByCurseForgeId(dataItem.getSlug());
content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : dataItem.getTitle()); content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : dataItem.getTitle());
content.setSubtitle(dataItem.getDescription()); content.setSubtitle(dataItem.getDescription());
content.getTags().setAll(dataItem.getCategories().stream() content.getTags().setAll(dataItem.getCategories().stream()
.map(category -> getSkinnable().getLocalizedCategory(category)) .map(category -> getSkinnable().getLocalizedCategory(category))

View File

@ -240,7 +240,7 @@ public class DownloadPage extends Control implements DecoratorPage {
TwoLineListItem content = new TwoLineListItem(); TwoLineListItem content = new TwoLineListItem();
HBox.setHgrow(content, Priority.ALWAYS); HBox.setHgrow(content, Priority.ALWAYS);
ModTranslations.Mod mod = getSkinnable().translations.getModByCurseForgeId(getSkinnable().addon.getSlug()); ModTranslations.Mod mod = getSkinnable().translations.getModByCurseForgeId(getSkinnable().addon.getSlug());
content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : getSkinnable().addon.getTitle()); content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : getSkinnable().addon.getTitle());
content.setSubtitle(getSkinnable().addon.getDescription()); content.setSubtitle(getSkinnable().addon.getDescription());
content.getTags().setAll(getSkinnable().addon.getCategories().stream() content.getTags().setAll(getSkinnable().addon.getCategories().stream()
.map(category -> getSkinnable().page.getLocalizedCategory(category)) .map(category -> getSkinnable().page.getLocalizedCategory(category))
@ -352,15 +352,21 @@ public class DownloadPage extends Control implements DecoratorPage {
container.setOnMouseClicked(e -> Controllers.navigate(new DownloadPage(page, addon, version, callback))); container.setOnMouseClicked(e -> Controllers.navigate(new DownloadPage(page, addon, version, callback)));
getChildren().setAll(container); getChildren().setAll(container);
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug()); if (addon != RemoteMod.BROKEN) {
content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : addon.getTitle()); ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug());
content.setSubtitle(addon.getDescription()); content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : addon.getTitle());
content.getTags().setAll(addon.getCategories().stream() content.setSubtitle(addon.getDescription());
.map(page::getLocalizedCategory) content.getTags().setAll(addon.getCategories().stream()
.collect(Collectors.toList())); .map(page::getLocalizedCategory)
.collect(Collectors.toList()));
if (StringUtils.isNotBlank(addon.getIconUrl())) { if (StringUtils.isNotBlank(addon.getIconUrl())) {
imageView.setImage(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true, true)); imageView.setImage(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true, true));
}
} else {
content.setTitle(i18n("mods.broken_dependency.title"));
content.setSubtitle(i18n("mods.broken_dependency.desc"));
imageView.setImage(FXUtils.newBuiltinImage("/assets/img/icon@8x.png", 40, 40, true, true));
} }
} }
} }

View File

@ -490,10 +490,8 @@ class ModListPageSkin extends SkinBase<ModListPage> {
content.getTags().add(i18n("install.installer.quilt")); content.getTags().add(i18n("install.installer.quilt"));
break; break;
} }
if (dataItem.getMod() != null) { if (dataItem.getMod() != null && I18n.isUseChinese()) {
if (I18n.getCurrentLocale().getLocale() == Locale.CHINA) { content.getTags().add(dataItem.getMod().getDisplayName());
content.getTags().add(dataItem.getMod().getDisplayName());
}
} }
content.setSubtitle(dataItem.getSubtitle()); content.setSubtitle(dataItem.getSubtitle());
if (booleanProperty != null) { if (booleanProperty != null) {

View File

@ -20,10 +20,9 @@ package org.jackhuang.hmcl.util.i18n;
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale; import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays; import java.time.ZoneId;
import java.util.IllegalFormatException; import java.time.format.DateTimeFormatter;
import java.util.MissingResourceException; import java.util.*;
import java.util.ResourceBundle;
import java.util.logging.Level; import java.util.logging.Level;
import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.Logging.LOG;
@ -34,17 +33,25 @@ public final class I18n {
} }
private static volatile SupportedLocale locale = Locales.DEFAULT; private static volatile SupportedLocale locale = Locales.DEFAULT;
private static volatile ResourceBundle resourceBundle = locale.getResourceBundle();
private static volatile DateTimeFormatter dateTimeFormatter;
public static void setLocale(SupportedLocale locale) { public static void setLocale(SupportedLocale locale) {
I18n.locale = locale; I18n.locale = locale;
resourceBundle = locale.getResourceBundle();
dateTimeFormatter = null;
} }
public static SupportedLocale getCurrentLocale() { public static boolean isUseChinese() {
return locale; return locale.getLocale() == Locale.CHINA;
} }
public static ResourceBundle getResourceBundle() { public static ResourceBundle getResourceBundle() {
return getCurrentLocale().getResourceBundle(); 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) {
@ -69,7 +76,12 @@ public final class I18n {
} }
public static String formatDateTime(Instant instant) { public static String formatDateTime(Instant instant) {
return getCurrentLocale().getDateTimeFormatter().format(instant); DateTimeFormatter formatter = dateTimeFormatter;
if (formatter == null) {
formatter = dateTimeFormatter = DateTimeFormatter.ofPattern(getResourceBundle().getString("world.time")).withZone(ZoneId.systemDefault());
}
return formatter.format(instant);
} }
public static boolean hasKey(String key) { public static boolean hasKey(String key) {

View File

@ -24,8 +24,6 @@ import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.platform.JavaVersion; import org.jackhuang.hmcl.util.platform.JavaVersion;
import java.io.IOException; import java.io.IOException;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -34,7 +32,7 @@ public final class Locales {
private Locales() { private Locales() {
} }
public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault(), "lang.default"); public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault());
/** /**
* English * English
@ -102,23 +100,10 @@ public final class Locales {
@JsonAdapter(SupportedLocale.TypeAdapter.class) @JsonAdapter(SupportedLocale.TypeAdapter.class)
public static final class SupportedLocale { public static final class SupportedLocale {
private final Locale locale; private final Locale locale;
private final String name; private ResourceBundle resourceBundle;
private final ResourceBundle resourceBundle;
private DateTimeFormatter dateTimeFormatter;
SupportedLocale(Locale locale) { SupportedLocale(Locale locale) {
this(locale, null);
}
SupportedLocale(Locale locale, String name) {
this.locale = locale; this.locale = locale;
this.name = name;
if (JavaVersion.CURRENT_JAVA.getParsedVersion() == JavaVersion.JAVA_8) {
resourceBundle = ResourceBundle.getBundle("assets.lang.I18N", locale, UTF8Control.INSTANCE);
} else {
// UTF-8 is supported in Java 9+
resourceBundle = ResourceBundle.getBundle("assets.lang.I18N", locale);
}
} }
public Locale getLocale() { public Locale getLocale() {
@ -126,20 +111,22 @@ public final class Locales {
} }
public ResourceBundle getResourceBundle() { public ResourceBundle getResourceBundle() {
return resourceBundle; ResourceBundle bundle = resourceBundle;
}
public DateTimeFormatter getDateTimeFormatter() { if (resourceBundle == null) {
if (dateTimeFormatter == null) { if (this != DEFAULT && this.locale == DEFAULT.locale) {
dateTimeFormatter = DateTimeFormatter.ofPattern(resourceBundle.getString("world.time")).withZone(ZoneId.systemDefault()); bundle = DEFAULT.getResourceBundle();
} else if (JavaVersion.CURRENT_JAVA.getParsedVersion() < 9) {
bundle = ResourceBundle.getBundle("assets.lang.I18N", locale, UTF8Control.INSTANCE);
} else {
// Java 9+ uses UTF-8 as the default encoding for resource bundles
bundle = ResourceBundle.getBundle("assets.lang.I18N", locale);
}
resourceBundle = bundle;
} }
return dateTimeFormatter; return bundle;
}
public String getName(ResourceBundle newResourceBundle) {
if (name == null) return resourceBundle.getString("lang");
else return newResourceBundle.getString(name);
} }
public static final class TypeAdapter extends com.google.gson.TypeAdapter<SupportedLocale> { public static final class TypeAdapter extends com.google.gson.TypeAdapter<SupportedLocale> {

View File

@ -23,6 +23,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -30,18 +31,18 @@ import java.util.stream.Stream;
import static org.jackhuang.hmcl.util.io.NetworkUtils.encodeLocation; import static org.jackhuang.hmcl.util.io.NetworkUtils.encodeLocation;
public class RemoteMod { public class RemoteMod {
private static RemoteMod EMPTY = null;
public static void registerEmptyRemoteMod(RemoteMod empty) { public static final RemoteMod BROKEN = new RemoteMod("", "", "RemoteMod.BROKEN", "", Collections.emptyList(), "", "", new RemoteMod.IMod() {
EMPTY = empty; @Override
} public List<RemoteMod> loadDependencies(RemoteModRepository modRepository) throws IOException {
throw new IOException();
public static RemoteMod getEmptyRemoteMod() {
if (EMPTY == null) {
throw new NullPointerException();
} }
return EMPTY;
} @Override
public Stream<RemoteMod.Version> loadVersions(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}
});
private final String slug; private final String slug;
private final String author; private final String author;
@ -158,7 +159,7 @@ public class RemoteMod {
public RemoteMod load() throws IOException { public RemoteMod load() throws IOException {
if (this.remoteMod == null) { if (this.remoteMod == null) {
if (this.type == DependencyType.BROKEN) { if (this.type == DependencyType.BROKEN) {
this.remoteMod = RemoteMod.getEmptyRemoteMod(); this.remoteMod = RemoteMod.BROKEN;
} else { } else {
this.remoteMod = this.remoteModRepository.getModById(this.id); this.remoteMod = this.remoteModRepository.getModById(this.id);
} }