From a1463263b5efd4b7fee8066ef34ba294f9eb42b3 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall <82708541+grorp@users.noreply.github.com> Date: Mon, 5 Jun 2023 12:02:10 +0200 Subject: [PATCH] Auto-detect locale on Android (#13561) --- .../net/minetest/minetest/GameActivity.java | 27 +++++- src/gettext.cpp | 5 +- src/porting_android.cpp | 82 +++++++++---------- src/porting_android.h | 3 +- 4 files changed, 68 insertions(+), 49 deletions(-) diff --git a/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/android/app/src/main/java/net/minetest/minetest/GameActivity.java index 05c4014c3..d4fb57e44 100644 --- a/android/app/src/main/java/net/minetest/minetest/GameActivity.java +++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -39,6 +39,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.FileProvider; import java.io.File; +import java.util.Locale; import java.util.Objects; // Native code finds these methods by name (see porting_android.cpp). @@ -54,8 +55,6 @@ public class GameActivity extends NativeActivity { private int messageReturnCode = -1; private String messageReturnValue = ""; - public static native void putMessageBoxResult(String text); - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -203,4 +202,28 @@ public class GameActivity extends NativeActivity { Intent shareIntent = Intent.createChooser(intent, null); startActivity(shareIntent); } + + public String getLanguage() { + String langCode = Locale.getDefault().getLanguage(); + + // getLanguage() still uses old language codes to preserve compatibility. + // List of code changes in ISO 639-2: + // https://www.loc.gov/standards/iso639-2/php/code_changes.php + switch (langCode) { + case "in": + langCode = "id"; // Indonesian + break; + case "iw": + langCode = "he"; // Hebrew + break; + case "ji": + langCode = "yi"; // Yiddish + break; + case "jw": + langCode = "jv"; // Javanese + break; + } + + return langCode; + } } diff --git a/src/gettext.cpp b/src/gettext.cpp index f56738d98..553c9c4c7 100644 --- a/src/gettext.cpp +++ b/src/gettext.cpp @@ -203,7 +203,10 @@ void init_gettext(const char *path, const std::string &configured_language, #endif // ifndef _WIN32 } else { - /* set current system default locale */ +#ifdef __ANDROID__ + setenv("LANG", porting::getLanguageAndroid().c_str(), 1); +#endif + /* set current system default locale */ setlocale(LC_ALL, ""); } diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 3604684a1..1ed56015f 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -64,21 +64,6 @@ void android_main(android_app *app) exit(retval); } -/** - * Handler for finished message box input - * Intentionally NOT in namespace porting - * ToDo: this doesn't work as expected, there's a workaround for it right now - */ -extern "C" { - JNIEXPORT void JNICALL Java_net_minetest_minetest_GameActivity_putMessageBoxResult( - JNIEnv *env, jclass thiz, jstring text) - { - errorstream << - "Java_net_minetest_minetest_GameActivity_putMessageBoxResult got: " << - std::string((const char*) env->GetStringChars(text, nullptr)) << std::endl; - } -} - namespace porting { android_app *app_global; JNIEnv *jnienv; @@ -118,7 +103,7 @@ void initAndroid() nativeActivity = findClass("net/minetest/minetest/GameActivity"); if (nativeActivity == nullptr) errorstream << - "porting::initAndroid unable to find java native activity class" << + "porting::initAndroid unable to find Java native activity class" << std::endl; #ifdef GPROF @@ -141,15 +126,14 @@ void cleanupAndroid() jvm->DetachCurrentThread(); } -static std::string javaStringToUTF8(jstring js) +static std::string readJavaString(jstring j_str) { - std::string str; - // Get string as a UTF-8 c-string - const char *c_str = jnienv->GetStringUTFChars(js, nullptr); + // Get string as a UTF-8 C string + const char *c_str = jnienv->GetStringUTFChars(j_str, nullptr); // Save it - str = c_str; - // And free the c-string - jnienv->ReleaseStringUTFChars(js, c_str); + std::string str(c_str); + // And free the C string + jnienv->ReleaseStringUTFChars(j_str, c_str); return str; } @@ -162,11 +146,10 @@ void initializePathsAndroid() FATAL_ERROR_IF(getUserDataPath==nullptr, "porting::initializePathsAndroid unable to find Java getUserDataPath method"); jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getUserDataPath); - const char *javachars = jnienv->GetStringUTFChars((jstring) result, nullptr); - path_user = javachars; - path_share = javachars; - path_locale = path_share + DIR_DELIM + "locale"; - jnienv->ReleaseStringUTFChars((jstring) result, javachars); + std::string str = readJavaString((jstring) result); + path_user = str; + path_share = str; + path_locale = str + DIR_DELIM + "locale"; } // Set cache path @@ -176,9 +159,7 @@ void initializePathsAndroid() FATAL_ERROR_IF(getCachePath==nullptr, "porting::initializePathsAndroid unable to find Java getCachePath method"); jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getCachePath); - const char *javachars = jnienv->GetStringUTFChars((jstring) result, nullptr); - path_cache = javachars; - jnienv->ReleaseStringUTFChars((jstring) result, javachars); + path_cache = readJavaString((jstring) result); migrateCachePath(); } @@ -191,7 +172,7 @@ void showInputDialog(const std::string &acceptButton, const std::string &hint, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); FATAL_ERROR_IF(showdialog == nullptr, - "porting::showInputDialog unable to find java show dialog method"); + "porting::showInputDialog unable to find Java showDialog method"); jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str()); jstring jhint = jnienv->NewStringUTF(hint.c_str()); @@ -208,7 +189,7 @@ void openURIAndroid(const std::string &url) "(Ljava/lang/String;)V"); FATAL_ERROR_IF(url_open == nullptr, - "porting::openURIAndroid unable to find java openURI method"); + "porting::openURIAndroid unable to find Java openURI method"); jstring jurl = jnienv->NewStringUTF(url.c_str()); jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); @@ -220,7 +201,7 @@ void shareFileAndroid(const std::string &path) "(Ljava/lang/String;)V"); FATAL_ERROR_IF(url_open == nullptr, - "porting::shareFileAndroid unable to find java openURI method"); + "porting::shareFileAndroid unable to find Java shareFile method"); jstring jurl = jnienv->NewStringUTF(path.c_str()); jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); @@ -232,7 +213,7 @@ int getInputDialogState() "getDialogState", "()I"); FATAL_ERROR_IF(dialogstate == nullptr, - "porting::getInputDialogState unable to find java dialog state method"); + "porting::getInputDialogState unable to find Java getDialogState method"); return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate); } @@ -243,16 +224,11 @@ std::string getInputDialogValue() "getDialogValue", "()Ljava/lang/String;"); FATAL_ERROR_IF(dialogvalue == nullptr, - "porting::getInputDialogValue unable to find java dialog value method"); + "porting::getInputDialogValue unable to find Java getDialogValue method"); jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, dialogvalue); - - const char *javachars = jnienv->GetStringUTFChars((jstring) result, nullptr); - std::string text(javachars); - jnienv->ReleaseStringUTFChars((jstring) result, javachars); - - return text; + return readJavaString((jstring) result); } #ifndef SERVER @@ -266,11 +242,12 @@ float getDisplayDensity() "getDensity", "()F"); FATAL_ERROR_IF(getDensity == nullptr, - "porting::getDisplayDensity unable to find java getDensity method"); + "porting::getDisplayDensity unable to find Java getDensity method"); value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity); firstrun = false; } + return value; } @@ -284,7 +261,7 @@ v2u32 getDisplaySize() "getDisplayWidth", "()I"); FATAL_ERROR_IF(getDisplayWidth == nullptr, - "porting::getDisplayWidth unable to find java getDisplayWidth method"); + "porting::getDisplayWidth unable to find Java getDisplayWidth method"); retval.X = jnienv->CallIntMethod(app_global->activity->clazz, getDisplayWidth); @@ -293,14 +270,29 @@ v2u32 getDisplaySize() "getDisplayHeight", "()I"); FATAL_ERROR_IF(getDisplayHeight == nullptr, - "porting::getDisplayHeight unable to find java getDisplayHeight method"); + "porting::getDisplayHeight unable to find Java getDisplayHeight method"); retval.Y = jnienv->CallIntMethod(app_global->activity->clazz, getDisplayHeight); firstrun = false; } + return retval; } + +std::string getLanguageAndroid() +{ + jmethodID getLanguage = jnienv->GetMethodID(nativeActivity, + "getLanguage", "()Ljava/lang/String;"); + + FATAL_ERROR_IF(getLanguage == nullptr, + "porting::getLanguageAndroid unable to find Java getLanguage method"); + + jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, + getLanguage); + return readJavaString((jstring) result); +} + #endif // ndef SERVER } diff --git a/src/porting_android.h b/src/porting_android.h index 265825fbd..d8cfc18d4 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -43,7 +43,6 @@ void cleanupAndroid(); /** * Initializes path_* variables for Android - * @param env Android JNI environment */ void initializePathsAndroid(); @@ -83,4 +82,6 @@ std::string getInputDialogValue(); float getDisplayDensity(); v2u32 getDisplaySize(); #endif + +std::string getLanguageAndroid(); }