diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/SupportedLocale.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/SupportedLocale.java
index a1d2b4ca9..820b5c0c2 100644
--- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/SupportedLocale.java
+++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/SupportedLocale.java
@@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.jackhuang.hmcl.util.i18n;
import com.google.gson.annotations.JsonAdapter;
@@ -73,6 +72,8 @@ public final class SupportedLocale {
private final boolean isDefault;
private final String name;
private final Locale locale;
+ private final TextDirection textDirection;
+
private ResourceBundle resourceBundle;
private ResourceBundle localeNamesBundle;
private List candidateLocales;
@@ -86,12 +87,14 @@ public final class SupportedLocale {
this.locale = StringUtils.isBlank(language)
? LocaleUtils.SYSTEM_DEFAULT
: Locale.forLanguageTag(language);
+ this.textDirection = LocaleUtils.getTextDirection(locale);
}
SupportedLocale(Locale locale) {
this.isDefault = false;
this.name = locale.toLanguageTag();
this.locale = locale;
+ this.textDirection = LocaleUtils.getTextDirection(locale);
}
public boolean isDefault() {
@@ -106,6 +109,10 @@ public final class SupportedLocale {
return locale;
}
+ public TextDirection getTextDirection() {
+ return textDirection;
+ }
+
public String getDisplayName(SupportedLocale inLocale) {
if (isDefault()) {
try {
diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/LocaleUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/LocaleUtils.java
index f254c30fb..783c95163 100644
--- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/LocaleUtils.java
+++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/LocaleUtils.java
@@ -27,14 +27,7 @@ import org.jetbrains.annotations.Unmodifiable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
@@ -55,6 +48,7 @@ public final class LocaleUtils {
private static final Map subLanguageToParent = new HashMap<>();
private static final Map iso3To2 = new HashMap<>();
+ private static final Set rtl = new HashSet<>();
static {
try {
@@ -96,6 +90,17 @@ public final class LocaleUtils {
} catch (Throwable e) {
LOG.warning("Failed to load iso_languages.csv", e);
}
+
+ try {
+ for (String line : Lang.toIterable(IOUtils.readFullyAsString(LocaleUtils.class.getResourceAsStream("/assets/lang/rtl.txt")).lines())) {
+ if (line.startsWith("#") || line.isBlank()) {
+ continue;
+ }
+ rtl.add(line.trim());
+ }
+ } catch (Throwable e) {
+ LOG.warning("Failed to load rtl.txt", e);
+ }
}
private static Locale getInstance(String language, String script, String region,
@@ -163,6 +168,21 @@ public final class LocaleUtils {
return locale.getScript();
}
+ public static @NotNull TextDirection getTextDirection(Locale locale) {
+ TextDirection direction = rtl.contains(getRootLanguage(locale))
+ ? TextDirection.RIGHT_TO_LEFT
+ : TextDirection.LEFT_TO_RIGHT;
+
+ if ("Qabs".equals(getScript(locale))) {
+ direction = switch (direction) {
+ case RIGHT_TO_LEFT -> TextDirection.LEFT_TO_RIGHT;
+ case LEFT_TO_RIGHT -> TextDirection.RIGHT_TO_LEFT;
+ };
+ }
+
+ return direction;
+ }
+
private static final ConcurrentMap> CANDIDATE_LOCALES = new ConcurrentHashMap<>();
public static @NotNull List getCandidateLocales(Locale locale) {
diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/TextDirection.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/TextDirection.java
new file mode 100644
index 000000000..f9f00f190
--- /dev/null
+++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/TextDirection.java
@@ -0,0 +1,24 @@
+/*
+ * Hello Minecraft! Launcher
+ * Copyright (C) 2025 huangyuhui 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 .
+ */
+package org.jackhuang.hmcl.util.i18n;
+
+/// @author Glavo
+public enum TextDirection {
+ LEFT_TO_RIGHT,
+ RIGHT_TO_LEFT;
+}
diff --git a/HMCLCore/src/main/resources/assets/lang/rtl.txt b/HMCLCore/src/main/resources/assets/lang/rtl.txt
new file mode 100644
index 000000000..cbd40fc72
--- /dev/null
+++ b/HMCLCore/src/main/resources/assets/lang/rtl.txt
@@ -0,0 +1,6 @@
+ar
+fa
+he
+ps
+ur
+yi
\ No newline at end of file
diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/i18n/LocaleUtilsTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/i18n/LocaleUtilsTest.java
index 9074138e5..64bce19f4 100644
--- a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/i18n/LocaleUtilsTest.java
+++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/i18n/LocaleUtilsTest.java
@@ -220,4 +220,18 @@ public final class LocaleUtilsTest {
assertNull(LocaleUtils.getParentLanguage("zh"));
assertNull(LocaleUtils.getParentLanguage("zho"));
}
+
+ @Test
+ public void testGetTextDirection() {
+ assertEquals(TextDirection.LEFT_TO_RIGHT, LocaleUtils.getTextDirection(Locale.forLanguageTag("en")));
+ assertEquals(TextDirection.LEFT_TO_RIGHT, LocaleUtils.getTextDirection(Locale.forLanguageTag("en-US")));
+ assertEquals(TextDirection.LEFT_TO_RIGHT, LocaleUtils.getTextDirection(Locale.forLanguageTag("zh")));
+ assertEquals(TextDirection.LEFT_TO_RIGHT, LocaleUtils.getTextDirection(Locale.forLanguageTag("zh-Hans")));
+ assertEquals(TextDirection.LEFT_TO_RIGHT, LocaleUtils.getTextDirection(Locale.forLanguageTag("zh-CN")));
+ assertEquals(TextDirection.LEFT_TO_RIGHT, LocaleUtils.getTextDirection(Locale.forLanguageTag("ar-Qabs")));
+
+ assertEquals(TextDirection.RIGHT_TO_LEFT, LocaleUtils.getTextDirection(Locale.forLanguageTag("en-Qabs")));
+ assertEquals(TextDirection.RIGHT_TO_LEFT, LocaleUtils.getTextDirection(Locale.forLanguageTag("ar")));
+ assertEquals(TextDirection.RIGHT_TO_LEFT, LocaleUtils.getTextDirection(Locale.forLanguageTag("ara")));
+ }
}