This commit is contained in:
Glavo 2025-09-21 16:40:41 +08:00
parent 65402fa914
commit 7992260575

View File

@ -0,0 +1,159 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 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.gradle.l10n;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/// @author Glavo
public abstract class UpsideDownTranslate extends DefaultTask {
@InputFile
public abstract RegularFileProperty getInputFile();
@OutputFile
public abstract RegularFileProperty getOutputFile();
@TaskAction
public void run() throws IOException {
Path inputFile = getInputFile().get().getAsFile().toPath();
Path outputFile = getOutputFile().get().getAsFile().toPath();
Properties english = new Properties();
try (var reader = Files.newBufferedReader(inputFile)) {
english.load(reader);
}
Properties output = new Properties();
Translator translator = new Translator();
english.forEach((k, v) -> output.put(k, translator.translate(v.toString())));
try (var writer = Files.newBufferedWriter(outputFile)) {
output.store(writer, "This file is automatically generated, please do not modify it manually");
}
}
private static final class Translator {
private static final Map<Integer, Integer> mapper = new LinkedHashMap<>();
private static void putChars(char baseChar, String upsideDownChars) {
for (int i = 0; i < upsideDownChars.length(); i++) {
mapper.put(baseChar + i, (int) upsideDownChars.charAt(i));
}
}
private static void putChars(String baseChars, String upsideDownChars) {
if (baseChars.length() != upsideDownChars.length()) {
throw new IllegalArgumentException("baseChars and upsideDownChars must have same length");
}
for (int i = 0; i < baseChars.length(); i++) {
mapper.put((int) baseChars.charAt(i), (int) upsideDownChars.charAt(i));
}
}
static {
putChars('a', "ɐqɔpǝɟbɥıظʞןɯuuodbɹsʇnʌʍxʎz");
putChars('A', "ⱯᗺƆᗡƎℲ⅁HIſʞꞀWNOԀὉᴚS⟘∩ΛMXʎZ");
putChars('0', "0ƖᄅƐㄣϛ9ㄥ86");
putChars("_,;.?!/\\'", "‾'؛˙¿¡/\\,");
}
private static final Pattern FORMAT_PATTERN = Pattern.compile("^%(\\d\\$)?[sd]");
private static final Pattern XML_TAG_PATTERN = Pattern.compile("^<(?<tag>[a-zA-Z]+)( href=\"[^\"]*\")?>");
private final StringBuilder lineBuilder = new StringBuilder();
private void appendToLineBuilder(String input) {
for (int i = 0; i < input.length(); ) {
int ch = input.codePointAt(i);
if (ch == '%') {
Matcher matcher = FORMAT_PATTERN.matcher(input).region(i, input.length());
if (matcher.find()) {
String formatString = matcher.group();
lineBuilder.insert(0, formatString);
i += formatString.length();
continue;
}
} else if (ch == '<') {
Matcher matcher = XML_TAG_PATTERN.matcher(input).region(i, input.length());
if (matcher.find()) {
String beginTag = matcher.group();
String endTag = "</" + matcher.group(1) + ">";
int endTagOffset = input.indexOf(endTag, i + beginTag.length());
if (endTagOffset > 0) {
lineBuilder.insert(0, endTag);
appendToLineBuilder(input.substring(i + beginTag.length(), endTagOffset));
lineBuilder.insert(0, beginTag);
i = endTagOffset + endTag.length();
continue;
}
}
}
Integer udCh = mapper.getOrDefault(ch, ch);
if (Character.isBmpCodePoint(udCh)) {
lineBuilder.insert(0, udCh);
} else {
lineBuilder.insert(0, Character.toChars(udCh));
}
i += Character.charCount(ch);
}
}
private final StringBuilder resultBuilder = new StringBuilder();
String translate(String input) {
resultBuilder.setLength(0);
for (int i = 0; i < input.length(); ) {
int idx = input.indexOf('\n');
String line = input.substring(0, idx < 0 ? input.length() : idx);
this.lineBuilder.setLength(0);
appendToLineBuilder(line);
resultBuilder.append(lineBuilder);
if (idx < 0) {
break;
} else {
resultBuilder.append('\n');
i = idx + 1;
}
}
return resultBuilder.toString();
}
}
}