feat(mod): Chinese translations for mod names.

This commit is contained in:
huanghongxun
2021-09-11 20:08:38 +08:00
parent 2d8248461a
commit f45e0abbf4
12 changed files with 228 additions and 23 deletions

View File

@@ -138,6 +138,7 @@ public class ModDownloadListPage extends Control implements DecoratorPage, Versi
setLoading(false);
if (exception == null) {
items.setAll(result);
failed.set(false);
} else {
failed.set(true);
}
@@ -301,7 +302,8 @@ public class ModDownloadListPage extends Control implements DecoratorPage, Versi
@Override
protected void updateControl(CurseAddon dataItem, boolean empty) {
if (empty) return;
content.setTitle(dataItem.getName());
ModTranslations.Mod mod = ModTranslations.getModByCurseForgeId(dataItem.getSlug());
content.setTitle(mod != null ? mod.getDisplayName() : dataItem.getName());
content.setSubtitle(dataItem.getSummary());
content.getTags().setAll(dataItem.getCategories().stream()
.map(category -> i18n("curse.category." + category.getCategoryId()))

View File

@@ -185,7 +185,8 @@ public class ModDownloadPage extends Control implements DecoratorPage {
TwoLineListItem content = new TwoLineListItem();
HBox.setHgrow(content, Priority.ALWAYS);
content.setTitle(getSkinnable().addon.getName());
ModTranslations.Mod mod = ModTranslations.getModByCurseForgeId(getSkinnable().addon.getSlug());
content.setTitle(mod != null ? mod.getDisplayName() : getSkinnable().addon.getName());
content.setSubtitle(getSkinnable().addon.getSummary());
content.getTags().setAll(getSkinnable().addon.getCategories().stream()
.map(category -> i18n("curse.category." + category.getCategoryId()))

View File

@@ -43,6 +43,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils;
@@ -53,6 +54,7 @@ import java.io.ByteArrayOutputStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2;
@@ -122,6 +124,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
private final BooleanProperty active;
private final ModInfo modInfo;
private final String message;
private final ModTranslations.Mod mod;
ModInfoObject(ModInfo modInfo) {
this.modInfo = modInfo;
@@ -134,6 +137,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
if (isNotBlank(modInfo.getAuthors()))
message.append(", ").append(i18n("archive.author")).append(": ").append(modInfo.getAuthors());
this.message = message.toString();
this.mod = ModTranslations.getModById(modInfo.getId());
}
String getTitle() {
@@ -148,6 +152,10 @@ class ModListPageSkin extends SkinBase<ModListPage> {
return modInfo;
}
public ModTranslations.Mod getMod() {
return mod;
}
@Override
public int compareTo(@NotNull ModListPageSkin.ModInfoObject o) {
return modInfo.getFileName().toLowerCase().compareTo(o.modInfo.getFileName().toLowerCase());
@@ -201,15 +209,24 @@ class ModListPageSkin extends SkinBase<ModListPage> {
JFXButton searchButton = new JFXButton();
searchButton.getStyleClass().add("dialog-cancel");
searchButton.setText(i18n("mods.mcmod.search"));
searchButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink(NetworkUtils.withQuery("https://search.mcmod.cn/s", mapOf(
pair("key", modInfo.getModInfo().getName()),
pair("site", "all"),
pair("filter", "0")
)));
});
if (modInfo.getMod() == null || StringUtils.isBlank(modInfo.getMod().getMcmod())) {
searchButton.setText(i18n("mods.mcmod.search"));
searchButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink(NetworkUtils.withQuery("https://search.mcmod.cn/s", mapOf(
pair("key", modInfo.getModInfo().getName()),
pair("site", "all"),
pair("filter", "0")
)));
});
} else {
searchButton.setText(i18n("mods.mcmod.page"));
searchButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink("https://www.mcmod.cn/class/" + modInfo.getMod().getMcmod() + ".html");
});
}
if (StringUtils.isNotBlank(modInfo.getModInfo().getUrl())) {
JFXButton officialPageButton = new JFXButton();
@@ -259,7 +276,12 @@ class ModListPageSkin extends SkinBase<ModListPage> {
@Override
protected void updateControl(ModInfoObject dataItem, boolean empty) {
if (empty) return;
content.setTitle(dataItem.getTitle());
if (dataItem.getMod() != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA) {
content.setTitle(dataItem.getMod().getDisplayName());
content.getTags().setAll(dataItem.getTitle());
} else {
content.setTitle(dataItem.getTitle());
}
content.setSubtitle(dataItem.getSubtitle());
if (booleanProperty != null) {
checkBox.selectedProperty().unbindBidirectional(booleanProperty);

View File

@@ -0,0 +1,165 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Logging.LOG;
/**
* Parser for mod_data.txt
*
* @see <a href="https://www.mcmod.cn">mcmod.cn</a>
*/
public final class ModTranslations {
private static List<Mod> mods;
private static Map<String, Mod> modIdMap; // mod id -> mod
private static Map<String, Mod> curseForgeMap; // curseforge id -> mod
private ModTranslations(){}
public static Mod getModByCurseForgeId(String id) {
if (StringUtils.isBlank(id) || !loadCurseForgeMap()) return null;
return curseForgeMap.get(id);
}
public static Mod getModById(String id) {
if (StringUtils.isBlank(id) || !loadModIdMap()) return null;
return modIdMap.get(id);
}
private static boolean loadFromResource() {
if (mods != null) return true;
try {
String modData = IOUtils.readFullyAsString(ModTranslations.class.getResourceAsStream("/assets/mod_data.txt"), StandardCharsets.UTF_8);
mods = Arrays.stream(modData.split("\n")).filter(line -> !line.startsWith("#")).map(Mod::new).collect(Collectors.toList());
return true;
} catch (Exception e) {
LOG.log(Level.WARNING, "Failed to load /assets/mod_data.txt", e);
return false;
}
}
private static boolean loadCurseForgeMap() {
if (curseForgeMap != null) {
return true;
}
if (mods == null) {
if (!loadFromResource()) return false;
}
curseForgeMap = new HashMap<>();
for (Mod mod : mods) {
if (StringUtils.isNotBlank(mod.getCurseforge())) {
curseForgeMap.put(mod.getCurseforge(), mod);
}
}
return true;
}
private static boolean loadModIdMap() {
if (modIdMap != null) {
return true;
}
if (mods == null) {
if (!loadFromResource()) return false;
}
modIdMap = new HashMap<>();
for (Mod mod : mods) {
for (String id : mod.getModIds()) {
modIdMap.put(id, mod);
}
}
return true;
}
public static class Mod {
private final String curseforge;
private final String mcmod;
private final String mcbbs;
private final List<String> modIds;
private final String name;
private final String subname;
public Mod(String line) {
String[] items = line.split(";", -1);
if (items.length != 6) {
throw new IllegalArgumentException("Illegal mod data line, 6 items expected " + line);
}
curseforge = items[0];
mcmod = items[1];
mcbbs = items[2];
modIds = Collections.unmodifiableList(Arrays.asList(items[3].split(",")));
name = items[4];
subname = items[5];
}
public Mod(String curseforge, String mcmod, String mcbbs, List<String> modIds, String name, String subname) {
this.curseforge = curseforge;
this.mcmod = mcmod;
this.mcbbs = mcbbs;
this.modIds = modIds;
this.name = name;
this.subname = subname;
}
public String getDisplayName() {
if (StringUtils.isBlank(subname)) {
return name;
} else {
return String.format("%s (%s)", name, subname);
}
}
public String getCurseforge() {
return curseforge;
}
public String getMcmod() {
return mcmod;
}
public String getMcbbs() {
return mcbbs;
}
public List<String> getModIds() {
return modIds;
}
public String getName() {
return name;
}
public String getSubname() {
return subname;
}
}
}

View File

@@ -1,3 +1,10 @@
#
# Hello Minecraft! Launcher
# Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
#
# This file is licensed under CC BY-NC-SA 3.0.
# Thanks to mcmod.cn and all contributors.
#
industrial-craft;2;515771;IC2,ic2;工业时代2;Industrial Craft 2
;3;;RedPowerCore,RedPowerBase;红石力量2;RedPower2
BuildCraft;4;884720;BuildCraft|Core,buildcraftlib,buildcraftcore,buildcraftbuilders,buildcrafttransport,buildcraftsilicon,buildcraftfactory,buildcraftrobotics,buildcraftenergy,BuildMod,kamenridercraft4th,BuildCraft|Energy,BuildCraft|Transport,BuildCraft|Factory,BuildCraft|Silicon,BuildCraft|Builders,BuildCraft|Robotics;建筑;BuildCraft