diff --git a/compose/asc-utils-l10n.c b/compose/asc-utils-l10n.c
index 2b9ccc4a..857e1405 100644
--- a/compose/asc-utils-l10n.c
+++ b/compose/asc-utils-l10n.c
@@ -488,6 +488,19 @@ asc_read_translation_status (AscResult *cres,
if (!have_results)
asc_result_add_hint_simple (cres, cpt, "translations-not-found");
+ /* Add a fake entry for the source locale. Do so after checking
+ * !have_results since the source locale is always guaranteed
+ * to exist, so would break that check.
+ * as_component_add_language() will deduplicate in case that’s
+ * needed. */
+ for (guint i = 0; i < ctx->translations->len; i++) {
+ AsTranslation *t = g_ptr_array_index (ctx->translations, i);
+
+ as_component_add_language (cpt,
+ as_translation_get_source_locale (t),
+ 100);
+ }
+
/* remove translation elements, they should no longer be in the resulting component */
g_ptr_array_set_size (ctx->translations, 0);
}
diff --git a/docs/xml/metainfo-component.xml b/docs/xml/metainfo-component.xml
index 6a1ef992..cd5989fe 100644
--- a/docs/xml/metainfo-component.xml
+++ b/docs/xml/metainfo-component.xml
@@ -1259,6 +1259,11 @@
In case a software components gets its translation from multiple translation domains, the <translation/> tag may be defined more
than once.
+
+ The source strings in the component are assumed to be in the en_US locale. If that is not the case, specify the source locale using
+ the source_locale attribute on the <translation/> tag. The metadata generator will use the source locale
+ to synthesize a <lang/> tag for the source locale, with 100% translation.
+
For Gettext translations, localization data will be looked for in ${prefix}/share/locale/${locale}/LC_MESSAGES/${id}.mo, where
${id} is replaced with the translation domain specified in the <translation/> tag.
@@ -1270,6 +1275,7 @@
Example:
foobar
+foobar
FooBar/translations/foobar]]>
diff --git a/src/as-translation.c b/src/as-translation.c
index 16fd97fa..3ed45656 100644
--- a/src/as-translation.c
+++ b/src/as-translation.c
@@ -42,6 +42,7 @@ typedef struct
{
AsTranslationKind kind;
GRefString *id;
+ GRefString *source_locale;
} AsTranslationPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (AsTranslation, as_translation, G_TYPE_OBJECT)
@@ -103,6 +104,7 @@ as_translation_finalize (GObject *object)
AsTranslationPrivate *priv = GET_PRIVATE (tr);
as_ref_string_release (priv->id);
+ as_ref_string_release (priv->source_locale);
G_OBJECT_CLASS (as_translation_parent_class)->finalize (object);
}
@@ -173,6 +175,42 @@ as_translation_set_id (AsTranslation *tr, const gchar *id)
as_ref_string_assign_safe (&priv->id, id);
}
+/**
+ * as_translation_get_source_locale:
+ * @tr: a #AsTranslation instance.
+ *
+ * The locale of the source strings for this component. If this has not been
+ * explicitly specified, `en_US` will be returned.
+ *
+ * Returns: (not nullable): The locale of the source strings for this component.
+ * Since: 0.14.6
+ */
+const gchar*
+as_translation_get_source_locale (AsTranslation *tr)
+{
+ AsTranslationPrivate *priv = GET_PRIVATE (tr);
+ return (priv->source_locale != NULL) ? priv->source_locale : "en_US";
+}
+
+/**
+ * as_translation_set_source_locale:
+ * @tr: a #AsTranslation instance.
+ * @locale: (nullable): The locale that the source strings are in, or %NULL if
+ * unknown or default.
+ *
+ * Set the locale of the source strings for this component. In gettext, this is
+ * referred to as the `C` locale. It’s almost always `en_US`, but for some
+ * components it may not be.
+ *
+ * Since: 0.14.6
+ */
+void
+as_translation_set_source_locale (AsTranslation *tr, const gchar *locale)
+{
+ AsTranslationPrivate *priv = GET_PRIVATE (tr);
+ as_ref_string_assign_safe (&priv->source_locale, locale);
+}
+
/**
* as_translation_load_from_xml:
* @tr: a #AsTranslation instance.
@@ -194,6 +232,9 @@ as_translation_load_from_xml (AsTranslation *tr, AsContext *ctx, xmlNode *node,
if (priv->kind == AS_TRANSLATION_KIND_UNKNOWN)
return FALSE;
+ as_ref_string_assign_transfer (&priv->source_locale,
+ as_xml_get_prop_value_refstr (node, "source_locale"));
+
content = as_xml_get_node_value (node);
as_translation_set_id (tr, content);
@@ -221,6 +262,8 @@ as_translation_to_xml_node (AsTranslation *tr, AsContext *ctx, xmlNode *root)
n = xmlNewTextChild (root, NULL, (xmlChar*) "translation", (xmlChar*) priv->id);
xmlNewProp (n, (xmlChar*) "type",
(xmlChar*) as_translation_kind_to_string (priv->kind));
+
+ as_xml_add_text_prop (n, "source_locale", priv->source_locale);
}
/**
diff --git a/src/as-translation.h b/src/as-translation.h
index 24fd78b1..1a072d66 100644
--- a/src/as-translation.h
+++ b/src/as-translation.h
@@ -73,6 +73,10 @@ const gchar *as_translation_get_id (AsTranslation *tr);
void as_translation_set_id (AsTranslation *tr,
const gchar *id);
+const gchar *as_translation_get_source_locale (AsTranslation *tr);
+void as_translation_set_source_locale (AsTranslation *tr,
+ const gchar *locale);
+
G_END_DECLS
#endif /* __AS_TRANSLATION_H */
diff --git a/tests/samples/appdata.xml b/tests/samples/appdata.xml
index c7c18d3f..0bba771d 100644
--- a/tests/samples/appdata.xml
+++ b/tests/samples/appdata.xml
@@ -30,5 +30,5 @@
web
http://www.mozilla.com
- firefox
+ firefox
diff --git a/tests/test-compose.c b/tests/test-compose.c
index 1093c2ae..1bda4bc2 100644
--- a/tests/test-compose.c
+++ b/tests/test-compose.c
@@ -628,6 +628,9 @@ test_compose_locale_stats ()
g_assert_cmpint (as_component_get_language (cpt, "en_GB"), ==, 100);
g_assert_cmpint (as_component_get_language (cpt, "ru"), ==, 33);
+ /* the source locale should be 100% translated */
+ g_assert_cmpint (as_component_get_language (cpt, "en_US"), ==, 100);
+
/* try loading Qt translations, style 1 */
as_component_clear_languages (cpt);
as_translation_set_kind (tr, AS_TRANSLATION_KIND_QT);
@@ -642,6 +645,9 @@ test_compose_locale_stats ()
g_assert_cmpint (as_component_get_language (cpt, "fr"), ==, 100);
g_assert_cmpint (as_component_get_language (cpt, "de"), ==, -1);
+ /* the source locale should be 100% translated */
+ g_assert_cmpint (as_component_get_language (cpt, "en_US"), ==, 100);
+
/* try loading Qt translations, style 2 */
as_component_clear_languages (cpt);
as_translation_set_kind (tr, AS_TRANSLATION_KIND_QT);
@@ -671,6 +677,53 @@ test_compose_locale_stats ()
g_assert_cmpint (as_component_get_language (cpt, "de"), ==, 100);
}
+static void
+test_compose_source_locale (void)
+{
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(AscResult) cres = NULL;
+ g_autoptr(AsComponent) cpt = NULL;
+ g_autoptr(AsTranslation) tr = NULL;
+ g_autoptr(AscDirectoryUnit) dirunit = asc_directory_unit_new (datadir);
+
+ /* open sample data directory unit */
+ ret = asc_unit_open (ASC_UNIT (dirunit), &error);
+ g_assert_no_error (error);
+ g_assert_true (ret);
+
+ /* create dummy result with a dummy component, and set a non-standard
+ * source locale on the translation */
+ cpt = as_component_new ();
+ as_component_set_id (cpt, "org.freedesktop.appstream.dummy");
+
+ tr = as_translation_new ();
+ as_translation_set_kind (tr, AS_TRANSLATION_KIND_GETTEXT);
+ as_translation_set_id (tr, "app");
+ as_translation_set_source_locale (tr, "de");
+ as_component_add_translation (cpt, tr);
+
+ cres = asc_result_new ();
+ ret = asc_result_add_component_with_string (cres, cpt, "", &error);
+ g_assert_no_error (error);
+ g_assert_true (ret);
+
+ /* try loading a Gettext translation */
+ asc_read_translation_status (cres,
+ ASC_UNIT (dirunit),
+ "/usr",
+ 25);
+ asc_assert_no_hints_in_result (cres);
+ g_assert_cmpint (as_component_get_language (cpt, "en_GB"), ==, 100);
+ g_assert_cmpint (as_component_get_language (cpt, "ru"), ==, 33);
+
+ /* the source locale should be 100% translated */
+ g_assert_cmpint (as_component_get_language (cpt, "de"), ==, 100);
+
+ /* and the default source locale should not be translated */
+ g_assert_cmpint (as_component_get_language (cpt, "en_US"), ==, -1);
+}
+
int
main (int argc, char **argv)
{
@@ -701,6 +754,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/Compose/DesktopEntry", test_compose_desktop_entry);
g_test_add_func ("/AppStream/Compose/DirectoryUnit", test_compose_directory_unit);
g_test_add_func ("/AppStream/Compose/LocaleStats", test_compose_locale_stats);
+ g_test_add_func ("/AppStream/Compose/SourceLocale", test_compose_source_locale);
ret = g_test_run ();
g_free (datadir);
diff --git a/tests/test-xmldata.c b/tests/test-xmldata.c
index d76a2adc..3d7abb23 100644
--- a/tests/test-xmldata.c
+++ b/tests/test-xmldata.c
@@ -192,6 +192,7 @@ test_appstream_parser_locale ()
g_assert_cmpint (trs->len, ==, 1);
tr = AS_TRANSLATION (g_ptr_array_index (trs, 0));
g_assert_cmpstr (as_translation_get_id (tr), ==, "firefox");
+ g_assert_cmpstr (as_translation_get_source_locale (tr), ==, "de");
/* check if we loaded the right amount of icons */
g_assert_cmpint (as_component_get_icons (cpt)->len, ==, 2);
@@ -236,7 +237,7 @@ test_appstream_write_locale ()
" x-scheme-handler/http\n"
" x-scheme-handler/https\n"
" \n"
- " firefox\n"
+ " firefox\n"
" \n"
" internet\n"
" web\n"