diff --git a/app/flatpak-builtins-config.c b/app/flatpak-builtins-config.c index 6fb40952..8b4855fd 100644 --- a/app/flatpak-builtins-config.c +++ b/app/flatpak-builtins-config.c @@ -51,7 +51,11 @@ looks_like_a_language (const char *s) int len = strlen (s); int i; - if (len < 2 || len > 3) + if (g_str_equal (s, "C") || + g_str_equal (s, "POSIX")) + return TRUE; + + if (len < 2) return FALSE; for (i = 0; i < len; i++) @@ -64,49 +68,73 @@ looks_like_a_language (const char *s) } static gboolean -looks_like_a_region (const char *s) +looks_like_a_territory (const char *s) { - g_auto(GStrv) locale = g_strsplit (s, "_", 3); - int i; - int len; + gsize len = strlen (s); + gsize i; - if (!looks_like_a_language(locale[0])) + if (len < 2) return FALSE; - if (locale[1] != NULL) + for (i = 0; i < len; i++) { - len = strlen (locale[1]); - - // This can be either GB or Latn - if (len < 2 || len > 4) + if (!g_ascii_isalpha (s[i]) || !g_ascii_isupper (s[i])) return FALSE; - - for (i = 0; i < len; i++) - { - if ((locale[2] == NULL) && (!g_ascii_isalpha (locale[1][i]) || !g_ascii_isupper (locale[1][i]))) - return FALSE; - else if (!g_ascii_isalpha (locale[1][i])) - return FALSE; - } } - if (g_strv_length (locale) > 2) + return TRUE; +} + +static gboolean +looks_like_a_codeset_or_modifier (const char *s) +{ + gsize len = strlen (s); + gsize i; + + if (len < 1) + return FALSE; + + for (i = 0; i < len; i++) { - len = strlen (locale[2]); - - if (len < 2 || len > 3) + if (!g_ascii_isalnum (s[i]) && s[i] != '-') return FALSE; - - for (i = 0; i < len; i++) - { - if (!g_ascii_isalpha (locale[2][i]) || !g_ascii_isupper (locale[2][i])) - return FALSE; - } } return TRUE; } +static gboolean +looks_like_a_locale (const char *s) +{ + g_autofree gchar *locale = g_strdup (s); + gchar *language, *territory, *codeset, *modifier; + + modifier = strchr (locale, '@'); + if (modifier != NULL) + *modifier++ = '\0'; + + codeset = strchr (locale, '.'); + if (codeset != NULL) + *codeset++ = '\0'; + + territory = strchr (locale, '_'); + if (territory != NULL) + *territory++ = '\0'; + + language = locale; + + if (!looks_like_a_language (language)) + return FALSE; + if (territory != NULL && !looks_like_a_territory (territory)) + return FALSE; + if (codeset != NULL && !looks_like_a_codeset_or_modifier (codeset)) + return FALSE; + if (modifier != NULL && !looks_like_a_codeset_or_modifier (modifier)) + return FALSE; + + return TRUE; +} + static char * parse_locale (const char *value, GError **error) { @@ -116,7 +144,7 @@ parse_locale (const char *value, GError **error) strs = g_strsplit (value, ";", 0); for (i = 0; strs[i]; i++) { - if (!looks_like_a_language (strs[i]) && !looks_like_a_region (strs[i])) + if (!looks_like_a_language (strs[i]) && !looks_like_a_locale (strs[i])) { flatpak_fail (error, _("'%s' does not look like a language/locale code"), strs[i]); return NULL; diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index 6a68da43..65a07922 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -14607,9 +14607,10 @@ flatpak_dir_get_default_locale_languages (FlatpakDir *self) extra_languages = flatpak_dir_get_config_strv (self, "xa.extra-languages"); for (i = 0; extra_languages != NULL && extra_languages[i] != NULL; i++) { - g_auto(GStrv) locales = g_strsplit (extra_languages[i], "_", -1); - g_free (extra_languages[i]); - extra_languages[i] = g_strdup (locales[0]); + /* Strip the locale, modifier or codeset, if present. */ + gchar *match = strpbrk (extra_languages[i], "._@"); + if (match != NULL) + *match = '\0'; } if (flatpak_dir_is_user (self)) diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c index ad730671..d6935b1d 100644 --- a/common/flatpak-installation.c +++ b/common/flatpak-installation.c @@ -1687,12 +1687,15 @@ flatpak_installation_get_default_languages (FlatpakInstallation *self, * @self: a #FlatpakInstallation * @error: return location for a #GError * - * Like flatpak_installation_get_default_languages() but includes region - * information (e.g. en_US rather than en) which may be included in the - * xa.extra-languages configuration. + * Like flatpak_installation_get_default_languages() but includes territory + * information (e.g. `en_US` rather than `en`) which may be included in the + * `xa.extra-languages` configuration. + * + * Strings returned by this function are in the format specified by + * [`setlocale()`](man:setlocale): `language[_territory][.codeset][@modifier]`. * * Returns: (array zero-terminated=1) (element-type utf8) (transfer full): - * A possibly empty array of language and locale strings, or %NULL on error. + * A possibly empty array of locale strings, or %NULL on error. * Since: 1.5.1 */ char ** diff --git a/doc/flatpak-config.xml b/doc/flatpak-config.xml index 32d5420e..83d57e50 100644 --- a/doc/flatpak-config.xml +++ b/doc/flatpak-config.xml @@ -61,8 +61,8 @@ The languages that are included when installing Locale extensions. The value is a semicolon-separated list of two-letter language codes, - or one of the special values * or all. If this key is unset, flatpak - defaults to including the extra-languages key and the current locale. + or one of the special values * or all. If this key is unset, flatpak + defaults to including the extra-languages key and the current locale. @@ -70,8 +70,10 @@ This key is used when languages is not set, and it defines extra locale extensions on top of the system configured languages. The value is a - semicolon-separated list of two-letter language codes or locale identifiers - (eg. en;en_DK;az_Latn_AZ). + semicolon-separated list of locale identifiers + (language, optional locale, optional codeset, optional modifier) as documented by + setlocale3 + (for example, en;en_DK;zh_HK.big5hkscs;uz_UZ.utf8@cyrillic). diff --git a/tests/testlibrary.c b/tests/testlibrary.c index 052507f7..ee2529f0 100644 --- a/tests/testlibrary.c +++ b/tests/testlibrary.c @@ -291,27 +291,30 @@ test_languages_config (void) g_assert_cmpstr (value[0], ==, "en"); g_assert_null (value[1]); - res = flatpak_installation_set_config_sync (inst, "extra-languages", "pt_BR;az_Latn_AZ;es", NULL, &error); + res = flatpak_installation_set_config_sync (inst, "extra-languages", "pt_BR;uz_UZ.utf8@cyrillic;es;zh_HK.big5hkscs;uz_UZ@cyrillic", NULL, &error); g_assert_no_error (error); g_assert_true (res); value = flatpak_installation_get_default_languages (inst, &error); g_assert_no_error (error); - g_assert_cmpstr (value[0], ==, "az"); - g_assert_cmpstr (value[1], ==, "en"); - g_assert_cmpstr (value[2], ==, "es"); - g_assert_cmpstr (value[3], ==, "pt"); - g_assert_null (value[4]); + g_assert_cmpstr (value[0], ==, "en"); + g_assert_cmpstr (value[1], ==, "es"); + g_assert_cmpstr (value[2], ==, "pt"); + g_assert_cmpstr (value[3], ==, "uz"); + g_assert_cmpstr (value[4], ==, "zh"); + g_assert_null (value[5]); g_clear_pointer (&value, g_strfreev); value = flatpak_installation_get_default_locales (inst, &error); g_assert_no_error (error); - g_assert_cmpstr (value[0], ==, "az_Latn_AZ"); - g_assert_cmpstr (value[1], ==, "en"); - g_assert_cmpstr (value[2], ==, "es"); - g_assert_cmpstr (value[3], ==, "pt_BR"); - g_assert_null (value[4]); + g_assert_cmpstr (value[0], ==, "en"); + g_assert_cmpstr (value[1], ==, "es"); + g_assert_cmpstr (value[2], ==, "pt_BR"); + g_assert_cmpstr (value[3], ==, "uz_UZ.utf8@cyrillic"); + g_assert_cmpstr (value[4], ==, "uz_UZ@cyrillic"); + g_assert_cmpstr (value[5], ==, "zh_HK.big5hkscs"); + g_assert_null (value[6]); g_clear_pointer (&value, g_strfreev);