Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ jobs:
uses: ./.github/workflows/common.yml
with:
type: Beta
build-task: buildRelease
artifact-path: build/libs/I18nUpdateMod-*-all.jar
secrets: inherit
18 changes: 13 additions & 5 deletions .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ on:
change-log:
required: false
type: string

build-task:
required: false
type: string
default: buildRelease
artifact-path:
required: false
type: string
default: build/libs/I18nUpdateMod-*-all.jar
jobs:
build-common:
name: Build Common
Expand All @@ -34,10 +41,10 @@ jobs:
env:
IS_SNAPSHOT: ${{ inputs.is-snapshot }}
run: |
./gradlew clean shadowJar --info --stacktrace
./gradlew clean ${{ inputs.build-task }} --info --stacktrace
-
name: Publish Modrinth
if: ${{ !inputs.is-snapshot }}
if: ${{ !inputs.is-snapshot && inputs.build-task == 'buildRelease' }}
env:
IS_SNAPSHOT: ${{ inputs.is-snapshot }}
CHANGE_LOG: ${{ inputs.change-log }}
Expand All @@ -46,7 +53,7 @@ jobs:
./gradlew modrinth modrinthSyncBody --info --stacktrace
-
name: Publish CurseForge
if: ${{ !inputs.is-snapshot }}
if: ${{ !inputs.is-snapshot && inputs.build-task == 'buildRelease' }}
env:
IS_SNAPSHOT: ${{ inputs.is-snapshot }}
CHANGE_LOG: ${{ inputs.change-log }}
Expand All @@ -57,4 +64,5 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: I18nUpdateMod-${{ inputs.type }}-${{ github.run_number }}
path: build/libs
path: ${{ inputs.artifact-path }}
if-no-files-found: error
6 changes: 4 additions & 2 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ on:
- reopened
branches:
- 'main'

workflow_dispatch:
jobs:
build-pull-request:
name: Build Pull Request
uses: ./.github/workflows/common.yml
with:
type: PullRequest
type: PullRequest
build-task: buildDebug
artifact-path: build/libs/I18nUpdateMod-*-debug.jar
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ jobs:
type: Release
is-snapshot: false
change-log: ${{ github.event.release.body }}
build-task: buildRelease
artifact-path: build/libs/I18nUpdateMod-*-all.jar
secrets: inherit
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

更现代的自动汉化更新模组。

