在 LocaleUtils 中添加更多工具方法 (#4390)

This commit is contained in:
Glavo
2025-09-04 15:51:17 +08:00
committed by GitHub
parent 3d63523f25
commit 7576bf6a01
7 changed files with 247 additions and 92 deletions

View File

@@ -22,22 +22,19 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.util.Lazy;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.DefaultResourceBundleControl;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
import org.jackhuang.hmcl.util.io.JarUtils;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.SystemUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
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;
@@ -145,47 +142,32 @@ public final class FontManager {
}
private static Font tryLoadLocalizedFont(Path dir) {
if (!Files.isDirectory(dir))
Map<String, Map<String, Path>> fontFiles = LocaleUtils.findAllLocalizedFiles(dir, "font", Set.of(FONT_EXTENSIONS));
if (fontFiles.isEmpty())
return null;
try (Stream<Path> list = Files.list(dir)) {
Map<String, Path> map = new HashMap<>();
List<Locale> candidateLocales = I18n.getLocale().getCandidateLocales();
Set<String> extensions = Set.of(FONT_EXTENSIONS);
list.forEach(file -> {
if (Files.isRegularFile(file)) {
String fileName = file.getFileName().toString();
String extension = StringUtils.substringAfterLast(fileName, '.');
for (Locale locale : candidateLocales) {
Map<String, Path> extToFiles = fontFiles.get(LocaleUtils.toLanguageKey(locale));
if (extToFiles != null) {
for (String ext : FONT_EXTENSIONS) {
Path fontFile = extToFiles.get(ext);
if (fontFile != null) {
LOG.info("Load font file: " + fontFile);
try {
Font font = Font.loadFont(
fontFile.toAbsolutePath().normalize().toUri().toURL().toExternalForm(),
DEFAULT_FONT_SIZE);
if (font != null)
return font;
} catch (MalformedURLException ignored) {
}
if (fileName.startsWith("font") && extensions.contains(extension)) {
map.put(fileName.substring(0, fileName.length() - extension.length() - 1), file);
LOG.warning("Failed to load font " + fontFile);
}
}
});
List<Locale> candidateLocales = I18n.getLocale().getCandidateLocales();
for (Locale locale : candidateLocales) {
String key = DefaultResourceBundleControl.INSTANCE.toBundleName("font", locale);
Path path = map.get(key);
if (path != null) {
LOG.info("Load font file: " + path);
try {
Font font = Font.loadFont(
path.toAbsolutePath().normalize().toUri().toURL().toExternalForm(),
DEFAULT_FONT_SIZE);
if (font != null)
return font;
} catch (MalformedURLException ignored) {
}
LOG.warning("Failed to load font " + path);
}
}
} catch (IOException e) {
LOG.warning("Failed to load font " + dir, e);
}
return null;

View File

@@ -20,7 +20,12 @@ package org.jackhuang.hmcl.util.i18n;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.temporal.TemporalAccessor;
import java.util.*;
@@ -60,7 +65,48 @@ public final class I18n {
}
public static String getDisplaySelfVersion(RemoteVersion version) {
return locale.getDisplaySelfVersion(version);
if (locale.getLocale().getLanguage().equals("lzh")) {
if (version instanceof GameRemoteVersion)
return WenyanUtils.translateGameVersion(GameVersionNumber.asGameVersion(version.getSelfVersion()));
else
return WenyanUtils.translateGenericVersion(version.getSelfVersion());
}
return version.getSelfVersion();
}
/// Find the builtin localized resource with given name and suffix.
///
/// For example, if the current locale is `zh-CN`, when calling `getBuiltinResource("assets.lang.foo", "json")`,
/// this method will look for the following built-in resources in order:
///
/// - `assets/lang/foo_zh_Hans_CN.json`
/// - `assets/lang/foo_zh_Hans.json`
/// - `assets/lang/foo_zh_CN.json`
/// - `assets/lang/foo_zh.json`
/// - `assets/lang/foo.json`
///
/// This method will return the first found resource;
/// if none of the above resources exist, it returns `null`.
public static @Nullable URL getBuiltinResource(String name, String suffix) {
var control = DefaultResourceBundleControl.INSTANCE;
var classLoader = I18n.class.getClassLoader();
for (Locale locale : locale.getCandidateLocales()) {
String resourceName = control.toResourceName(control.toBundleName(name, locale), suffix);
URL input = classLoader.getResource(resourceName);
if (input != null)
return input;
}
return null;
}
/// @see [#getBuiltinResource(String, String) ]
public static @Nullable InputStream getBuiltinResourceAsStream(String name, String suffix) {
URL resource = getBuiltinResource(name, suffix);
try {
return resource != null ? resource.openStream() : null;
} catch (IOException e) {
return null;
}
}
public static String getWikiLink(GameRemoteVersion remoteVersion) {

View File

@@ -20,14 +20,9 @@ package org.jackhuang.hmcl.util.i18n;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
@@ -199,16 +194,6 @@ public final class Locales {
return formatter.format(time);
}
public String getDisplaySelfVersion(RemoteVersion version) {
if (locale.getLanguage().equals("lzh")) {
if (version instanceof GameRemoteVersion)
return WenyanUtils.translateGameVersion(GameVersionNumber.asGameVersion(version.getSelfVersion()));
else
return WenyanUtils.translateGenericVersion(version.getSelfVersion());
}
return version.getSelfVersion();
}
public String getFcMatchPattern() {
String language = locale.getLanguage();
String region = locale.getCountry();
@@ -244,31 +229,6 @@ public final class Locales {
return region.isEmpty() ? language : language + "-" + region;
}
/// Find the builtin localized resource with given name and suffix.
///
/// For example, if the current locale is `zh-CN`, when calling `findBuiltinResource("assets.lang.foo", "json")`,
/// this method will look for the following built-in resources in order:
///
/// - `assets/lang/foo_zh_Hans_CN.json`
/// - `assets/lang/foo_zh_Hans.json`
/// - `assets/lang/foo_zh_CN.json`
/// - `assets/lang/foo_zh.json`
/// - `assets/lang/foo.json`
///
/// This method will open and return the first found resource;
/// if none of the above resources exist, it returns `null`.
public @Nullable InputStream findBuiltinResource(String name, String suffix) {
var control = DefaultResourceBundleControl.INSTANCE;
var classLoader = Locales.class.getClassLoader();
for (Locale locale : getCandidateLocales()) {
String resourceName = control.toResourceName(control.toBundleName(name, locale), suffix);
InputStream input = classLoader.getResourceAsStream(resourceName);
if (input != null)
return input;
}
return null;
}
public boolean isSameLanguage(SupportedLocale other) {
return (this.getLocale().getLanguage().equals(other.getLocale().getLanguage()))
|| (LocaleUtils.isChinese(this.getLocale()) && LocaleUtils.isChinese(other.getLocale()));