Version 0.23 - September 2024
* Programming languages support:
- - XML: XML schemas for .its and .loc files are now provided.
+ - XML:
+ o The escaping of characters such as & < > has been changed:
+ - No escaping is done any more by xgettext, when creating a POT file.
+ - Instead, extra escaping can be requested for the msgfmt pass, when
+ merging into an XML file.
+ - The default value of 'escape' in the <gt:escapeRule> was "yes";
+ now it is "no".
+ This means that existing translations of older POT files may no longer
+ fully apply. As a maintainer of a package that has translatable XML files,
+ you need to regenerate the POT file and pass it on to your translators.
+ o XML schemas for .its and .loc files are now provided.
- Python:
o xgettext now assumes source code for Python 3 rather than Python 2.
This affects the interpretation of escape sequences in string literals.
@subsection Preparing Rules for XML Internationalization
@cindex preparing rules for XML translation
+@c The ITS support in GNU gettext was designed so as to supersede
+@c the GNOME itstool <https://itstool.org/>. See
+@c <https://lists.gnu.org/archive/html/bug-gettext/2015-10/msg00001.html> and
+@c <https://mail.gnome.org/archives/desktop-devel-list/2015-October/msg00013.html>.
+
@menu
* ITS Rules:: Specifying ITS Rules
* Locating Rules:: Specifying where to find the ITS Rules
@table @samp
@item Context
+@c Rationale: Glade 2.
This data category associates @code{msgctxt} to the extracted text. In
the global rule, the @code{contextRule} element contains the following:
selector pointing to a node that holds the @code{msgid} value.
@end itemize
-@item Escape Special Characters
-
-This data category indicates whether the special XML characters
-(@code{<}, @code{>}, @code{&}, @code{"}) are escaped with entity
-reference. In the global rule, the @code{escapeRule} element contains
-the following:
-
-@itemize @bullet
-@item
-A required @code{selector} attribute. It contains an absolute selector
-that selects the nodes to which this rule applies.
-
-@item
-A required @code{escape} attribute with the value @code{yes} or @code{no}.
-@end itemize
-
@item Extended Preserve Space
+@c Rationale: GSettings.
This data category extends the standard @samp{Preserve Space} data
category with the additional values @samp{trim} and @samp{paragraph}.
@code{preserve}, @code{trim}, or @code{paragraph}.
@end itemize
+@item Escape Special Characters
+
+This data category indicates whether the special XML characters
+(@code{<}, @code{>}, @code{&}, @code{"}) are escaped with entity
+references. In the global rule, the @code{escapeRule} element contains
+the following:
+
+@itemize @bullet
+@item
+A required @code{selector} attribute. It contains an absolute selector
+that selects the nodes to which this rule applies.
+
+@item
+A required @code{escape} attribute with the value @code{yes} or @code{no}.
+@end itemize
+
+@noindent
+The default value, @code{no}, should be good for most XML file types.
+A rule with @code{escape="no"},
+that was necessary with GNU gettext versions before 0.23,
+is now redundant.
+
@end table
All those extended data categories can only be expressed with global
@subsubsection Two Use-cases of Translated Strings in XML
-For XML, there are two use-cases of translated strings. One is the case
-where the translated strings are directly consumed by programs, and the
-other is the case where the translated strings are merged back to the
-original XML document. In the former case, special characters in the
-extracted strings shouldn't be escaped, while they should in the latter
-case. To control whether to escape special characters, the @samp{Escape
-Special Characters} data category can be used.
-
-To merge the translations, the @samp{msgfmt} program can be used with
-the option @code{--xml}. @xref{msgfmt Invocation}, for more details
-about how one calls the @samp{msgfmt} program. @samp{msgfmt}'s
-@code{--xml} option doesn't perform character escaping, so translated
-strings can have arbitrary XML constructs, such as elements for markup.
+After strings have been extracted from an XML file to a POT file
+through @code{xgettext}
+and the translator has produced a PO file with translations,
+it can be used in two ways:
+
+@itemize @bullet
+@item
+The PO file (or the MO file generated from it) can be directly consumed
+by a program.
+
+@item
+Or the translated strings can be merged back to the original XML document.
+To do this use the @code{msgfmt} program with the option @code{--xml}.
+@xref{msgfmt Invocation}, for more details about how one calls
+the @samp{msgfmt} program.
+
+During this merge from a PO file into an XML file, it may happen that
+more escaping of special characters for XML is needed
+than what @code{msgfmt} does by default.
+In this case, you can enforce more escaping
+either throuch an @code{<escapeRule>} ITS rule,
+or through an attribute @code{gt:escape="yes"} on the particular XML element.
+
+@end itemize
@c This is the template for new data formats.
@ignore
<?xml version="1.0"?>
<!--
- Copyright (C) 2015 Free Software Foundation, Inc.
+ Copyright (C) 2015-2024 Free Software Foundation, Inc.
This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
This program is free software: you can redistribute it and/or modify
translate="yes"/>
<its:preserveSpaceRule selector="/GTK-Interface" space="preserve"/>
+
+ <!-- This rule is redundant since gettext 0.23. -->
<gt:escapeRule selector="/GTK-Interface" escape="no"/>
</its:rules>
<?xml version="1.0"?>
<!--
- Copyright (C) 2015 Free Software Foundation, Inc.
+ Copyright (C) 2015-2024 Free Software Foundation, Inc.
This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
This program is free software: you can redistribute it and/or modify
textPointer="substring-after(., '|')"/>
<its:preserveSpaceRule selector="/glade-interface" space="preserve"/>
+
+ <!-- This rule is redundant since gettext 0.23. -->
<gt:escapeRule selector="/glade-interface" escape="no"/>
</its:rules>
<?xml version="1.0"?>
<!--
- Copyright (C) 2015 Free Software Foundation, Inc.
+ Copyright (C) 2015-2024 Free Software Foundation, Inc.
This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
This program is free software: you can redistribute it and/or modify
<gt:escapeRule selector="//default/@context" escape="no"/>
<gt:preserveSpaceRule selector="//default" space="trim"/>
+
+ <!-- This rule is redundant since gettext 0.23. -->
<gt:escapeRule selector="/schemalist" escape="no"/>
</its:rules>
<?xml version="1.0"?>
<!--
- Copyright (C) 2015, 2023 Free Software Foundation, Inc.
+ Copyright (C) 2015-2024 Free Software Foundation, Inc.
This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
This program is free software: you can redistribute it and/or modify
<gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
<its:preserveSpaceRule selector="/interface" space="preserve"/>
+
+ <!-- This rule is redundant since gettext 0.23. -->
<gt:escapeRule selector="/interface" escape="no"/>
</its:rules>
<?xml version="1.0"?>
<!--
- Copyright (C) 2015, 2017 Free Software Foundation, Inc.
+ Copyright (C) 2015-2024 Free Software Foundation, Inc.
This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
This program is free software: you can redistribute it and/or modify
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<its:rules xmlns:its="http://www.w3.org/2005/11/its"
+ xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
version="2.0">
<its:translateRule selector="/component" translate="no"/>
<its:translateRule selector="/component/name |
/component/developer_name |
/component/screenshots/screenshot/caption"
translate="yes"/>
+
+ <!-- This rule is redundant since gettext 0.23. -->
+ <gt:escapeRule selector="/component" escape="no"/>
</its:rules>
</complexType>
<!-- If no <gt:escapeRule> is present, the default 'escape' property
- is "yes". -->
+ is "no". -->
<complexType name="EscapeRuleType">
<attribute name="selector" type="string" use="required"></attribute>
<attribute name="escape" use="required">
{
/* FIXME: Respect space attribute. */
char *content = _its_collect_text_content (n, ITS_WHITESPACE_NORMALIZE,
- true);
+ false);
its_value_list_append (&rule->values, "locNote", content);
free (content);
}
struct its_value_list_ty *values;
const char *value;
char *msgid = NULL, *msgctxt = NULL, *comment = NULL;
- bool no_escape;
+ bool do_escape;
+ bool do_escape_during_extract;
enum its_whitespace_type_ty whitespace;
values = its_rule_list_eval (rules, node);
value = its_value_list_get_value (values, "escape");
- no_escape = value != NULL && strcmp (value, "no") == 0;
+ do_escape = value != NULL && strcmp (value, "yes") == 0;
+ /* Consider also a locally declared 'gt:escape' attribute. */
+ if (node->type == XML_ELEMENT_NODE
+ && xmlHasNsProp (node, BAD_CAST "escape", BAD_CAST GT_NS))
+ {
+ char *prop = _its_get_attribute (node, "escape", GT_NS);
+ if (strcmp (prop, "yes") == 0 || strcmp (prop, "no") == 0)
+ do_escape = strcmp (prop, "yes") == 0;
+ free (prop);
+ }
+
+ do_escape_during_extract = do_escape;
+ /* But no, during message extraction (i.e. what xgettext does), we do
+ *not* want escaping to be done. The contents of the POT file is meant
+ for translators, and
+ - the messages are not labelled as requiring XML content syntax,
+ - it is better for the translators if they can write various
+ characters such as & < > without escaping them.
+ Escaping needs to happen in the message merge phase (i.e. what msgfmt
+ does) instead. */
+ do_escape_during_extract = false;
value = its_value_list_get_value (values, "locNote");
if (value)
value = its_value_list_get_value (values, "locNotePointer");
if (value)
comment = _its_get_content (rules, node, value, ITS_WHITESPACE_TRIM,
- !no_escape);
+ do_escape_during_extract);
}
if (comment != NULL && *comment != '\0')
value = its_value_list_get_value (values, "contextPointer");
if (value)
msgctxt = _its_get_content (rules, node, value, ITS_WHITESPACE_PRESERVE,
- !no_escape);
+ do_escape_during_extract);
value = its_value_list_get_value (values, "textPointer");
if (value)
msgid = _its_get_content (rules, node, value, ITS_WHITESPACE_PRESERVE,
- !no_escape);
+ do_escape_during_extract);
its_value_list_destroy (values);
free (values);
if (msgid == NULL)
- msgid = _its_collect_text_content (node, whitespace, !no_escape);
+ msgid = _its_collect_text_content (node, whitespace,
+ do_escape_during_extract);
if (*msgid != '\0')
{
lex_pos_ty pos;
struct its_node_list_ty nodes;
};
+/* Returns true if S starts with a character reference. */
+static bool
+starts_with_character_reference (const char *s)
+{
+ /* <https://www.w3.org/TR/xml/#NT-CharRef> defines
+ CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';' */
+ if (*s == '&')
+ {
+ s++;
+ if (*s == '#')
+ {
+ s++;
+ if (*s >= '0' && *s <= '9')
+ {
+ do
+ s++;
+ while (*s >= '0' && *s <= '9');
+ return *s == ';';
+ }
+ if (*s == 'x')
+ {
+ s++;
+ if ((*s >= '0' && *s <= '9')
+ || (*s >= 'A' && *s <= 'F')
+ || (*s >= 'a' && *s <= 'f'))
+ {
+ do
+ s++;
+ while ((*s >= '0' && *s <= '9')
+ || (*s >= 'A' && *s <= 'F')
+ || (*s >= 'a' && *s <= 'f'));
+ return *s == ';';
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static char *
+_its_encode_special_chars_for_merge (const char *content)
+{
+ const char *str;
+ size_t amount = 0;
+ char *result, *p;
+
+ for (str = content; *str != '\0'; str++)
+ {
+ if (*str == '&' && starts_with_character_reference (str))
+ amount += sizeof ("&");
+ else if (*str == '<')
+ amount += sizeof ("<");
+ else if (*str == '>')
+ amount += sizeof (">");
+ else
+ amount += 1;
+ }
+
+ result = XNMALLOC (amount + 1, char);
+ *result = '\0';
+ p = result;
+ for (str = content; *str != '\0'; str++)
+ {
+ if (*str == '&' && starts_with_character_reference (str))
+ p = stpcpy (p, "&");
+ else if (*str == '<')
+ p = stpcpy (p, "<");
+ else if (*str == '>')
+ p = stpcpy (p, ">");
+ else
+ *p++ = *str;
+ }
+ *p = '\0';
+ return result;
+}
+
static void
its_merge_context_merge_node (struct its_merge_context_ty *context,
xmlNode *node,
struct its_value_list_ty *values;
const char *value;
char *msgid = NULL, *msgctxt = NULL;
- bool no_escape;
+ bool do_escape;
+ bool do_escape_during_extract;
+ bool do_escape_during_merge;
enum its_whitespace_type_ty whitespace;
values = its_rule_list_eval (context->rules, node);
value = its_value_list_get_value (values, "escape");
- no_escape = value != NULL && strcmp (value, "no") == 0;
+ do_escape = value != NULL && strcmp (value, "yes") == 0;
+ /* Consider also a locally declared 'gt:escape' attribute. */
+ if (xmlHasNsProp (node, BAD_CAST "escape", BAD_CAST GT_NS))
+ {
+ char *prop = _its_get_attribute (node, "escape", GT_NS);
+ if (strcmp (prop, "yes") == 0 || strcmp (prop, "no") == 0)
+ do_escape = strcmp (prop, "yes") == 0;
+ free (prop);
+ }
+
+ do_escape_during_extract = do_escape;
+ /* Like above, in its_rule_list_extract_text. */
+ do_escape_during_extract = false;
+
+ do_escape_during_merge = do_escape;
value = its_value_list_get_value (values, "space");
if (value && strcmp (value, "preserve") == 0)
value = its_value_list_get_value (values, "contextPointer");
if (value)
msgctxt = _its_get_content (context->rules, node, value,
- ITS_WHITESPACE_PRESERVE, !no_escape);
+ ITS_WHITESPACE_PRESERVE,
+ do_escape_during_extract);
value = its_value_list_get_value (values, "textPointer");
if (value)
msgid = _its_get_content (context->rules, node, value,
- ITS_WHITESPACE_PRESERVE, !no_escape);
+ ITS_WHITESPACE_PRESERVE,
+ do_escape_during_extract);
its_value_list_destroy (values);
free (values);
if (msgid == NULL)
- msgid = _its_collect_text_content (node, whitespace, !no_escape);
+ msgid = _its_collect_text_content (node, whitespace,
+ do_escape_during_extract);
if (*msgid != '\0')
{
message_ty *mp;
translated = xmlNewNode (node->ns, node->name);
xmlSetProp (translated, BAD_CAST "xml:lang", BAD_CAST language);
- xmlNodeAddContent (translated, BAD_CAST mp->msgstr);
+ /* libxml2 offers two functions for setting the content of an
+ element: xmlNodeSetContent and xmlNodeAddContent. They differ
+ in the amount of escaping they do:
+ - xmlNodeSetContent does no escaping, at the risk of creating
+ malformed XML.
+ - xmlNodeAddContent escapes all of & < >, which always produces
+ well-formed XML but is not the right thing for entity
+ references.
+ We need a middle ground between both, that is adapted to what
+ translators will usually produce.
+
+ translated | no escaping | middle-ground | full escaping
+ | SetContent | | AddContent
+ -----------------+-------------+---------------+--------------
+ & | & | & | &
+ " | " | " | &quot;
+ & | & | & | &amp;
+ < | < | < | <
+ > | > | > | >
+ < | < | < | &lt;
+ > | > | > | &gt;
+ © | © | &#xa9; | &#xa9;
+ © | © | © | &copy;
+ -----------------+-------------+---------------+--------------
+
+ The function _its_encode_special_chars_for_merge implements
+ this middle-ground. But we allow full escaping to be requested
+ through a gt:escape="yes" attribute. */
+
+ if (do_escape_during_merge)
+ {
+ /* These three are equivalent:
+ xmlNodeAddContent (translated, BAD_CAST mp->msgstr);
+ xmlNodeSetContent (translated, xmlEncodeEntitiesReentrant (context->doc, BAD_CAST mp->msgstr));
+ xmlNodeSetContent (translated, xmlEncodeSpecialChars (context->doc, BAD_CAST mp->msgstr)); */
+ xmlNodeAddContent (translated, BAD_CAST mp->msgstr);
+ }
+ else
+ {
+ char *middle_ground = _its_encode_special_chars_for_merge (mp->msgstr);
+ xmlNodeSetContent (translated, BAD_CAST middle_ground);
+ free (middle_ground);
+ }
+
xmlAddNextSibling (node, translated);
}
}
xgettext-13 xgettext-14 xgettext-15 xgettext-16 xgettext-17 \
xgettext-18 \
xgettext-combine-1 xgettext-combine-2 xgettext-combine-3 \
- xgettext-appdata-1 \
+ xgettext-appdata-1 xgettext-appdata-2 \
xgettext-awk-1 xgettext-awk-2 xgettext-awk-3 \
xgettext-awk-stackovfl-1 xgettext-awk-stackovfl-2 \
xgettext-c-2 xgettext-c-3 xgettext-c-4 xgettext-c-5 xgettext-c-6 \
cat <<\EOF > mf.appdata.xml
<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
+<!DOCTYPE component PUBLIC "" "" [
+<!ENTITY author1 "Giovanni Campagna">
+<!ENTITY author2 "Daiki Ueno">
+<!ENTITY author3 "Bilal Elmoussaoui">
+]>
+<component type="desktop" xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0">
<id>org.gnome.Characters.desktop</id>
<name>GNOME Characters</name>
<summary>Character map application</summary>
You can also browse characters by categories, such as
Punctuation, Pictures, etc.
</p>
+ <p gt:escape="yes">
+ Did you know that the copyright sign (©, U+00A9) can be written in HTML
+ as &#xa9;,
+ as &#169;,
+ or as &copy;?
+ </p>
+ <p>Written by &author1;, &author2;, and &author3;.</p>
+ <p gt:escape="no">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p gt:escape="yes">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
</description>
<url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
<updatecontact>dueno_at_src.gnome.org</updatecontact>
msgstr ""
"Vous pouvez aussi naviguer dans les caractères par catégories, comme par "
"Ponctuation, Images, etc."
+
+msgid ""
+"Did you know that the copyright sign (©, U+00A9) can be written in HTML as "
+"©, as ©, or as ©?"
+msgstr ""
+"Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML "
+"comme ©, comme © ou comme © ?"
+
+msgid "Written by &author1;, &author2;, and &author3;."
+msgstr "Écrit par &author1;, &author2;, et &author3;."
+
+msgid ""
+"Escape gallery: operator x&y, standard XML entities & \" ' & < >, character "
+"reference ©, escaped character reference ©, entity references © "
+"&author1;"
+msgstr ""
+"Exposition d'échappements: operateur x&y, entités XML standard & \" ' & < >, "
+"caractère ©, caractère échappé ©, entités © &author1;"
EOF
cat <<\EOF > mf.appdata.xml.ok
<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
+<!DOCTYPE component PUBLIC "" "" [
+<!ENTITY author1 "Giovanni Campagna">
+<!ENTITY author2 "Daiki Ueno">
+<!ENTITY author3 "Bilal Elmoussaoui">
+]>
+<component xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0" type="desktop">
<id>org.gnome.Characters.desktop</id>
<name>GNOME Characters</name>
<summary>Character map application</summary>
Punctuation, Pictures, etc.
</p>
<p xml:lang="fr">Vous pouvez aussi naviguer dans les caractères par catégories, comme par Ponctuation, Images, etc.</p>
+ <p gt:escape="yes">
+ Did you know that the copyright sign (©, U+00A9) can be written in HTML
+ as &#xa9;,
+ as &#169;,
+ or as &copy;?
+ </p>
+ <p xml:lang="fr">Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML comme &#xa9;, comme &#169; ou comme &copy; ?</p>
+ <p>Written by &author1;, &author2;, and &author3;.</p>
+ <p xml:lang="fr">Écrit par &author1;, &author2;, et &author3;.</p>
+ <p gt:escape="no">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & < >, caractère ©, caractère échappé &#xa9;, entités © &author1;</p>
+ <p gt:escape="yes">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & < >, caractère ©, caractère échappé &#xa9;, entités &copy; &author1;</p>
</description>
<url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
<updatecontact>dueno_at_src.gnome.org</updatecontact>
cat <<\EOF > mf.appdata.xml
<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
+<!DOCTYPE component PUBLIC "" "" [
+<!ENTITY author1 "Giovanni Campagna">
+<!ENTITY author2 "Daiki Ueno">
+<!ENTITY author3 "Bilal Elmoussaoui">
+]>
+<component type="desktop" xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0">
<id>org.gnome.Characters.desktop</id>
<name>GNOME Characters</name>
<summary>Character map application</summary>
You can also browse characters by categories, such as
Punctuation, Pictures, etc.
</p>
+ <p gt:escape="yes">
+ Did you know that the copyright sign (©, U+00A9) can be written in HTML
+ as &#xa9;,
+ as &#169;,
+ or as &copy;?
+ </p>
+ <p>Written by &author1;, &author2;, and &author3;.</p>
+ <p gt:escape="no">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p gt:escape="yes">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
</description>
<url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
<updatecontact>dueno_at_src.gnome.org</updatecontact>
msgstr ""
"Vous pouvez aussi naviguer dans les caractères par catégories, comme par "
"Ponctuation, Images, etc."
+
+msgid ""
+"Did you know that the copyright sign (©, U+00A9) can be written in HTML as "
+"©, as ©, or as ©?"
+msgstr ""
+"Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML "
+"comme ©, comme © ou comme © ?"
+
+msgid "Written by &author1;, &author2;, and &author3;."
+msgstr "Écrit par &author1;, &author2;, et &author3;."
+
+msgid ""
+"Escape gallery: operator x&y, standard XML entities & \" ' & < >, character "
+"reference ©, escaped character reference ©, entity references © "
+"&author1;"
+msgstr ""
+"Exposition d'échappements: operateur x&y, entités XML standard & \" ' & < >, "
+"caractère ©, caractère échappé ©, entités © &author1;"
EOF
cat <<\EOF > po/de.po
msgstr ""
"Sie können ebenfalls nach Kategorie suchen, wie z.B. nach Zeichensetzung oder "
"Bildern."
+
+msgid ""
+"Did you know that the copyright sign (©, U+00A9) can be written in HTML as "
+"©, as ©, or as ©?"
+msgstr ""
+"Wussten Sie, dass das Copyright-Zeichen (©, U+00A9) in HTML als "
+"©, als ©, oder als © "
+"geschrieben werden kann?"
+
+msgid "Written by &author1;, &author2;, and &author3;."
+msgstr "Geschrieben von &author1;, &author2; und &author3;."
+
+msgid ""
+"Escape gallery: operator x&y, standard XML entities & \" ' & < >, character "
+"reference ©, escaped character reference ©, entity references © "
+"&author1;"
+msgstr ""
+"Escape-Beispiele: Operator x&y, Standard-XML Entitäten & \" ' & < >, Zeichen "
+"©, escaptes Zeichen ©, Entitäten © &author1;"
EOF
cat <<\EOF > mf.appdata.xml.ok
<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
+<!DOCTYPE component PUBLIC "" "" [
+<!ENTITY author1 "Giovanni Campagna">
+<!ENTITY author2 "Daiki Ueno">
+<!ENTITY author3 "Bilal Elmoussaoui">
+]>
+<component xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0" type="desktop">
<id>org.gnome.Characters.desktop</id>
<name>GNOME Characters</name>
<summary>Character map application</summary>
</p>
<p xml:lang="fr">Vous pouvez aussi naviguer dans les caractères par catégories, comme par Ponctuation, Images, etc.</p>
<p xml:lang="de">Sie können ebenfalls nach Kategorie suchen, wie z.B. nach Zeichensetzung oder Bildern.</p>
+ <p gt:escape="yes">
+ Did you know that the copyright sign (©, U+00A9) can be written in HTML
+ as &#xa9;,
+ as &#169;,
+ or as &copy;?
+ </p>
+ <p xml:lang="fr">Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML comme &#xa9;, comme &#169; ou comme &copy; ?</p>
+ <p xml:lang="de">Wussten Sie, dass das Copyright-Zeichen (©, U+00A9) in HTML als &#xa9;, als &#169;, oder als &copy; geschrieben werden kann?</p>
+ <p>Written by &author1;, &author2;, and &author3;.</p>
+ <p xml:lang="fr">Écrit par &author1;, &author2;, et &author3;.</p>
+ <p xml:lang="de">Geschrieben von &author1;, &author2; und &author3;.</p>
+ <p gt:escape="no">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & < >, caractère ©, caractère échappé &#xa9;, entités © &author1;</p>
+ <p xml:lang="de">Escape-Beispiele: Operator x&y, Standard-XML Entitäten & " ' & < >, Zeichen ©, escaptes Zeichen &#xa9;, Entitäten © &author1;</p>
+ <p gt:escape="yes">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & < >, caractère ©, caractère échappé &#xa9;, entités &copy; &author1;</p>
+ <p xml:lang="de">Escape-Beispiele: Operator x&y, Standard-XML Entitäten & " ' & < >, Zeichen ©, escaptes Zeichen &#xa9;, Entitäten &copy; &author1;</p>
</description>
<url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
<updatecontact>dueno_at_src.gnome.org</updatecontact>
cat <<\EOF > mf.appdata.xml.desired.ok
<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
+<!DOCTYPE component PUBLIC "" "" [
+<!ENTITY author1 "Giovanni Campagna">
+<!ENTITY author2 "Daiki Ueno">
+<!ENTITY author3 "Bilal Elmoussaoui">
+]>
+<component xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0" type="desktop">
<id>org.gnome.Characters.desktop</id>
<name>GNOME Characters</name>
<summary>Character map application</summary>
Punctuation, Pictures, etc.
</p>
<p xml:lang="fr">Vous pouvez aussi naviguer dans les caractères par catégories, comme par Ponctuation, Images, etc.</p>
+ <p gt:escape="yes">
+ Did you know that the copyright sign (©, U+00A9) can be written in HTML
+ as &#xa9;,
+ as &#169;,
+ or as &copy;?
+ </p>
+ <p xml:lang="fr">Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML comme &#xa9;, comme &#169; ou comme &copy; ?</p>
+ <p>Written by &author1;, &author2;, and &author3;.</p>
+ <p xml:lang="fr">Écrit par &author1;, &author2;, et &author3;.</p>
+ <p gt:escape="no">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & < >, caractère ©, caractère échappé &#xa9;, entités © &author1;</p>
+ <p gt:escape="yes">Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;</p>
+ <p xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & < >, caractère ©, caractère échappé &#xa9;, entités &copy; &author1;</p>
</description>
<url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
<updatecontact>dueno_at_src.gnome.org</updatecontact>
#!/bin/sh
. "${srcdir=.}/init.sh"; path_prepend_ . ../src
-# Test of AppData support.
+# Test of AppData support: HTML markup.
cat <<\EOF > xg-gs-1-empty.appdata.xml
<?xml version="1.0"?>
--- /dev/null
+#!/bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test of AppData support: escaping of XML entities.
+
+cat <<\EOF > xg-gs-2-empty.appdata.xml
+<?xml version="1.0"?>
+<component type="desktop"/>
+EOF
+
+: ${XGETTEXT=xgettext}
+${XGETTEXT} -o xg-gs-2.pot xg-gs-2-empty.appdata.xml 2>/dev/null
+test $? = 0 || {
+ echo "Skipping test: xgettext was built without AppData support"
+ Exit 77
+}
+
+cat <<\EOF > xg-gs-2.appdata.xml
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE component PUBLIC "" "" [
+<!ENTITY author1 "Giovanni Campagna">
+<!ENTITY author2 "Daiki Ueno">
+<!ENTITY author3 "Bilal Elmoussaoui">
+]>
+<component type="desktop">
+ <id>org.gnome.Characters.desktop</id>
+ <name>GNOME Characters</name>
+ <summary>Character map application</summary>
+ <licence>CC0</licence>
+ <description>
+ <p>
+ Characters is a simple utility application to find and insert
+ unusual characters. It allows you to quickly find the character
+ you are looking for by searching for keywords.
+ </p>
+ <p>
+ You can also browse characters by categories, such as
+ Punctuation, Pictures, etc.
+ </p>
+ <p>
+ Did you know that the copyright sign (©, U+00A9) can be written in HTML
+ as &#xa9;,
+ as &#169;,
+ or as &copy;?
+ </p>
+ <p>Written by &author1;, &author2;, and &author3;.</p>
+ <p>Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;, escaped entity reference &copy;</p>
+ <p>Escape gallery: operator x&y, standard XML entities & " ' & < >, character reference ©, escaped character reference &#xa9;, entity references © &author1;, escaped entity reference &copy;</p>
+ </description>
+ <url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
+ <updatecontact>dueno_at_src.gnome.org</updatecontact>
+</component>
+EOF
+
+: ${XGETTEXT=xgettext}
+${XGETTEXT} --add-comments -o xg-gs-2.tmp xg-gs-2.appdata.xml || Exit 1
+func_filter_POT_Creation_Date xg-gs-2.tmp xg-gs-2.pot
+
+cat <<\EOF > xg-gs-2.ok
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: xg-gs-2.appdata.xml:9
+msgid "GNOME Characters"
+msgstr ""
+
+#: xg-gs-2.appdata.xml:10
+msgid "Character map application"
+msgstr ""
+
+#: xg-gs-2.appdata.xml:13
+msgid ""
+"Characters is a simple utility application to find and insert unusual "
+"characters. It allows you to quickly find the character you are looking for "
+"by searching for keywords."
+msgstr ""
+
+#: xg-gs-2.appdata.xml:18
+msgid ""
+"You can also browse characters by categories, such as Punctuation, Pictures, "
+"etc."
+msgstr ""
+
+#: xg-gs-2.appdata.xml:22
+msgid ""
+"Did you know that the copyright sign (©, U+00A9) can be written in HTML as "
+"©, as ©, or as ©?"
+msgstr ""
+
+#: xg-gs-2.appdata.xml:28
+msgid "Written by &author1;, &author2;, and &author3;."
+msgstr ""
+
+#: xg-gs-2.appdata.xml:29 xg-gs-2.appdata.xml:30
+msgid ""
+"Escape gallery: operator x&y, standard XML entities & \" ' & < >, character "
+"reference ©, escaped character reference ©, entity references © "
+"&author1;, escaped entity reference ©"
+msgstr ""
+EOF
+
+: ${DIFF=diff}
+${DIFF} xg-gs-2.ok xg-gs-2.pot
+result=$?
+
+exit $result
cat <<\EOF >messages.ok
#. (itstool) path: message/p
#: messages.xml:8
-msgid "This is a test message &foo;><&\"\""
+msgid "This is a test message &foo;><&\"\""
msgstr ""
#. (itstool) path: message/p
#: messages.xml:17
#, no-wrap
msgid ""
-" $ echo ' ' >> /dev/null\n"
-" $ cat < /dev/yes\n"
-" $ sleep 10 &\n"
+" $ echo ' ' >> /dev/null\n"
+" $ cat < /dev/yes\n"
+" $ sleep 10 &\n"
msgstr ""
#. This is a comment
#. (itstool) path: messages/message@comment
#: messages.xml:22
-msgid "This is a comment <>&""
+msgid "This is a comment <>&\""
msgstr ""
#. (itstool) path: message/p