feat(mod): Chinese translations for mod names.
This commit is contained in:
@@ -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()))
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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(), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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, "", "", "", "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user