Skip to content
Open
47 changes: 28 additions & 19 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,31 +114,40 @@ private void generateOptionsTxt() {
private static String normalizedLanguageTag(Locale locale, GameVersionNumber gameVersion) {
String region = locale.getCountry();

if ("en".equals(LocaleUtils.getRootLanguage(locale))) {
if ("Qabs".equals(LocaleUtils.getScript(locale)) && gameVersion.compareTo("1.16") >= 0) {
return "en_UD";
}

return "";
}

if ("zh".equals(LocaleUtils.getRootLanguage(locale))) {
if ("lzh".equals(locale.getLanguage())) {
return gameVersion.compareTo("1.16") >= 0 ? "lzh" : "zh_TW";
}

if ("Hant".equals(LocaleUtils.getScript(locale))) {
if (region.equals("HK") || region.equals("MO")) {
return gameVersion.compareTo("1.16") >= 0 ? "zh_HK" : "zh_TW";
}
return "zh_TW";
}

return "zh_CN";
}

String languageTag = LocaleUtils.getMinecraftLanguageTag(locale);
if (languageTag != null && languageTag.contains("_")) {
return languageTag;
}

return switch (LocaleUtils.getRootLanguage(locale)) {
case "ar" -> "ar_SA";
case "es" -> "es_ES";
case "ja" -> "ja_JP";
case "ru" -> "ru_RU";
case "uk" -> "uk_UA";
case "zh" -> {
if ("lzh".equals(locale.getLanguage()) && gameVersion.compareTo("1.16") >= 0)
yield "lzh";

String script = LocaleUtils.getScript(locale);
if ("Hant".equals(script)) {
if ((region.equals("HK") || region.equals("MO") && gameVersion.compareTo("1.16") >= 0))
yield "zh_HK";
yield "zh_TW";
}
yield "zh_CN";
}
case "en" -> {
if ("Qabs".equals(LocaleUtils.getScript(locale)) && gameVersion.compareTo("1.16") >= 0) {
yield "en_UD";
}

yield "";
}
default -> "";
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.io.FileUtils;

import java.io.ByteArrayInputStream;
Expand All @@ -45,6 +46,7 @@
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Stream;

Expand Down Expand Up @@ -80,12 +82,13 @@ public void loadVersion(Profile profile, String version) {
public void refresh() {
if (resourcepackDirectory == null || !Files.isDirectory(resourcepackDirectory)) return;
setLoading(true);
Locale locale = I18n.getLocale().getLocale();
Task.supplyAsync(Schedulers.io(), () -> {
try (Stream<Path> stream = Files.list(resourcepackDirectory)) {
return stream.sorted(Comparator.comparing(FileUtils::getName))
.flatMap(item -> {
try {
return Stream.of(ResourcepackFile.parse(item)).filter(Objects::nonNull).map(ResourcepackInfoObject::new);
return Stream.of(ResourcepackFile.parse(item, locale)).filter(Objects::nonNull).map(ResourcepackInfoObject::new);
} catch (IOException e) {
LOG.warning("Failed to load resourcepack " + item, e);
return Stream.empty();
Expand Down
118 changes: 60 additions & 58 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,65 @@

@JsonSerializable
public record PackMcMeta(@SerializedName("pack") PackInfo pack) implements Validation {
private static List<LocalModFile.Description.Part> pairToPart(List<Pair<String, String>> lists, String color) {
List<LocalModFile.Description.Part> parts = new ArrayList<>();
for (Pair<String, String> list : lists) {
parts.add(new LocalModFile.Description.Part(list.getKey(), list.getValue().isEmpty() ? color : list.getValue()));
}
return parts;
}

private static void parseComponent(JsonElement element, List<LocalModFile.Description.Part> parts, String parentColor) throws JsonParseException {
if (parentColor == null) {
parentColor = "";
}
String color = parentColor;
if (element instanceof JsonPrimitive primitive) {
parts.addAll(pairToPart(StringUtils.parseMinecraftColorCodes(primitive.getAsString()), color));
} else if (element instanceof JsonObject jsonObj) {
if (jsonObj.get("color") instanceof JsonPrimitive primitive) {
color = primitive.getAsString();
}
if (jsonObj.get("text") instanceof JsonPrimitive primitive) {
parts.addAll(pairToPart(StringUtils.parseMinecraftColorCodes(primitive.getAsString()), color));
}
if (jsonObj.get("extra") instanceof JsonArray jsonArray) {
parseComponent(jsonArray, parts, color);
}
} else if (element instanceof JsonArray jsonArray) {
if (!jsonArray.isEmpty() && jsonArray.get(0) instanceof JsonObject jsonObj && jsonObj.get("color") instanceof JsonPrimitive primitive) {
color = primitive.getAsString();
}

for (JsonElement childElement : jsonArray) {
parseComponent(childElement, parts, color);
}
} else {
LOG.warning("Skipping unsupported element in description. Expected a string, object, or array, but got type " + element.getClass().getSimpleName() + ". Value: " + element);
}
}

public static LocalModFile.Description parseDescription(JsonElement json) throws JsonParseException {
List<LocalModFile.Description.Part> parts = new ArrayList<>();

if (json == null || json.isJsonNull()) {
return new LocalModFile.Description(parts);
}

try {
parseComponent(json, parts, "");
} catch (JsonParseException | IllegalStateException e) {
parts.clear();
LOG.warning("An unexpected error occurred while parsing a description component. The description may be incomplete.", e);
}

return new LocalModFile.Description(parts);
}

public static LocalModFile.Description parseDescription(String text) {
return parseDescription(new JsonPrimitive(text));
}

@Override
public void validate() throws JsonParseException {
if (pack == null)
Expand Down Expand Up @@ -110,62 +169,6 @@ public static PackVersion fromJson(JsonElement element) throws JsonParseExceptio
}

public static final class PackInfoDeserializer implements JsonDeserializer<PackInfo> {

private List<LocalModFile.Description.Part> pairToPart(List<Pair<String, String>> lists, String color) {
List<LocalModFile.Description.Part> parts = new ArrayList<>();
for (Pair<String, String> list : lists) {
parts.add(new LocalModFile.Description.Part(list.getKey(), list.getValue().isEmpty() ? color : list.getValue()));
}
return parts;
}

private void parseComponent(JsonElement element, List<LocalModFile.Description.Part> parts, String parentColor) throws JsonParseException {
if (parentColor == null) {
parentColor = "";
}
String color = parentColor;
if (element instanceof JsonPrimitive primitive) {
parts.addAll(pairToPart(StringUtils.parseMinecraftColorCodes(primitive.getAsString()), color));
} else if (element instanceof JsonObject jsonObj) {
if (jsonObj.get("color") instanceof JsonPrimitive primitive) {
color = primitive.getAsString();
}
if (jsonObj.get("text") instanceof JsonPrimitive primitive) {
parts.addAll(pairToPart(StringUtils.parseMinecraftColorCodes(primitive.getAsString()), color));
}
if (jsonObj.get("extra") instanceof JsonArray jsonArray) {
parseComponent(jsonArray, parts, color);
}
} else if (element instanceof JsonArray jsonArray) {
if (!jsonArray.isEmpty() && jsonArray.get(0) instanceof JsonObject jsonObj && jsonObj.get("color") instanceof JsonPrimitive primitive) {
color = primitive.getAsString();
}

for (JsonElement childElement : jsonArray) {
parseComponent(childElement, parts, color);
}
} else {
LOG.warning("Skipping unsupported element in description. Expected a string, object, or array, but got type " + element.getClass().getSimpleName() + ". Value: " + element);
}
}

private List<LocalModFile.Description.Part> parseDescription(JsonElement json) throws JsonParseException {
List<LocalModFile.Description.Part> parts = new ArrayList<>();

if (json == null || json.isJsonNull()) {
return parts;
}

try {
parseComponent(json, parts, "");
} catch (JsonParseException | IllegalStateException e) {
parts.clear();
LOG.warning("An unexpected error occurred while parsing a description component. The description may be incomplete.", e);
}

return parts;
}

@Override
public PackInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject packInfo = json.getAsJsonObject();
Expand All @@ -178,8 +181,7 @@ public PackInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationC
PackVersion minVersion = PackVersion.fromJson(packInfo.get("min_format"));
PackVersion maxVersion = PackVersion.fromJson(packInfo.get("max_format"));

List<LocalModFile.Description.Part> parts = parseDescription(packInfo.get("description"));
return new PackInfo(packFormat, minVersion, maxVersion, new LocalModFile.Description(parts));
return new PackInfo(packFormat, minVersion, maxVersion, PackMcMeta.parseDescription(packInfo.get("description")));
}
}

Expand Down
Loading