From 5cc6af2237352a02d36b64867fd5d374fd0159f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Tue, 11 Feb 2025 09:31:33 +0000 Subject: [PATCH] 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 | 25 ++++++++++++++++++++----- glib/tests/keyfile.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c index 637ac9c15..32c14433f 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, 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,6 +2171,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 (since GLib 2.84). + * * Since: 2.6 **/ void @@ -2187,7 +2190,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 +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. + * %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 @@ -2253,6 +2258,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 +2338,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); @@ -2359,6 +2370,8 @@ g_key_file_get_locale_for_key (GKeyFile *key_file, * 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 (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 * %G_KEY_FILE_KEEP_TRANSLATIONS in order to load strings for all locales. @@ -2436,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. + * it is created. + * + * If @locale is `C` then the untranslated value is set (since GLib 2.84). * * Since: 2.6 **/ @@ -2469,7 +2484,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 92f80100b..3a61c711b 100644 --- a/glib/tests/keyfile.c +++ b/glib/tests/keyfile.c @@ -811,6 +811,40 @@ 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"); + 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); + g_free (old_locale); } static void