「[简体中文资源包(Minecraft Mod Language Package)](https://github.com/CFPAOrg/Minecraft-Mod-Language-Package)」是由「[CFPAOrg](http://cfpa.team/)」团队维护的「自动汉化资源包」,可以将一些Mod中的文本翻译为中文。
「[简体中文资源包(Minecraft Mod Language Package)](https://github.com/CFPAOrg/Minecraft-Mod-Language-Package)
」是由「[CFPAOrg](http://cfpa.team/)」团队维护的「自动汉化资源包」,可以将一些Mod中的文本翻译为中文。
本Mod用于自动下载、更新、应用「简体中文资源包」。

## 下载
Expand All @@ -21,23 +22,26 @@

## 支持的版本

- Minecraft:1.6.1~1.21.10 都支持
- Minecraft:1.6.1~1.21.11、26.1~26.1.2 都支持
- Mod加载器:MinecraftForge、NeoForge、Fabric、Quilt 都支持
- Java:8~21 都支持
- Java:8~25 都支持

仅仅需要在mods文件夹中放置本Mod的jar文件即可,Mod本身与各主流Minecraft版本、Mod Loader、Java版本均兼容,Mod本身不需要进行任何版本隔离。

## 支持的「简体中文资源包」

为了尽可能实用,目前本Mod会根据游戏版本自动下载、合并、转换「简体中文资源包」。

- 官方资源:1.10.2、1.12.2、1.16、1.18、1.19、1.20、1.21
- 官方资源:1.10.2、1.12.2、1.16、1.18、1.19、1.20、1.21、26.1
- 合并转换:会合并加转换最近版本的一些资源包,尽可能做最大化的支持
- 特别说明:1.13开始将语言文件变化为json格式,所以不能将1.12.2的资源包用于1.13以上,反之同理

V2 按模组下载翻译时最多使用 16 路并行,结果按完成顺序收集,不维护组包顺序,缓存策略不变;旧版整包下载回退流程保持原策略。

## 开发环境

请使用Java 8及以上的JDK构建。
构建需要 JDK 21(用于编译新版 NeoForge 的服务接口);产物仍使用 Java 8 字节码,旧版游戏的 Java 运行要求不变。

```shell
gradle clean shadowJar
```
70 changes: 66 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

plugins {
id("java")
id("com.github.johnrengelman.shadow") version "8.1.1"
Expand All @@ -9,15 +11,16 @@ group = "i18nupdatemod"
version = project.properties["version"].toString() + if ("false" == System.getenv("IS_SNAPSHOT")) "" else "-SNAPSHOT"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.release.set(8)
}

tasks.shadowJar {
fun ShadowJar.configureI18nPackaging() {
manifest {
attributes(
"TweakClass" to "i18nupdatemod.launchwrapper.LaunchWrapperTweaker",
Expand All @@ -28,17 +31,56 @@ tasks.shadowJar {
minimize()
archiveBaseName.set("I18nUpdateMod")
relocate("com.google.archivepatcher", "include.com.google.archivepatcher")
relocate("org.tukaani.xz", "include.org.tukaani.xz")
dependencies {
include(dependency("net.runelite.archive-patcher:archive-patcher-applier:.*"))
include(dependency("org.tukaani:xz:.*"))
}
exclude("LICENSE")
}

tasks.shadowJar {
configureI18nPackaging()
}

tasks.register<ShadowJar>("shadowJarDebug") {
from(sourceSets.main.get().output)
configurations = listOf(project.configurations.runtimeClasspath.get())
archiveClassifier.set("debug")
configureI18nPackaging()
}

mapOf(
"Release" to "http://downloader1.meitangdehulu.com:22943/",
"Debug" to "https://i18dl.imc.wiki/",
).forEach { (variant, baseUrl) ->
val configFile = layout.buildDirectory.file("generated/buildConfig/$variant/i18n-build.properties")
val generateConfig = tasks.register("generate${variant}Config") {
inputs.property("assetBaseUrl", baseUrl)
outputs.file(configFile)
doLast {
configFile.get().asFile.apply {
parentFile.mkdirs()
writeText("assetBaseUrl=$baseUrl\n")
}
}
}
val archive = tasks.named<ShadowJar>(if (variant == "Release") "shadowJar" else "shadowJarDebug") {
from(generateConfig)
}
tasks.register("build$variant") {
group = "build"
description = "Build the ${variant.lowercase()} artifact."
dependsOn(archive)
}
}

repositories {
mavenCentral()
maven("https://libraries.minecraft.net/")
maven("https://maven.fabricmc.net/")
maven("https://files.minecraftforge.net/maven")
maven("https://maven.neoforged.net/releases/")
maven("https://repo.runelite.net/")
}

Expand All @@ -49,24 +91,44 @@ configurations.configureEach {
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.3")
// Transitive resolution is disabled, including for the test configurations.
testImplementation("org.apiguardian:apiguardian-api:1.1.2")
testImplementation("org.opentest4j:opentest4j:1.3.0")
testImplementation("org.junit.platform:junit-platform-commons:1.10.3")
testRuntimeOnly("org.junit.platform:junit-platform-engine:1.10.3")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.10.3")
implementation("net.runelite.archive-patcher:archive-patcher-applier:1.2")
implementation("org.tukaani:xz:1.10")
// Forge 1.13.x provides NightConfig 3.6.0; keep that API baseline without bundling it.
compileOnly("com.electronwill.night-config:core:3.6.0")
compileOnly("com.electronwill.night-config:toml:3.6.0")
testRuntimeOnly("com.electronwill.night-config:core:3.6.0")
testRuntimeOnly("com.electronwill.night-config:toml:3.6.0")
compileOnly("org.jetbrains:annotations:24.1.0")
// Only the early-service interface is linked; never bundle loader implementation classes.
compileOnly("net.neoforged.fancymodloader:loader:10.0.34") {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}

implementation("net.fabricmc:fabric-loader:0.15.9")
implementation("cpw.mods:modlauncher:8.1.3")
implementation("net.minecraft:launchwrapper:1.12")

implementation("commons-io:commons-io:2.16.1")
implementation("org.ow2.asm:asm:9.7")
implementation("com.google.code.gson:gson:2.11.0")
implementation("org.ow2.asm:asm-tree:9.7")
// Minecraft supplies Gson at runtime; compile against the 1.7.10 API baseline.
implementation("com.google.code.gson:gson:2.2.4")

}

tasks.test {
useJUnitPlatform()
}

tasks.processResources {
exclude("i18n-build.properties")
filesMatching("**") {
expand(
"version" to project.version,
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version=3.7.1
minecraft=1.6.1,1.6.2,1.6.4,1.7.2,1.7.10,1.8,1.8.8,1.8.9,1.9,1.9.4,1.10,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2,1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11
minecraft=1.6.1,1.6.2,1.6.4,1.7.2,1.7.10,1.8,1.8.8,1.8.9,1.9,1.9.4,1.10,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2,1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1,26.1.1,26.1.2
curseforge=NeoForge,Forge,Fabric,Quilt,Client,Java 8,Java 9,Java 10,Java 11,Java 12,Java 13,Java 14,Java 15,Java 16,Java 17,Java 18
58 changes: 17 additions & 41 deletions src/main/java/i18nupdatemod/I18nUpdateMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,19 @@
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import i18nupdatemod.core.GameConfig;
import i18nupdatemod.core.I18nConfig;
import i18nupdatemod.core.ResourcePack;
import i18nupdatemod.core.ResourcePackConverter;
import i18nupdatemod.entity.GameAssetDetail;
import i18nupdatemod.entity.GameMetaData;
import i18nupdatemod.util.FileUtil;
import i18nupdatemod.core.ResourcePackUpdater;
import i18nupdatemod.core.RuntimePackActivation;
import i18nupdatemod.entity.ModTranslation;
import i18nupdatemod.util.Log;
import i18nupdatemod.util.Version;
import org.jetbrains.annotations.NotNull;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class I18nUpdateMod {
Expand All @@ -29,14 +24,14 @@ public class I18nUpdateMod {

public static final Gson GSON = new Gson();

public static void init(Path minecraftPath, String minecraftVersion, String loader, @NotNull HashSet<String> modDomainsSet) {
public static void init(Path minecraftPath, String minecraftVersion, String loader, @NotNull List<ModTranslation> mods) {
try (InputStream is = I18nUpdateMod.class.getResourceAsStream("/i18nMetaData.json")) {
MOD_VERSION = GSON.fromJson(new InputStreamReader(is), JsonObject.class).get("version").getAsString();
} catch (Exception e) {
Log.warning("Error getting version: " + e);
}

modDomainsSet.remove("i18nupdatemod");
mods.removeIf(mod -> MOD_ID.equals(mod.namespace));

Log.info(String.format("I18nUpdate Mod %s is loaded in %s with %s", MOD_VERSION, minecraftVersion, loader));
Log.debug(String.format("Minecraft path: %s", minecraftPath));
Expand All @@ -56,49 +51,30 @@ public static void init(Path minecraftPath, String minecraftVersion, String load
} catch (ClassNotFoundException ignored) {
}

FileUtil.setResourcePackDirPath(minecraftPath.resolve("resourcepacks"));

int minecraftMajorVersion = Integer.parseInt(minecraftVersion.split("\\.")[1]);

try {
//Get asset
GameAssetDetail assets = I18nConfig.getAssetDetail(minecraftVersion, loader);

//Update resource pack
List<ResourcePack> languagePacks = new ArrayList<>();
for (GameAssetDetail.AssetDownloadDetail it : assets.downloads) {
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, it.targetVersion));
ResourcePack languagePack = new ResourcePack(it.fileName);
languagePack.checkUpdate(it.fileUrl, it.md5Url);
languagePacks.add(languagePack);
Path resourcePackDirectory = minecraftPath.resolve("resourcepacks");
Path cacheRoot = Paths.get(localStorage, "." + MOD_ID);
Path convertedPack = ResourcePackUpdater.update(
minecraftVersion, loader, mods, resourcePackDirectory, cacheRoot);

// NeoForge applies the selection after its resource repository is populated.
if (RuntimePackActivation.isEnabled()) {
RuntimePackActivation.prepare(minecraftPath, convertedPack);
return;
}

//Convert resourcepack
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, minecraftVersion));
String applyFileName = assets.covertFileName;
GameMetaData metaData = I18nConfig.getPackFormat(minecraftVersion);
ResourcePackConverter converter = new ResourcePackConverter(languagePacks, applyFileName);
converter.convert(metaData, getResourcePackDescription(assets.downloads), modDomainsSet);

//Apply resource pack
GameConfig config = new GameConfig(minecraftPath.resolve("options.txt"));
config.addResourcePack("Minecraft-Mod-Language-Modpack",
(minecraftMajorVersion <= 12 ? "" : "file/") + applyFileName);
(Version.from(minecraftVersion).compareTo(Version.from("1.13")) < 0 ? "" : "file/")
+ convertedPack.getFileName().toString());
config.writeToFile();
} catch (Exception e) {
Log.warning(String.format("Failed to update resource pack: %s", e));
// e.printStackTrace();
}
}

private static String getResourcePackDescription(List<GameAssetDetail.AssetDownloadDetail> downloads) {
return downloads.size() > 1 ?
String.format("该包由%s版本合并\n作者:CFPA团队及汉化项目贡献者",
downloads.stream().map(it -> it.targetVersion).collect(Collectors.joining("和"))) :
String.format("该包对应的官方支持版本为%s\n作者:CFPA团队及汉化项目贡献者",
downloads.get(0).targetVersion);

}

public static String getLocalStoragePos(Path minecraftPath) {
Path userHome = Paths.get(System.getProperty("user.home"));
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/i18nupdatemod/core/GameConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ public void addResourcePack(String baseName, String resourcePack) {
Log.info(String.format("Resource Packs: %s", configs.get("resourcePacks")));
// configs.put("lang", "zh_cn");
}

}

Loading
Loading