diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index ba2bcec56..360dae621 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -190,12 +190,10 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab if (remoteVersion instanceof GameRemoteVersion) { RemoteVersion.Type versionType = remoteVersion.getVersionType(); - String wikiSuffix = getWikiUrlSuffix(remoteVersion.getGameVersion()); switch (versionType) { case RELEASE: content.getTags().setAll(i18n("version.game.release")); content.setImage(VersionIconType.GRASS.getIcon()); - content.setExternalLink(i18n("wiki.version.game", wikiSuffix)); break; case PENDING: case SNAPSHOT: @@ -207,14 +205,13 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab content.getTags().setAll(i18n("version.game.snapshot")); content.setImage(VersionIconType.COMMAND.getIcon()); } - content.setExternalLink(i18n("wiki.version.game", wikiSuffix)); break; default: content.getTags().setAll(i18n("version.game.old")); content.setImage(VersionIconType.CRAFT_TABLE.getIcon()); - content.setExternalLink(i18n("wiki.version.game", wikiSuffix)); break; } + content.setExternalLink(I18n.getWikiLink((GameRemoteVersion) remoteVersion)); } else { VersionIconType iconType; if (remoteVersion instanceof LiteLoaderRemoteVersion) @@ -240,75 +237,6 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab content.setExternalLink(null); } } - - private String getWikiUrlSuffix(String gameVersion) { - String id = gameVersion.toLowerCase(Locale.ROOT); - - switch (id) { - case "0.30-1": - case "0.30-2": - case "c0.30_01c": - return i18n("wiki.version.game.search", "Classic_0.30"); - case "in-20100206-2103": - return i18n("wiki.version.game.search", "Indev_20100206"); - case "inf-20100630-1": - return i18n("wiki.version.game.search", "Infdev_20100630"); - case "inf-20100630-2": - return i18n("wiki.version.game.search", "Alpha_v1.0.0"); - case "1.19_deep_dark_experimental_snapshot-1": - return "1.19-exp1"; - case "in-20100130": - return i18n("wiki.version.game.search", "Indev_0.31_20100130"); - case "b1.6-tb3": - return i18n("wiki.version.game.search", "Beta_1.6_Test_Build_3"); - case "1.14_combat-212796": - return i18n("wiki.version.game.search", "1.14.3_-_Combat_Test"); - case "1.14_combat-0": - return i18n("wiki.version.game.search", "Combat_Test_2"); - case "1.14_combat-3": - return i18n("wiki.version.game.search", "Combat_Test_3"); - case "1_15_combat-1": - return i18n("wiki.version.game.search", "Combat_Test_4"); - case "1_15_combat-6": - return i18n("wiki.version.game.search", "Combat_Test_5"); - case "1_16_combat-0": - return i18n("wiki.version.game.search", "Combat_Test_6"); - case "1_16_combat-1": - return i18n("wiki.version.game.search", "Combat_Test_7"); - case "1_16_combat-2": - return i18n("wiki.version.game.search", "Combat_Test_7b"); - case "1_16_combat-3": - return i18n("wiki.version.game.search", "Combat_Test_7c"); - case "1_16_combat-4": - return i18n("wiki.version.game.search", "Combat_Test_8"); - case "1_16_combat-5": - return i18n("wiki.version.game.search", "Combat_Test_8b"); - case "1_16_combat-6": - return i18n("wiki.version.game.search", "Combat_Test_8c"); - } - - if (id.startsWith("1.0.0-rc2")) return "RC2"; - if (id.startsWith("2.0")) return i18n("wiki.version.game.search", "2.0"); - if (id.startsWith("b1.8-pre1")) return "Beta_1.8-pre1"; - if (id.startsWith("b1.1-")) return i18n("wiki.version.game.search", "Beta_1.1"); - if (id.startsWith("a1.1.0")) return "Alpha_v1.1.0"; - if (id.startsWith("a1.0.14")) return "Alpha_v1.0.14"; - if (id.startsWith("a1.0.13_01")) return "Alpha_v1.0.13_01"; - if (id.startsWith("in-20100214")) return i18n("wiki.version.game.search", "Indev_20100214"); - - if (id.contains("experimental-snapshot")) { - return id.replace("_experimental-snapshot-", "-exp"); - } - - if (id.startsWith("inf-")) return id.replace("inf-", "Infdev_"); - if (id.startsWith("in-")) return id.replace("in-", "Indev_"); - if (id.startsWith("rd-")) return "pre-Classic_" + id; - if (id.startsWith("b")) return id.replace("b", "Beta_"); - if (id.startsWith("a")) return id.replace("a", "Alpha_v"); - if (id.startsWith("c")) return id.replace("c", "Classic_").replace("st", "SURVIVAL_TEST"); - - return i18n("wiki.version.game.search", id); - } } private static final class VersionsPageSkin extends SkinBase { 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 c7e0cc45a..72459ab12 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 @@ -18,13 +18,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 java.time.temporal.TemporalAccessor; import java.util.*; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; - public final class I18n { private I18n() { @@ -51,24 +50,11 @@ public final class I18n { } public static String i18n(String key, Object... formatArgs) { - try { - return String.format(getResourceBundle().getString(key), formatArgs); - } catch (MissingResourceException e) { - LOG.error("Cannot find key " + key + " in resource bundle", e); - } catch (IllegalFormatException e) { - LOG.error("Illegal format string, key=" + key + ", args=" + Arrays.toString(formatArgs), e); - } - - return key + Arrays.toString(formatArgs); + return locale.i18n(key, formatArgs); } public static String i18n(String key) { - try { - return getResourceBundle().getString(key); - } catch (MissingResourceException e) { - LOG.error("Cannot find key " + key + " in resource bundle", e); - return key; - } + return locale.i18n(key); } public static String formatDateTime(TemporalAccessor time) { @@ -79,6 +65,10 @@ public final class I18n { return locale.getDisplaySelfVersion(version); } + public static String getWikiLink(GameRemoteVersion remoteVersion) { + return MinecraftWiki.getWikiLink(locale, remoteVersion); + } + public static boolean hasKey(String key) { return getResourceBundle().containsKey(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 67f30a20d..b4daba9e4 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 @@ -188,6 +188,27 @@ public final class Locales { return bundle; } + public String i18n(String key, Object... formatArgs) { + try { + return String.format(getResourceBundle().getString(key), formatArgs); + } catch (MissingResourceException e) { + LOG.error("Cannot find key " + key + " in resource bundle", e); + } catch (IllegalFormatException e) { + LOG.error("Illegal format string, key=" + key + ", args=" + Arrays.toString(formatArgs), e); + } + + return key + Arrays.toString(formatArgs); + } + + public String i18n(String key) { + try { + return getResourceBundle().getString(key); + } catch (MissingResourceException e) { + LOG.error("Cannot find key " + key + " in resource bundle", e); + return key; + } + } + public String formatDateTime(TemporalAccessor time) { DateTimeFormatter formatter = dateTimeFormatter; if (formatter == null) @@ -202,7 +223,7 @@ public final class Locales { public String getFcMatchPattern() { String language = locale.getLanguage(); - String country = locale.getCountry(); + String region = locale.getCountry(); if (isEnglish(locale)) return ""; @@ -212,13 +233,13 @@ public final class Locales { String charset; if (isSimplifiedChinese(locale)) { - lang = country.equals("SG") || country.equals("MY") - ? "zh-" + country + lang = region.equals("SG") || region.equals("MY") + ? "zh-" + region : "zh-CN"; charset = "0x6e38,0x620f"; } else { - lang = country.equals("HK") || country.equals("MO") - ? "zh-" + country + lang = region.equals("HK") || region.equals("MO") + ? "zh-" + region : "zh-TW"; charset = "0x904a,0x6232"; } @@ -226,7 +247,7 @@ public final class Locales { return ":lang=" + lang + ":charset=" + charset; } - return country.isEmpty() ? language : language + "-" + country; + return region.isEmpty() ? language : language + "-" + region; } public boolean isSameLanguage(SupportedLocale other) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/MinecraftWiki.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/MinecraftWiki.java new file mode 100644 index 000000000..d5a34eb18 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/MinecraftWiki.java @@ -0,0 +1,183 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.util.i18n; + +import org.jackhuang.hmcl.download.game.GameRemoteVersion; +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; + +import java.util.Locale; +import java.util.regex.Pattern; + +public final class MinecraftWiki { + + private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("^[0-9]{2}w[0-9]{2}.+$"); + + public static String getWikiLink(Locales.SupportedLocale locale, GameRemoteVersion version) { + String wikiVersion = version.getSelfVersion(); + var gameVersion = GameVersionNumber.asGameVersion(wikiVersion); + + if (locale.getLocale().getLanguage().equals("lzh")) { + String translatedVersion; + if (wikiVersion.startsWith("2.0")) + translatedVersion = "二點〇"; + else if (wikiVersion.startsWith("1.0.0-rc2")) + translatedVersion = WenyanUtils.translateGameVersion(GameVersionNumber.asGameVersion("1.0.0-rc2")); + else + translatedVersion = WenyanUtils.translateGameVersion(gameVersion); + + if (translatedVersion.equals(gameVersion.toString()) || gameVersion instanceof GameVersionNumber.Old) { + return getWikiLink(Locales.ZH_HANT, version); + } else if (SNAPSHOT_PATTERN.matcher(wikiVersion).matches()) { + return locale.i18n("wiki.version.game.snapshot", translatedVersion); + } else { + return locale.i18n("wiki.version.game", translatedVersion); + } + } + + String variantSuffix; + if (Locales.isChinese(locale.getLocale())) { + if (Locales.isSimplifiedChinese(locale.getLocale())) + variantSuffix = "?variant=zh-cn"; + else if (locale.getLocale().getCountry().equals("HK") || locale.getLocale().getCountry().equals("MO")) + variantSuffix = "?variant=zh-hk"; + else + variantSuffix = "?variant=zh-tw"; + } else + variantSuffix = ""; + + replace: + if (gameVersion instanceof GameVersionNumber.Release) { + if (wikiVersion.startsWith("1.0")) { + if (wikiVersion.equals("1.0")) + wikiVersion = "1.0.0"; + else if (wikiVersion.startsWith("1.0.0-rc2")) + wikiVersion = "1.0.0-rc2"; + } + } else if (gameVersion instanceof GameVersionNumber.Snapshot) { + return locale.i18n("wiki.version.game.snapshot", wikiVersion) + variantSuffix; + } else { + if (wikiVersion.length() >= 6 && wikiVersion.charAt(2) == 'w') { + // Starting from 2020, all April Fools' versions follow this pattern + if (SNAPSHOT_PATTERN.matcher(wikiVersion).matches()) { + if (wikiVersion.equals("22w13oneblockatatime")) + wikiVersion = "22w13oneBlockAtATime"; + return locale.i18n("wiki.version.game.snapshot", wikiVersion) + variantSuffix; + } + } + + String lower = wikiVersion.toLowerCase(Locale.ROOT); + switch (lower) { + case "0.30-1": + case "0.30-2": + case "c0.30_01c": + wikiVersion = "Classic_0.30"; + break replace; + case "in-20100206-2103": + wikiVersion = "Indev_20100206"; + break replace; + case "inf-20100630-1": + wikiVersion = "Infdev_20100630"; + break replace; + case "inf-20100630-2": + wikiVersion = "Alpha_v1.0.0"; + break replace; + case "1.19_deep_dark_experimental_snapshot-1": + wikiVersion = "1.19-exp1"; + break replace; + case "in-20100130": + wikiVersion = "Indev_0.31_20100130"; + break replace; + case "b1.6-tb3": + wikiVersion = "Beta_1.6_Test_Build_3"; + break replace; + case "1.14_combat-212796": + wikiVersion = "1.14.3_-_Combat_Test"; + break replace; + case "1.14_combat-0": + wikiVersion = "Combat_Test_2"; + break replace; + case "1.14_combat-3": + wikiVersion = "Combat_Test_3"; + break replace; + case "1_15_combat-1": + wikiVersion = "Combat_Test_4"; + break replace; + case "1_15_combat-6": + wikiVersion = "Combat_Test_5"; + break replace; + case "1_16_combat-0": + wikiVersion = "Combat_Test_6"; + break replace; + case "1_16_combat-1": + wikiVersion = "Combat_Test_7"; + break replace; + case "1_16_combat-2": + wikiVersion = "Combat_Test_7b"; + break replace; + case "1_16_combat-3": + wikiVersion = "Combat_Test_7c"; + break replace; + case "1_16_combat-4": + wikiVersion = "Combat_Test_8"; + break replace; + case "1_16_combat-5": + wikiVersion = "Combat_Test_8b"; + break replace; + case "1_16_combat-6": + wikiVersion = "Combat_Test_8c"; + break replace; + } + + if (lower.startsWith("2.0")) + wikiVersion = "2.0"; + else if (lower.startsWith("b1.8-pre1")) + wikiVersion = "Beta_1.8-pre1"; + else if (lower.startsWith("b1.1-")) + wikiVersion = "Beta_1.1"; + else if (lower.startsWith("a1.1.0")) + wikiVersion = "Alpha_v1.1.0"; + else if (lower.startsWith("a1.0.14")) + wikiVersion = "Alpha_v1.0.14"; + else if (lower.startsWith("a1.0.13_01")) + wikiVersion = "Alpha_v1.0.13_01"; + else if (lower.startsWith("in-20100214")) + wikiVersion = "Indev_20100214"; + else if (lower.contains("experimental-snapshot")) + wikiVersion = lower.replace("_experimental-snapshot-", "-exp"); + else if (lower.startsWith("inf-")) + wikiVersion = lower.replace("inf-", "Infdev_"); + else if (lower.startsWith("in-")) + wikiVersion = lower.replace("in-", "Indev_"); + else if (lower.startsWith("rd-")) + wikiVersion = "pre-Classic_" + lower; + else if (lower.startsWith("b")) + wikiVersion = lower.replace("b", "Beta_"); + else if (lower.startsWith("a")) + wikiVersion = lower.replace("a", "Alpha_v"); + else if (lower.startsWith("c")) + wikiVersion = lower + .replace("c", "Classic_") + .replace("st", "SURVIVAL_TEST"); + } + + return locale.i18n("wiki.version.game", wikiVersion) + variantSuffix; + } + + private MinecraftWiki() { + } +} diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index cbec63502..6a3f62335 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1483,8 +1483,8 @@ version.settings=Settings version.update=Update Modpack wiki.tooltip=Minecraft Wiki Page -wiki.version.game=https://minecraft.wiki/w/Special:Search?search=%s -wiki.version.game.search=Java_Edition_%s +wiki.version.game=https://minecraft.wiki/w/Java_Edition_%s +wiki.version.game.snapshot=https://minecraft.wiki/w/Java_Edition_%s wizard.prev=< Prev wizard.failed=Failed diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 63d50ab0e..a08a13c82 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -1482,8 +1482,8 @@ version.settings=Configuración version.update=Actualizar Modpack wiki.tooltip=Página de Minecraft Wiki -wiki.version.game=https://es.minecraft.wiki/w/Special:Search?search=%s -wiki.version.game.search=Java_Edition_%s +wiki.version.game=https://es.minecraft.wiki/w/Java_Edition_%s +wiki.version.game.snapshot=https://es.minecraft.wiki/w/Java_Edition_%s wizard.prev=< Previo wizard.failed=Falló diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index 768b72fcd..5cc7ce9f1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -962,8 +962,8 @@ version.manage.rename.fail=バージョンの名前を変更できませんで version.settings=設定 version.update=modpackを更新する -wiki.version.game=https://ja.minecraft.wiki/w/Special:Search?search=%s -wiki.version.game.search=Java_Edition_%s +wiki.version.game=https://ja.minecraft.wiki/w/Java_Edition_%s +wiki.version.game.snapshot=https://ja.minecraft.wiki/w/Java_Edition_%s wizard.prev=< 前へ wizard.failed=失敗 diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 4a4e55fb9..1f64c8dc1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -1196,8 +1196,8 @@ version.settings=規設 version.update=更改囊集 wiki.tooltip=礦藝大典 -wiki.version.game=https://zh.minecraft.wiki/w/Special:Search?search=%s&variant=zh-tw -wiki.version.game.search=Java版%s +wiki.version.game=https://lzh.minecraft.wiki/w/爪哇版%s +wiki.version.game.snapshot=https://lzh.minecraft.wiki/w/%s wizard.prev=< 前步 wizard.failed=未成 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index dbf6e15f5..f25afb1c3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -1482,8 +1482,8 @@ version.settings=Настройки version.update=Обновить модпак wiki.tooltip=Страница Minecraft Wiki -wiki.version.game=https://ru.minecraft.wiki/w/Special:Search?search=%s -wiki.version.game.search=%s_(Java_Edition) +wiki.version.game=https://ru.minecraft.wiki/w/%s_(Java_Edition) +wiki.version.game.snapshot=https://ru.minecraft.wiki/w/%s_(Java_Edition) wizard.prev=< Пред. wizard.failed=Не удалось diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 4870f2fd7..525038c87 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -1427,8 +1427,8 @@ version.settings=Налаштування version.update=Оновити модпак wiki.tooltip=Сторінка Minecraft Wiki -wiki.version.game=https://uk.minecraft.wiki/w/Special:Search?search=%s -wiki.version.game.search=%s_(Java_Edition) +wiki.version.game=https://uk.minecraft.wiki/w/%s_(Java_Edition) +wiki.version.game.snapshot=https://uk.minecraft.wiki/w/%s_(Java_Edition) wizard.prev=< Попередній wizard.failed=Не вдалося diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 64259de43..7c9b71ffa 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1269,8 +1269,8 @@ version.settings=遊戲設定 version.update=更新模組包 wiki.tooltip=Minecraft Wiki 頁面 -wiki.version.game=https://zh.minecraft.wiki/w/Special:Search?search=%s&variant=zh-tw -wiki.version.game.search=Java版%s +wiki.version.game=https://zh.minecraft.wiki/w/Java版%s +wiki.version.game.snapshot=https://zh.minecraft.wiki/w/%s wizard.prev=< 上一步 wizard.failed=失敗 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 67c04812c..abdc7cb1f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1279,8 +1279,8 @@ version.settings=游戏设置 version.update=更新整合包 wiki.tooltip=Minecraft Wiki 页面 -wiki.version.game=https://zh.minecraft.wiki/w/Special:Search?search=%s&variant=zh-cn -wiki.version.game.search=Java版%s +wiki.version.game=https://zh.minecraft.wiki/w/Java版%s +wiki.version.game.snapshot=https://zh.minecraft.wiki/w/%s wizard.prev=< 上一步 wizard.failed=失败