diff --git a/.idea/modules/core/Ark-Pets.core.main.iml b/.idea/modules/core/Ark-Pets.core.main.iml
index a4522ccd..a9957d23 100644
--- a/.idea/modules/core/Ark-Pets.core.main.iml
+++ b/.idea/modules/core/Ark-Pets.core.main.iml
@@ -3,6 +3,7 @@
+
\ No newline at end of file
diff --git a/core/src/main/java/cn/harryh/arkpets/ArkConfig.java b/core/src/main/java/cn/harryh/arkpets/ArkConfig.java
index 9c4a2257..4dfd35a1 100644
--- a/core/src/main/java/cn/harryh/arkpets/ArkConfig.java
+++ b/core/src/main/java/cn/harryh/arkpets/ArkConfig.java
@@ -65,6 +65,7 @@ public class ArkConfig {
public float physic_static_friction_acc;
public float physic_speed_limit_x;
public float physic_speed_limit_y;
+ public String prefer_language;
private ArkConfig() {
}
diff --git a/core/src/main/java/cn/harryh/arkpets/i18n/I18n.java b/core/src/main/java/cn/harryh/arkpets/i18n/I18n.java
new file mode 100644
index 00000000..6e7fd263
--- /dev/null
+++ b/core/src/main/java/cn/harryh/arkpets/i18n/I18n.java
@@ -0,0 +1,51 @@
+package cn.harryh.arkpets.i18n;
+
+
+import cn.harryh.arkpets.ArkConfig;
+import cn.harryh.arkpets.utils.Logger;
+
+import java.util.*;
+
+
+public final class I18n {
+
+ private I18n() {
+ }
+
+ public static Locales.SupportedLocale getCurrentLocale() {
+ try {
+ return Locales.getLocaleByName(Objects.requireNonNull(ArkConfig.getConfig()).prefer_language);
+ } catch (IllegalStateException e) {
+ return Locales.DEFAULT;
+ }
+ }
+
+ public static ResourceBundle getResourceBundle() {
+ return getCurrentLocale().getResourceBundle();
+ }
+
+ public static String i18n(String key, Object... formatArgs) {
+ try {
+ return String.format(getResourceBundle().getString(key), formatArgs);
+ } catch (MissingResourceException e) {
+ Logger.error("I18n", "Cannot find key " + key + " in resource bundle" + e);
+ } catch (IllegalFormatException e) {
+ Logger.error("I18n", "Illegal format string, key=" + key + ", args=" + Arrays.toString(formatArgs) + e);
+ }
+
+ return key + Arrays.toString(formatArgs);
+ }
+
+ public static String i18n(String key) {
+ try {
+ return getResourceBundle().getString(key);
+ } catch (MissingResourceException e) {
+ Logger.error("I18n", "Cannot find key " + key + " in resource bundle", e);
+ return key;
+ }
+ }
+
+ public static boolean hasKey(String key) {
+ return getResourceBundle().containsKey(key);
+ }
+}
diff --git a/core/src/main/java/cn/harryh/arkpets/i18n/Locales.java b/core/src/main/java/cn/harryh/arkpets/i18n/Locales.java
new file mode 100644
index 00000000..ee5d24a3
--- /dev/null
+++ b/core/src/main/java/cn/harryh/arkpets/i18n/Locales.java
@@ -0,0 +1,77 @@
+package cn.harryh.arkpets.i18n;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+
+public final class Locales {
+ public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault(), "lang.default");
+ /**
+ * English
+ */
+ public static final SupportedLocale EN = new SupportedLocale(Locale.ROOT);
+ /**
+ * Traditional Chinese
+ */
+ public static final SupportedLocale ZH_TW = new SupportedLocale(Locale.TRADITIONAL_CHINESE);
+ /**
+ * Simplified Chinese
+ */
+ public static final SupportedLocale ZH_CN = new SupportedLocale(Locale.SIMPLIFIED_CHINESE);
+
+ private Locales() {
+ }
+
+ public static SupportedLocale getLocaleByName(String name) {
+ if (name == null) return DEFAULT;
+ return switch (name.toLowerCase(Locale.ROOT)) {
+ case "en" -> EN;
+ case "zh_tw" -> ZH_TW;
+ case "zh_cn" -> ZH_CN;
+ default -> DEFAULT;
+ };
+ }
+
+ public static List getSupportedLanguages() {
+ return List.of("en", "zh_tw", "zh_cn");
+ }
+
+ public static String getNameByLocale(SupportedLocale locale) {
+ if (locale == EN) return "en";
+ if (locale == ZH_TW) return "zh_tw";
+ if (locale == ZH_CN) return "zh_cn";
+ if (locale == DEFAULT) return "def";
+ throw new IllegalArgumentException("Unknown locale: " + locale);
+ }
+
+ public static class SupportedLocale {
+ private final Locale locale;
+ private final String name;
+ private final ResourceBundle resourceBundle;
+
+ SupportedLocale(Locale locale) {
+ this(locale, null);
+ }
+
+ SupportedLocale(Locale locale, String name) {
+ this.locale = locale;
+ this.name = name;
+ resourceBundle = ResourceBundle.getBundle("lang.I18N", locale);
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+
+ public ResourceBundle getResourceBundle() {
+ return resourceBundle;
+ }
+
+ public String getName(ResourceBundle newResourceBundle) {
+ if (name == null) return resourceBundle.getString("lang");
+ else return newResourceBundle.getString(name);
+ }
+
+ }
+}
diff --git a/core/src/main/resources/lang/I18N.properties b/core/src/main/resources/lang/I18N.properties
new file mode 100644
index 00000000..69de2aac
--- /dev/null
+++ b/core/src/main/resources/lang/I18N.properties
@@ -0,0 +1 @@
+app.title = Ark pet
diff --git a/core/src/main/resources/lang/I18N_zh_cn.properties b/core/src/main/resources/lang/I18N_zh_cn.properties
new file mode 100644
index 00000000..25f8d0cd
--- /dev/null
+++ b/core/src/main/resources/lang/I18N_zh_cn.properties
@@ -0,0 +1 @@
+app.title = Ark Pet
diff --git a/core/src/main/resources/lang/I18N_zh_tw.properties b/core/src/main/resources/lang/I18N_zh_tw.properties
new file mode 100644
index 00000000..69de2aac
--- /dev/null
+++ b/core/src/main/resources/lang/I18N_zh_tw.properties
@@ -0,0 +1 @@
+app.title = Ark pet
diff --git a/desktop/src/main/resources/ArkPetsConfigDefault.json b/desktop/src/main/resources/ArkPetsConfigDefault.json
index 14559ec8..f15fc7f4 100644
--- a/desktop/src/main/resources/ArkPetsConfigDefault.json
+++ b/desktop/src/main/resources/ArkPetsConfigDefault.json
@@ -20,5 +20,6 @@
"physic_gravity_acc":800.0,
"physic_speed_limit_x":1000.0,
"physic_speed_limit_y":1000.0,
- "physic_static_friction_acc":500.0
-}
\ No newline at end of file
+ "physic_static_friction_acc":500.0,
+ "prefer_language": "zh_cn"
+}