diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index 4bb84658a..d31bf4efd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -25,8 +25,6 @@ import javafx.scene.control.ButtonType; import javafx.scene.input.Clipboard; import javafx.scene.input.DataFormat; 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.SambaException; import org.jackhuang.hmcl.task.AsyncTaskExecutor; @@ -52,10 +50,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import java.util.stream.Stream; import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.util.Logging.LOG; @@ -70,8 +66,6 @@ public final class Launcher extends Application { CookieHandler.setDefault(COOKIE_MANAGER); - register(); - LOG.info("JavaFX Version: " + System.getProperty("javafx.runtime.version")); try { 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 loadDependencies(RemoteModRepository modRepository) throws IOException { - throw new IOException(); - } - - @Override - public Stream loadVersions(RemoteModRepository modRepository) throws IOException { - throw new IOException(); - } - })); - } - private static ButtonType showAlert(AlertType alertType, String contentText, ButtonType... buttons) { return new Alert(alertType, contentText, buttons).showAndWait().orElse(null); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index f37ffe862..2034f8ac5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -29,7 +29,6 @@ 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.util.logging.Level; @@ -72,7 +71,7 @@ public final class HMCLGameLauncher extends DefaultLauncher { } } - if (I18n.getCurrentLocale().getLocale() != Locale.CHINA) { + if (!I18n.isUseChinese()) { return; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java index 445f28be1..39bd0ae17 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java @@ -177,7 +177,7 @@ public abstract class SettingsView extends StackPane { languagePane.setLeft(left); cboLanguage = new JFXComboBox<>(); - cboLanguage.setConverter(stringConverter(locale -> locale.getName(I18n.getCurrentLocale().getResourceBundle()))); + cboLanguage.setConverter(stringConverter(I18n::getName)); FXUtils.setLimitWidth(cboLanguage, 300); languagePane.setRight(cboLanguage); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index 32e05cd7c..46df504b1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -477,7 +477,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP protected void updateControl(RemoteMod dataItem, boolean empty) { if (empty) return; 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.getTags().setAll(dataItem.getCategories().stream() .map(category -> getSkinnable().getLocalizedCategory(category)) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index f00db92d2..90c3fdc5a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -240,7 +240,7 @@ public class DownloadPage extends Control implements DecoratorPage { TwoLineListItem content = new TwoLineListItem(); HBox.setHgrow(content, Priority.ALWAYS); 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.getTags().setAll(getSkinnable().addon.getCategories().stream() .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))); getChildren().setAll(container); - ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug()); - content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : addon.getTitle()); - content.setSubtitle(addon.getDescription()); - content.getTags().setAll(addon.getCategories().stream() - .map(page::getLocalizedCategory) - .collect(Collectors.toList())); + if (addon != RemoteMod.BROKEN) { + ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug()); + content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : addon.getTitle()); + content.setSubtitle(addon.getDescription()); + content.getTags().setAll(addon.getCategories().stream() + .map(page::getLocalizedCategory) + .collect(Collectors.toList())); - if (StringUtils.isNotBlank(addon.getIconUrl())) { - imageView.setImage(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true, true)); + if (StringUtils.isNotBlank(addon.getIconUrl())) { + 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)); } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 1b8965bc0..a7f162c5b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -490,10 +490,8 @@ class ModListPageSkin extends SkinBase { content.getTags().add(i18n("install.installer.quilt")); break; } - if (dataItem.getMod() != null) { - if (I18n.getCurrentLocale().getLocale() == Locale.CHINA) { - content.getTags().add(dataItem.getMod().getDisplayName()); - } + if (dataItem.getMod() != null && I18n.isUseChinese()) { + content.getTags().add(dataItem.getMod().getDisplayName()); } content.setSubtitle(dataItem.getSubtitle()); if (booleanProperty != null) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java index 638c28e24..4f839b8f9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java @@ -20,10 +20,9 @@ package org.jackhuang.hmcl.util.i18n; import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale; import java.time.Instant; -import java.util.Arrays; -import java.util.IllegalFormatException; -import java.util.MissingResourceException; -import java.util.ResourceBundle; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; import java.util.logging.Level; 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 ResourceBundle resourceBundle = locale.getResourceBundle(); + private static volatile DateTimeFormatter dateTimeFormatter; public static void setLocale(SupportedLocale locale) { I18n.locale = locale; + resourceBundle = locale.getResourceBundle(); + dateTimeFormatter = null; } - public static SupportedLocale getCurrentLocale() { - return locale; + public static boolean isUseChinese() { + return locale.getLocale() == Locale.CHINA; } 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) { @@ -69,7 +76,12 @@ public final class I18n { } 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) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/Locales.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/Locales.java index cba28cd2e..3da9e612c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/Locales.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/Locales.java @@ -24,8 +24,6 @@ import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.platform.JavaVersion; import java.io.IOException; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; @@ -34,7 +32,7 @@ public final class Locales { private Locales() { } - public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault(), "lang.default"); + public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault()); /** * English @@ -102,23 +100,10 @@ public final class Locales { @JsonAdapter(SupportedLocale.TypeAdapter.class) public static final class SupportedLocale { private final Locale locale; - private final String name; - private final ResourceBundle resourceBundle; - private DateTimeFormatter dateTimeFormatter; + private ResourceBundle resourceBundle; SupportedLocale(Locale locale) { - this(locale, null); - } - - SupportedLocale(Locale locale, String name) { 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() { @@ -126,20 +111,22 @@ public final class Locales { } public ResourceBundle getResourceBundle() { - return resourceBundle; - } + ResourceBundle bundle = resourceBundle; - public DateTimeFormatter getDateTimeFormatter() { - if (dateTimeFormatter == null) { - dateTimeFormatter = DateTimeFormatter.ofPattern(resourceBundle.getString("world.time")).withZone(ZoneId.systemDefault()); + if (resourceBundle == null) { + if (this != DEFAULT && this.locale == DEFAULT.locale) { + 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; - } - - public String getName(ResourceBundle newResourceBundle) { - if (name == null) return resourceBundle.getString("lang"); - else return newResourceBundle.getString(name); + return bundle; } public static final class TypeAdapter extends com.google.gson.TypeAdapter { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java index 23365637c..20ad0b805 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java @@ -23,6 +23,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask; import java.io.IOException; import java.time.Instant; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -30,18 +31,18 @@ import java.util.stream.Stream; import static org.jackhuang.hmcl.util.io.NetworkUtils.encodeLocation; public class RemoteMod { - private static RemoteMod EMPTY = null; - public static void registerEmptyRemoteMod(RemoteMod empty) { - EMPTY = empty; - } - - public static RemoteMod getEmptyRemoteMod() { - if (EMPTY == null) { - throw new NullPointerException(); + public static final RemoteMod BROKEN = new RemoteMod("", "", "RemoteMod.BROKEN", "", Collections.emptyList(), "", "", new RemoteMod.IMod() { + @Override + public List loadDependencies(RemoteModRepository modRepository) throws IOException { + throw new IOException(); } - return EMPTY; - } + + @Override + public Stream loadVersions(RemoteModRepository modRepository) throws IOException { + throw new IOException(); + } + }); private final String slug; private final String author; @@ -158,7 +159,7 @@ public class RemoteMod { public RemoteMod load() throws IOException { if (this.remoteMod == null) { if (this.type == DependencyType.BROKEN) { - this.remoteMod = RemoteMod.getEmptyRemoteMod(); + this.remoteMod = RemoteMod.BROKEN; } else { this.remoteMod = this.remoteModRepository.getModById(this.id); }