From 3c0f06d730efbe885337bbacfc363d363eb8503d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Mon, 10 Feb 2025 08:01:07 +0100 Subject: [PATCH 1/3] gkeyfile: Fix C locale handling As gettext does: * if C locale is used to set a value, set the untranslated value, i.e. key=value and not key[C]=value; * if C locale is used to get a value, return the untranslated value, i.e. not the value from key[C]=value, if it ever exists, and do not consider C as an undefined locale otherwise. Fixes: #3578 --- glib/gkeyfile.c | 22 ++++++++++++++++------ glib/tests/keyfile.c | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c index 637ac9c150..b301b987c6 100644 --- a/glib/gkeyfile.c +++ b/glib/gkeyfile.c @@ -119,7 +119,8 @@ * Key-value pairs generally have the form `key=value`, with the exception * of localized strings, which have the form `key[locale]=value`, with a * locale identifier of the form `lang_COUNTRY@MODIFIER` where `COUNTRY` - * and `MODIFIER` are optional. Space before and after the '=' character + * and `MODIFIER` are optional. As a special case, locale `C` is associated with + * the untranslated pair `key=value`. Space before and after the '=' character * are ignored. Newline, tab, carriage return and backslash characters in * value are escaped as `\n`, `\t`, `\r`, and `\\\\`, respectively. To preserve * leading spaces in values, these can also be escaped as `\s`. @@ -2169,6 +2170,7 @@ g_key_file_set_string_list (GKeyFile *key_file, * * Associates a string value for @key and @locale under @group_name. * If the translation for @key cannot be found then it is created. + * If locale is `C` then the untranslated value is set. * * Since: 2.6 **/ @@ -2187,7 +2189,7 @@ g_key_file_set_locale_string (GKeyFile *key_file, g_return_if_fail (string != NULL); value = g_key_file_parse_string_as_value (key_file, string, FALSE); - full_key = g_strdup_printf ("%s[%s]", key, locale); + full_key = g_strcmp0 (locale, "C") != 0 ? g_strdup_printf ("%s[%s]", key, locale) : g_strdup (key); g_key_file_set_value (key_file, group_name, full_key, value); g_free (full_key); g_free (value); @@ -2203,7 +2205,8 @@ g_key_file_set_locale_string (GKeyFile *key_file, * * Returns the value associated with @key under @group_name * translated in the given @locale if available. If @locale is - * %NULL then the current locale is assumed. + * %NULL then the current locale is assumed. If locale is `C` then the + * untranslated value is returned. * * If @locale is to be non-%NULL, or if the current locale will change over * the lifetime of the #GKeyFile, it must be loaded with @@ -2253,6 +2256,9 @@ g_key_file_get_locale_string (GKeyFile *key_file, for (i = 0; languages[i]; i++) { + if (g_strcmp0 (languages[i], "C") == 0) + break; + candidate_key = g_strdup_printf ("%s[%s]", key, languages[i]); translated_value = g_key_file_get_string (key_file, @@ -2330,6 +2336,9 @@ g_key_file_get_locale_for_key (GKeyFile *key_file, { gchar *candidate_key, *translated_value; + if (g_strcmp0 (languages[i], "C") == 0) + break; + candidate_key = g_strdup_printf ("%s[%s]", key, languages[i]); translated_value = g_key_file_get_string (key_file, group_name, candidate_key, NULL); g_free (translated_value); @@ -2357,7 +2366,8 @@ g_key_file_get_locale_for_key (GKeyFile *key_file, * * Returns the values associated with @key under @group_name * translated in the given @locale if available. If @locale is - * %NULL then the current locale is assumed. + * %NULL then the current locale is assumed. If locale is `C` then the + * untranslated value is returned. * * If @locale is to be non-%NULL, or if the current locale will change over * the lifetime of the #GKeyFile, it must be loaded with @@ -2436,7 +2446,7 @@ g_key_file_get_locale_string_list (GKeyFile *key_file, * * Associates a list of string values for @key and @locale under * @group_name. If the translation for @key cannot be found then - * it is created. + * it is created. If locale is `C` then the untranslated value is set. * * Since: 2.6 **/ @@ -2469,7 +2479,7 @@ g_key_file_set_locale_string_list (GKeyFile *key_file, g_free (value); } - full_key = g_strdup_printf ("%s[%s]", key, locale); + full_key = g_strcmp0 (locale, "C") != 0 ? g_strdup_printf ("%s[%s]", key, locale) : g_strdup (key); g_key_file_set_value (key_file, group_name, full_key, value_list->str); g_free (full_key); g_string_free (value_list, TRUE); diff --git a/glib/tests/keyfile.c b/glib/tests/keyfile.c index 92f80100ba..b95d598e2d 100644 --- a/glib/tests/keyfile.c +++ b/glib/tests/keyfile.c @@ -811,6 +811,31 @@ test_locale_string (void) setlocale (LC_ALL, old_locale); g_free (old_locale); + + /* test that C locale is handled properly (as gettext does) */ + + old_locale = g_strdup (setlocale (LC_ALL, NULL)); + keyfile = load_data (data, G_KEY_FILE_KEEP_TRANSLATIONS); + + g_key_file_set_locale_string (keyfile, "valid", "key1", "C", "v1_C"); + check_locale_string_value (keyfile, "valid", "key1", "C", "v1_C"); + check_string_value (keyfile, "valid", "key1", "v1_C"); + + g_setenv ("LANGUAGE", "C:it:de", TRUE); + setlocale (LC_ALL, ""); + check_locale_string_value (keyfile, "valid", "key1", NULL, "v1_C"); + + g_setenv ("LANGUAGE", "it:C:de", TRUE); + setlocale (LC_ALL, ""); + check_locale_string_value (keyfile, "valid", "key1", NULL, "v1_C"); + + g_setenv ("LANGUAGE", "it:de:C", TRUE); + setlocale (LC_ALL, ""); + check_locale_string_value (keyfile, "valid", "key1", NULL, "v1-de"); + + g_key_file_free (keyfile); + setlocale (LC_ALL, old_locale); + g_free (old_locale); } static void -- GitLab From 08dbbc1b63b68b7a498b0c29d232299a2f9fa290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Mon, 10 Feb 2025 16:25:53 +0000 Subject: [PATCH 2/3] Apply 6 suggestion(s) to 1 file(s) Co-authored-by: Philip Withnall --- glib/gkeyfile.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c index b301b987c6..32c14433fb 100644 --- a/glib/gkeyfile.c +++ b/glib/gkeyfile.c @@ -119,8 +119,8 @@ * Key-value pairs generally have the form `key=value`, with the exception * of localized strings, which have the form `key[locale]=value`, with a * locale identifier of the form `lang_COUNTRY@MODIFIER` where `COUNTRY` - * and `MODIFIER` are optional. As a special case, locale `C` is associated with - * the untranslated pair `key=value`. Space before and after the '=' character + * and `MODIFIER` are optional. As a special case, the locale `C` is associated with + * the untranslated pair `key=value` (since GLib 2.84). Space before and after the '=' character * are ignored. Newline, tab, carriage return and backslash characters in * value are escaped as `\n`, `\t`, `\r`, and `\\\\`, respectively. To preserve * leading spaces in values, these can also be escaped as `\s`. @@ -2170,7 +2170,8 @@ g_key_file_set_string_list (GKeyFile *key_file, * * Associates a string value for @key and @locale under @group_name. * If the translation for @key cannot be found then it is created. - * If locale is `C` then the untranslated value is set. + * + * If @locale is `C` then the untranslated value is set (since GLib 2.84). * * Since: 2.6 **/ @@ -2205,8 +2206,9 @@ g_key_file_set_locale_string (GKeyFile *key_file, * * Returns the value associated with @key under @group_name * translated in the given @locale if available. If @locale is - * %NULL then the current locale is assumed. If locale is `C` then the - * untranslated value is returned. + * %NULL then the current locale is assumed. + * + * If @locale is `C` then the untranslated value is returned (since GLib 2.84). * * If @locale is to be non-%NULL, or if the current locale will change over * the lifetime of the #GKeyFile, it must be loaded with @@ -2366,8 +2368,9 @@ g_key_file_get_locale_for_key (GKeyFile *key_file, * * Returns the values associated with @key under @group_name * translated in the given @locale if available. If @locale is - * %NULL then the current locale is assumed. If locale is `C` then the - * untranslated value is returned. + * %NULL then the current locale is assumed. + * + * If @locale is `C` then the untranslated value is returned (since GLib 2.84). * * If @locale is to be non-%NULL, or if the current locale will change over * the lifetime of the #GKeyFile, it must be loaded with @@ -2446,7 +2449,9 @@ g_key_file_get_locale_string_list (GKeyFile *key_file, * * Associates a list of string values for @key and @locale under * @group_name. If the translation for @key cannot be found then - * it is created. If locale is `C` then the untranslated value is set. + * it is created. + * + * If @locale is `C` then the untranslated value is set (since GLib 2.84). * * Since: 2.6 **/ -- GitLab From c482924c5827aeb877fe56f1bd8d33cf742af09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Mon, 10 Feb 2025 17:48:16 +0100 Subject: [PATCH 3/3] Add missing tests and FIXME --- glib/tests/keyfile.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/glib/tests/keyfile.c b/glib/tests/keyfile.c index b95d598e2d..3a61c711ba 100644 --- a/glib/tests/keyfile.c +++ b/glib/tests/keyfile.c @@ -820,18 +820,27 @@ test_locale_string (void) g_key_file_set_locale_string (keyfile, "valid", "key1", "C", "v1_C"); check_locale_string_value (keyfile, "valid", "key1", "C", "v1_C"); check_string_value (keyfile, "valid", "key1", "v1_C"); + check_string_locale_value (keyfile, "valid", "key1", "C", "C"); + /* + * FIXME: + * - Tests using setenv() needs to be converted to spawn subprocesses. + * - Tests using setlocale() should use uselocale() instead. + */ g_setenv ("LANGUAGE", "C:it:de", TRUE); setlocale (LC_ALL, ""); check_locale_string_value (keyfile, "valid", "key1", NULL, "v1_C"); + check_string_locale_value (keyfile, "valid", "key1", NULL, "C"); g_setenv ("LANGUAGE", "it:C:de", TRUE); setlocale (LC_ALL, ""); check_locale_string_value (keyfile, "valid", "key1", NULL, "v1_C"); + check_string_locale_value (keyfile, "valid", "key1", NULL, "C"); g_setenv ("LANGUAGE", "it:de:C", TRUE); setlocale (LC_ALL, ""); check_locale_string_value (keyfile, "valid", "key1", NULL, "v1-de"); + check_string_locale_value (keyfile, "valid", "key1", NULL, "de"); g_key_file_free (keyfile); setlocale (LC_ALL, old_locale); -- GitLab