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'
This commit is contained in:
Jehan 2022-07-07 14:47:28 +02:00
parent ccf6ecf43f
commit 25e35e17fe
3 changed files with 87 additions and 7 deletions

View file

@ -21,7 +21,9 @@
#include "config.h"
#ifdef HAVE__NL_IDENTIFICATION_LANGUAGE
#include <langinfo.h>
#endif
#include <locale.h>
#include <glib.h>
@ -30,10 +32,16 @@
#include <windows.h>
#include <winnls.h>
#endif
#ifdef PLATFORM_OSX
#include <Foundation/NSLocale.h>
#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;
}

View file

@ -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 <langinfo.h>]],
[[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=''

View file

@ -286,6 +286,15 @@ conf.set('HAVE__NL_MEASUREMENT_MEASUREMENT',
''')
)
conf.set('HAVE__NL_IDENTIFICATION_LANGUAGE',
cc.compiles('''
#include<langinfo.h>
int main() {
char c = *((unsigned char *) nl_langinfo(_NL_IDENTIFICATION_LANGUAGE));
}
''')
)
################################################################################
# Dependencies