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);