From c9e195549fc0521d9a292ca3fe97da5e6053ec86 Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Sun, 6 Feb 2022 20:15:40 -0500 Subject: [PATCH] macOS system language detection. It appears the system language detection doesn't work on [some versions of?] macOS. Fixed. Strangely, querying the Cocoa API for the system locale on a system in US with the preferred language set to Korean produces the invalid locale "ko-US" instead of the expected "ko-KR". This behavior of returning the language with a invalid region qualifier for the language appears to happen only with languages where macOS does not have regional variants (this issue does not seem to occur with Canadian English, en-CA, for example.) A fuzzy matching locale function has been added to handle this issue. --- docs/CHANGES.txt | 3 +++ src/i18n.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++-- src/ios.h | 1 + src/ios.m | 6 +++++ src/macos.h | 1 + src/macos.m | 32 +++++++++++++++++++++++++-- 6 files changed, 96 insertions(+), 4 deletions(-) diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index a4670f817..f6c7a258f 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -113,6 +113,9 @@ http://www.tuxpaint.org/ * Tux Paint for macOS can now localize the macOS menubar. Mark Kim + * Tux Paint for macOS can now detect the system language. + Mark Kim + * Updated `src/po/check_translations.sh` sanity-checking script so it properly finds the updated docs & other changes. Bill Kendrick diff --git a/src/i18n.c b/src/i18n.c index 302dd12ca..7354efc5d 100644 --- a/src/i18n.c +++ b/src/i18n.c @@ -47,6 +47,12 @@ #include #endif +#if defined(__APPLE__) +#include "macos.h" +#elif defined(__IOS__) +#include "ios.h" +#endif + /* Globals: */ @@ -790,7 +796,7 @@ static void ctype_utf8(void) } /** - * For a given language, return its local, or exit with a usage error. + * For a given language, return its locale, or exit with a usage error. * * @param langstr Name of language (e.g., "german") * @return Locale (e.g., "de_DE.UTF-8") @@ -811,6 +817,47 @@ static const char *language_to_locale(const char *langstr) return NULL; } +/** + * For a given locale, return the known locale that matches it closest, or exit + * with a usage error. + * + * @param inlocale Name of some locale (e.g., "ko_US") + * @return Known locale. (e.g., "ko_KR.UTF-8") + */ +static const char *locale_to_closest_locale(const char *inlocale) +{ + const int numlocale = sizeof(language_to_locale_array) / sizeof(language_to_locale_array[0]); + const char* outlocale = NULL; + int outlocale_score = 0; + int i = 0; + int j = 0; + + /* find the locale with the longest string match */ + for (i=0; i outlocale_score) + { + outlocale = candidate; + outlocale_score = j; + } + } + + /* locale must match at least three characters */ + if (outlocale_score < 3) + { + outlocale = NULL; + } + + return outlocale; +} + /** * Set language ("langint" global) based on a given locale; * will try a few ways of checking, is case-insensitive, etc. @@ -1225,7 +1272,13 @@ int setup_i18n(const char *restrict lang, const char *restrict locale, int * num } } else - locale = ""; + { + #if defined(__APPLE__) + locale = locale_to_closest_locale(apple_locale()); + #else + locale = ""; + #endif + } if (lang) locale = language_to_locale(lang); diff --git a/src/ios.h b/src/ios.h index e4e965d09..50767c5b7 100644 --- a/src/ios.h +++ b/src/ios.h @@ -24,6 +24,7 @@ void apple_init(void); +const char *apple_locale(void); const char *apple_fontsPath(void); const char *apple_preferencesPath(void); const char *apple_globalPreferencesPath(void); diff --git a/src/ios.m b/src/ios.m index 596e1f002..4754f3c17 100644 --- a/src/ios.m +++ b/src/ios.m @@ -49,6 +49,12 @@ void apple_init(void) } +const char *apple_locale(void) +{ + return "" +} + + const char *apple_fontsPath(void) { return IOS_FONTS_PATH; diff --git a/src/macos.h b/src/macos.h index c9f9dd5df..bbd8615fc 100644 --- a/src/macos.h +++ b/src/macos.h @@ -27,6 +27,7 @@ void apple_init(void); +const char *apple_locale(void); const char *apple_fontsPath(void); const char *apple_preferencesPath(void); const char *apple_globalPreferencesPath(void); diff --git a/src/macos.m b/src/macos.m index 61041a514..235df821c 100644 --- a/src/macos.m +++ b/src/macos.m @@ -34,6 +34,9 @@ #define MACOS_PICTURES_PATH "%s/Pictures" +static char *APPLE_LOCALE = NULL; + + static void setupApplicationMenu(void) { /* @@ -130,10 +133,35 @@ static void removeSdlMenu(void) void apple_init(void) { /* Override SDL's default menu with our gettext-translatable menu. We do - * this by adding our menus, then removing the menus installed by SDL. */ + * this by removing the menus added by SDL, then adding ours. */ + removeSdlMenu(); setupApplicationMenu(); setupWindowMenu(); - removeSdlMenu(); +} + + +const char *apple_locale(void) +{ + if(!APPLE_LOCALE) { + const char *locale = [[[NSLocale preferredLanguages] firstObject] UTF8String]; + + /* Copy to writable memory */ + APPLE_LOCALE = strdup(locale); + + if(!APPLE_LOCALE) { + perror("apple_locale"); + return "C"; /* Default to C */ + } + + /* Change the locale hyphen separator to underscore (e.g., en-US to en_US) */ + if(APPLE_LOCALE[2] == '-') { + APPLE_LOCALE[2] = '_'; + } + } + + DEBUG_PRINTF("locale=%s\n", APPLE_LOCALE); + + return APPLE_LOCALE; }