From 25e35e17fe57f9e4609152ea80eeb6b93bd60d7d Mon Sep 17 00:00:00 2001 From: Jehan Date: Thu, 7 Jul 2022 14:47:28 +0200 Subject: [PATCH] app: detect system language with a more robust logic. langinfo.h is not on all systems apparently and/or the locale item we test for may not be available everywhere. Actually even on Linux, after testing more deeply, I could create cases where nl_langinfo() would not return a result (if the locale is broken through environment variable for instance). setlocale() seems to always return usable value so far, so I fallback on it. As a last resort, I look at environment variables (even though these may contain invalid contents. As for Windows and macOS, I try to use more platform-specific methods. In macOS in particular, as I understood from reports, GIMP follows correctly the language preference order, which means we should not look at a single (top) lang, but at the whole list of prefered languages as a single settings to determine whether the language was changed or not. Should fix on Windows: > fatal error: langinfo.h: No such file or directory and on macOS: > error: use of undeclared identifier '_NL_IDENTIFICATION_LANGUAGE' --- app/language.c | 71 +++++++++++++++++++++++++++++++++++++++++++++----- configure.ac | 14 ++++++++++ meson.build | 9 +++++++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/app/language.c b/app/language.c index f50bba737a..c1623f562d 100644 --- a/app/language.c +++ b/app/language.c @@ -21,7 +21,9 @@ #include "config.h" +#ifdef HAVE__NL_IDENTIFICATION_LANGUAGE #include +#endif #include #include @@ -30,10 +32,16 @@ #include #include #endif +#ifdef PLATFORM_OSX +#include +#endif #include "language.h" +static gchar * language_get_system_lang_id (void); + + const gchar * language_init (const gchar *language) { @@ -740,13 +748,7 @@ language_init (const gchar *language) */ if (! language || strlen (language) == 0) { - /* Using system language. It doesn't matter too much that the string - * format is different when using system or preference-set language, - * because this string is only used for comparison. As long as 2 - * similar run have the same settings, the strings will be - * identical. - */ - actual_language = g_strdup (nl_langinfo (_NL_IDENTIFICATION_LANGUAGE)); + actual_language = language_get_system_lang_id (); } else { @@ -758,3 +760,58 @@ language_init (const gchar *language) return actual_language; } + +static gchar * +language_get_system_lang_id (void) +{ + const gchar *syslang = NULL; + + /* Using system language. It doesn't matter too much that the string + * format is different when using system or preference-set language, + * because this string is only used for comparison. As long as 2 + * similar run have the same settings, the strings will be + * identical. + */ +#if defined G_OS_WIN32 + return g_strdup_printf ("LANGID-%d", GetUserDefaultUILanguage()); +#elif defined PLATFORM_OSX + NSString *langs; + + /* In macOS, the user sets a list of prefered languages and the + * software respects this preference order. I.e. that just storing the + * top-prefered lang would not be enough. What if GIMP didn't have + * translations for it, then it would fallback to the second lang. If + * this second lang changed, GIMP localization would change but we + * would not be aware of it. Instead, let's use the whole list as our + * language identifier. If this list changes in any way, we consider + * the lang may have potentially changed. + */ + langs = [[NSLocale preferredLanguages] componentsJoinedByString:@","]; + + return g_strdup_printf ("%s", [langs UTF8String]); +#elif defined HAVE__NL_IDENTIFICATION_LANGUAGE + syslang = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE); +#endif + + if (syslang == NULL || strlen (syslang) == 0) + /* This should return an opaque string which represents the whole + * locale configuration on this system. + */ + syslang = setlocale (LC_ALL, NULL); + + /* I don't think we'd ever get here but just in case, as a last + * resort, if none of the previous methods returned a valid result, + * let's just check environment variables ourselves. + * This is the proper order of priority. + */ + if (syslang == NULL || strlen (syslang) == 0) + syslang = g_getenv ("LANGUAGE"); + if (syslang == NULL || strlen (syslang) == 0) + syslang = g_getenv ("LC_ALL"); + if (syslang == NULL || strlen (syslang) == 0) + syslang = g_getenv ("LC_MESSAGES"); + if (syslang == NULL || strlen (syslang) == 0) + syslang = g_getenv ("LANG"); + + return syslang && strlen (syslang) > 0 ? g_strdup (syslang) : NULL; +} diff --git a/configure.ac b/configure.ac index ec85f0cdda..5ba0447c52 100644 --- a/configure.ac +++ b/configure.ac @@ -639,6 +639,20 @@ if test "$nl_ok" = "yes"; then [Define to 1 if _NL_MEASUREMENT_MEASUREMENT is available]) fi +# _NL_IDENTIFICATION_LANGUAGE is an enum and not a define +AC_MSG_CHECKING([for _NL_IDENTIFICATION_LANGUAGE]) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[char c = *((unsigned char *) nl_langinfo(_NL_IDENTIFICATION_LANGUAGE));]])], + [nl_ok=yes], + [nl_ok=no]) +AC_MSG_RESULT($nl_ok) +if test "$nl_ok" = "yes"; then + AC_DEFINE(HAVE__NL_IDENTIFICATION_LANGUAGE, 1, + [Define to 1 if _NL_IDENTIFICATION_LANGUAGE is available]) +fi + # Macro to keep track of failed dependencies. required_deps='' diff --git a/meson.build b/meson.build index 19ff90c9d2..f29e9eacd8 100644 --- a/meson.build +++ b/meson.build @@ -286,6 +286,15 @@ conf.set('HAVE__NL_MEASUREMENT_MEASUREMENT', ''') ) +conf.set('HAVE__NL_IDENTIFICATION_LANGUAGE', + cc.compiles(''' + #include + int main() { + char c = *((unsigned char *) nl_langinfo(_NL_IDENTIFICATION_LANGUAGE)); + } + ''') +) + ################################################################################ # Dependencies