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

View File

@@ -42,6 +42,7 @@ import java.util.stream.Collectors;
@Immutable
public final class FabricModMetadata {
private final String id;
private final String name;
private final String version;
private final String description;
@@ -50,10 +51,11 @@ public final class FabricModMetadata {
private final Map<String, String> contact;
public FabricModMetadata() {
this("", "", "", "", Collections.emptyList(), Collections.emptyMap());
this("", "", "", "", "", Collections.emptyList(), Collections.emptyMap());
}
public FabricModMetadata(String name, String version, String icon, String description, List<FabricModAuthor> authors, Map<String, String> contact) {
public FabricModMetadata(String id, String name, String version, String icon, String description, List<FabricModAuthor> authors, Map<String, String> contact) {
this.id = id;
this.name = name;
this.version = version;
this.icon = icon;
@@ -69,7 +71,7 @@ public final class FabricModMetadata {
throw new IOException("File " + modFile + " is not a Fabric mod.");
FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class);
String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", "));
return new ModInfo(modManager, modFile, metadata.name, new ModInfo.Description(metadata.description),
return new ModInfo(modManager, modFile, metadata.id, metadata.name, new ModInfo.Description(metadata.description),
authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon);
}
}

View File

@@ -135,7 +135,7 @@ public final class ForgeNewModMetadata {
LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile.getPath());
}
}
return new ModInfo(modManager, modFile, mod.getDisplayName(), new ModInfo.Description(mod.getDescription()),
return new ModInfo(modManager, modFile, mod.getModId(), mod.getDisplayName(), new ModInfo.Description(mod.getDescription()),
mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
mod.getDisplayURL(),
metadata.getLogoFile());

View File

@@ -138,7 +138,7 @@ public final class ForgeOldModMetadata {
authors = String.join(", ", metadata.getAuthorList());
if (StringUtils.isBlank(authors))
authors = metadata.getCredits();
return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()),
return new ModInfo(modManager, modFile, metadata.getModId(), metadata.getName(), new ModInfo.Description(metadata.getDescription()),
authors, metadata.getVersion(), metadata.getGameVersion(),
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
metadata.getLogoFile());

View File

@@ -116,7 +116,7 @@ public final class LiteModMetadata {
LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class);
if (metadata == null)
throw new IOException("Mod " + modFile + " `litemod.json` is malformed.");
return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(),
return new ModInfo(modManager, modFile, null, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(),
metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), "");
}
}

View File

@@ -38,6 +38,7 @@ import java.util.logging.Level;
public final class ModInfo implements Comparable<ModInfo> {
private Path file;
private final String id;
private final String name;
private final Description description;
private final String authors;
@@ -48,12 +49,13 @@ public final class ModInfo implements Comparable<ModInfo> {
private final String logoPath;
private final BooleanProperty activeProperty;
public ModInfo(ModManager modManager, File file, String name, Description description) {
this(modManager, file, name, description, "", "", "", "", "");
public ModInfo(ModManager modManager, File file, String id, String name, Description description) {
this(modManager, file, id, name, description, "", "", "", "", "");
}
public ModInfo(ModManager modManager, File file, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) {
public ModInfo(ModManager modManager, File file, String id, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) {
this.file = file.toPath();
this.id = id;
this.name = name;
this.description = description;
this.authors = authors;
@@ -85,6 +87,10 @@ public final class ModInfo implements Comparable<ModInfo> {
return file;
}
public String getId() {
return id;
}
public String getName() {
return name;
}

View File

@@ -99,7 +99,7 @@ public final class ModManager {
default:
throw new IllegalArgumentException("File " + modFile + " is not a mod file.");
}
return new ModInfo(this, modFile, FileUtils.getNameWithoutExtension(modFile), new ModInfo.Description(description));
return new ModInfo(this, modFile, null, FileUtils.getNameWithoutExtension(modFile), new ModInfo.Description(description));
}
public void refreshMods() throws IOException {

View File

@@ -150,7 +150,7 @@ public class PackMcMeta implements Validation {
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a resource pack.");
PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class);
return new ModInfo(modManager, modFile, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", "");
return new ModInfo(modManager, modFile, null, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", "");
}
}
}