]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
msgfmt: New option --replace-text.
authorBruno Haible <bruno@clisp.org>
Sun, 6 Oct 2024 07:43:43 +0000 (09:43 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 6 Oct 2024 07:43:43 +0000 (09:43 +0200)
Reported by Asterix <asterix@lagaule.org>
at <https://savannah.gnu.org/bugs/?52159>.

* gettext-tools/src/its.h (its_merge_context_merge): Add parameter replace_text.
* gettext-tools/src/its.c (its_merge_context_merge_node): Add parameter
replace_text.
(its_merge_context_merge): Likewise.
* gettext-tools/src/write-xml.h (msgdomain_write_xml, msgdomain_write_xml_bulk):
Add parameter replace_text.
* gettext-tools/src/write-xml.c (msgdomain_write_xml_bulk): Add parameter
replace_text.
(msgdomain_write_xml): Likewise.
* gettext-tools/src/msgfmt.c (xml_replace_text): New variable.
(long_options): Add --replace-text.
(main): Handle --replace-text.
(usage): Document option --replace-text.
* gettext-tools/tests/msgfmt-xml-4: New file, based on
gettext-tools/tests/msgfmt-xml-1.
* gettext-tools/tests/Makefile.am (TESTS): Add it.
* gettext-tools/doc/msgfmt.texi: Document option --replace-text.
* NEWS: Mention the change.

NEWS
gettext-tools/doc/msgfmt.texi
gettext-tools/src/its.c
gettext-tools/src/its.h
gettext-tools/src/msgfmt.c
gettext-tools/src/write-xml.c
gettext-tools/src/write-xml.h
gettext-tools/tests/Makefile.am
gettext-tools/tests/msgfmt-xml-4 [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 4421854b5cba61a154e2dadb50558f04167e656b..46e57bee855013748e858a4792bd29213ae40e2b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Version 0.23 - September 2024
+Version 0.23 - October 2024
 
 * Programming languages support:
   - XML:
@@ -14,6 +14,8 @@ Version 0.23 - September 2024
     o XML schemas for .its and .loc files are now provided.
     o The value of the xml:lang attribute, inserted by msgfmt, now conforms
       to W3C standards.
+    o 'msgfmt --xml' accept an option --replace-text, that causes the output
+      to be a mono-lingual XML file instead of a multi-lingual XML file.
   - Python:
     o xgettext now assumes source code for Python 3 rather than Python 2.
       This affects the interpretation of escape sequences in string literals.
index aeb204f104648aa9ec940f79fce475f330b9262d..9485c40fc811296a4c6c1cdd964386137acffc77 100644 (file)
@@ -176,7 +176,7 @@ Specify the base directory of @file{.msg} message catalogs.
 The @samp{-l} and @samp{-d} options are mandatory.  The @file{.msg} file is
 written in the specified directory.
 
-@subsection Desktop Entry mode operations
+@subsection Desktop Entry mode options
 
 @table @samp
 @item --template=@var{template}
@@ -227,7 +227,7 @@ variable.
 For either operation modes, the @samp{-o} and @samp{--template}
 options are mandatory.
 
-@subsection XML mode operations
+@subsection XML mode options
 
 @table @samp
 @item --template=@var{template}
@@ -252,6 +252,12 @@ or a combined language and country specification of the form @var{ll_CC}.
 @opindex -d@r{, @code{msgfmt} option}
 Specify the base directory of @file{.po} message catalogs.
 
+@item --replace-text
+@opindex --replace-text@r{, @code{msgfmt} option}
+Output XML with translated text replacing the original text,
+not augmenting the original text.
+With this option, @code{msgfmt} produces a mono-lingual XML file.
+Without this option, it produces a multi-lingual XML file.
 @end table
 
 To generate an XML file for a single locale, you can use it as follows.
index 98521c5b2fb5c8288520584e042559c20ece57ff..0aabb6e9a5acc2ab609cd5c2e67007a97637bc09 100644 (file)
@@ -2079,7 +2079,8 @@ static void
 its_merge_context_merge_node (struct its_merge_context_ty *context,
                               xmlNode *node,
                               const char *language,
-                              message_list_ty *mlp)
+                              message_list_ty *mlp,
+                              bool replace_text)
 {
   if (node->type == XML_ELEMENT_NODE)
     {
@@ -2147,9 +2148,19 @@ its_merge_context_merge_node (struct its_merge_context_ty *context,
               xmlNode *translated;
               char language_bcp47[BCP47_MAX];
 
-              /* Create a new element node, of the same name, with the same
-                 attributes.  */
-              translated = _its_copy_node_with_attributes (node);
+              if (replace_text)
+                {
+                  /* Reuse the node.  But first, clear its text content and all
+                     its children nodes (except the attributes).  */
+                  xmlNodeSetContent (node, NULL);
+                  translated = node;
+                }
+              else
+                {
+                  /* Create a new element node, of the same name, with the same
+                     attributes.  */
+                  translated = _its_copy_node_with_attributes (node);
+                }
 
               /* Set the xml:lang attribute.
                  <https://www.w3.org/International/questions/qa-when-xmllang.en.html>
@@ -2202,7 +2213,8 @@ its_merge_context_merge_node (struct its_merge_context_ty *context,
                   free (middle_ground);
                 }
 
-              xmlAddNextSibling (node, translated);
+              if (!replace_text)
+                xmlAddNextSibling (node, translated);
             }
         }
       free (msgctxt);
@@ -2213,14 +2225,16 @@ its_merge_context_merge_node (struct its_merge_context_ty *context,
 void
 its_merge_context_merge (its_merge_context_ty *context,
                          const char *language,
-                         message_list_ty *mlp)
+                         message_list_ty *mlp,
+                         bool replace_text)
 {
   size_t i;
 
   for (i = 0; i < context->nodes.nitems; i++)
     its_merge_context_merge_node (context, context->nodes.items[i],
                                   language,
-                                  mlp);
+                                  mlp,
+                                  replace_text);
 }
 
 struct its_merge_context_ty *
index 9fe03e1dd2bb1a36d5b937d30478539482e4b59a..f5221ef495a6d64e6eb353415fe5dee2d7660aec 100644 (file)
@@ -84,7 +84,8 @@ extern its_merge_context_ty *
 extern void its_merge_context_free (its_merge_context_ty *context);
 extern void its_merge_context_merge (its_merge_context_ty *context,
                                      const char *language,
-                                     message_list_ty *mlp);
+                                     message_list_ty *mlp,
+                                     bool replace_text);
 
 extern void its_merge_context_write (its_merge_context_ty *context,
                                      FILE *fp);
index 0ea1fde4a45e7d4d5f01d2de79ffe0fdec2979c2..b26f5c0800c52cd06d6e1e93a39abffe93133855 100644 (file)
@@ -120,6 +120,7 @@ static bool desktop_default_keywords = true;
 
 /* XML mode output file specification.  */
 static bool xml_mode;
+static bool xml_replace_text;
 static const char *xml_locale_name;
 static const char *xml_template_name;
 static const char *xml_base_directory;
@@ -204,6 +205,7 @@ static const struct option long_options[] =
   { "output-file", required_argument, NULL, 'o' },
   { "properties-input", no_argument, NULL, 'P' },
   { "qt", no_argument, NULL, CHAR_MAX + 9 },
+  { "replace-text", no_argument, NULL, CHAR_MAX + 19 },
   { "resource", required_argument, NULL, 'r' },
   { "source", no_argument, NULL, CHAR_MAX + 14 },
   { "statistics", no_argument, &do_statistics, 1 },
@@ -434,6 +436,9 @@ main (int argc, char *argv[])
       case CHAR_MAX + 18: /* --no-redundancy */
         no_redundancy = true;
         break;
+      case CHAR_MAX + 19: /* --replace-text */
+        xml_replace_text = true;
+        break;
       default:
         usage (EXIT_FAILURE);
         break;
@@ -508,6 +513,12 @@ There is NO WARRANTY, to the extent permitted by law.\n\
                first_option, second_option);
       }
   }
+  if (!xml_mode && xml_replace_text)
+    {
+      error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
+             "--replace-text", "--xml");
+      usage (EXIT_FAILURE);
+    }
   if (java_mode)
     {
       if (output_file_name != NULL)
@@ -611,6 +622,10 @@ There is NO WARRANTY, to the extent permitted by law.\n\
                  "--xml");
           usage (EXIT_FAILURE);
         }
+      if (xml_replace_text && xml_base_directory != NULL)
+        error (EXIT_FAILURE, 0,
+               _("%s and %s are mutually exclusive in %s"),
+               "--replace-text", "-d", "--xml");
       if (xml_base_directory != NULL && xml_locale_name != NULL)
         error (EXIT_FAILURE, 0,
                _("%s and %s are mutually exclusive in %s"),
@@ -850,6 +865,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
                                    xml_locale_name,
                                    xml_template_name,
                                    xml_its_rules,
+                                   xml_replace_text,
                                    domain->file_name))
             exit_status = EXIT_FAILURE;
         }
@@ -1024,6 +1040,9 @@ XML mode options:\n"));
       printf (_("\
   -d DIRECTORY                base directory of .po files\n"));
       printf (_("\
+  --replace-text              output XML with translated text replacing the\n\
+                              original text, not augmenting the original text\n"));
+      printf (_("\
 The -l, -o, and --template options are mandatory.  If -D is specified, input\n\
 files are read from the directory instead of the command line arguments.\n"));
       printf ("\n");
@@ -1694,6 +1713,7 @@ msgfmt_xml_bulk (const char *directory,
   status = msgdomain_write_xml_bulk (&operands,
                                      template_file_name,
                                      its_rules,
+                                     false,
                                      file_name);
 
   msgfmt_operand_list_destroy (&operands);
index 6c94d7cad9b7ef2bdfbe811f7bc1a0edf976753a..9485941231674e0a6da4a85c2e599d5047b77fd0 100644 (file)
@@ -44,6 +44,7 @@ int
 msgdomain_write_xml_bulk (msgfmt_operand_list_ty *operands,
                           const char *template_file_name,
                           its_rule_list_ty *its_rules,
+                          bool replace_text,
                           const char *file_name)
 {
   its_merge_context_ty *context;
@@ -67,7 +68,8 @@ msgdomain_write_xml_bulk (msgfmt_operand_list_ty *operands,
   for (i = 0; i < operands->nitems; i++)
     its_merge_context_merge (context,
                              operands->items[i].language,
-                             operands->items[i].mlp);
+                             operands->items[i].mlp,
+                             replace_text);
   its_merge_context_write (context, fp);
   its_merge_context_free (context);
 
@@ -88,6 +90,7 @@ msgdomain_write_xml (message_list_ty *mlp,
                      const char *locale_name,
                      const char *template_file_name,
                      its_rule_list_ty *its_rules,
+                     bool replace_text,
                      const char *file_name)
 {
   msgfmt_operand_ty operand;
@@ -110,5 +113,6 @@ msgdomain_write_xml (message_list_ty *mlp,
   return msgdomain_write_xml_bulk (&operands,
                                    template_file_name,
                                    its_rules,
+                                   replace_text,
                                    file_name);
 }
index 5b80ac4b704290e17a0e02eb0ee66256e7999cbb..5f9343642c602423cb687a40f5cc9d6757b98f80 100644 (file)
@@ -1,5 +1,5 @@
 /* Writing XML files.
-   Copyright (C) 2024 Free Software Foundation, Inc.
+   Copyright (C) 2015-2024 Free Software Foundation, Inc.
    This file was written by Daiki Ueno <ueno@gnu.org>.
 
    This program is free software: you can redistribute it and/or modify
@@ -35,12 +35,14 @@ extern int
                             const char *locale_name,
                             const char *template_file_name,
                             its_rule_list_ty *its_rules,
+                            bool replace_text,
                             const char *file_name);
 
 extern int
        msgdomain_write_xml_bulk (msgfmt_operand_list_ty *operands,
                                  const char *template_file_name,
                                  its_rule_list_ty *its_rules,
+                                 bool replace_text,
                                  const char *file_name);
 
 #ifdef __cplusplus
index 9c7b3276e4d8c5b0f9233209e6110e3e98fcfe8e..cad3a3d77a7972724728a072ca6df49a2e4d03da 100644 (file)
@@ -57,7 +57,7 @@ TESTS = gettext-1 gettext-2 \
        msgfmt-tcl-1 msgfmt-tcl-2 \
        msgfmt-qt-1 msgfmt-qt-2 \
        msgfmt-desktop-1 msgfmt-desktop-2 msgfmt-desktop-3 \
-       msgfmt-xml-1 msgfmt-xml-2 msgfmt-xml-3 \
+       msgfmt-xml-1 msgfmt-xml-2 msgfmt-xml-3 msgfmt-xml-4 \
        msggrep-1 msggrep-2 msggrep-3 msggrep-4 msggrep-5 msggrep-6 msggrep-7 \
        msggrep-8 msggrep-9 msggrep-10 msggrep-11 \
        msginit-1 msginit-2 msginit-3 msginit-4 \
diff --git a/gettext-tools/tests/msgfmt-xml-4 b/gettext-tools/tests/msgfmt-xml-4
new file mode 100755 (executable)
index 0000000..b83a893
--- /dev/null
@@ -0,0 +1,144 @@
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test msgfmt --xml: --replace-text option
+
+cat <<\EOF > mf.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" 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>
+  <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 gt:escape="yes">
+      Did you know that the copyright sign (&#xa9;, U+00A9) can be written in HTML
+      as &amp;#xa9;,
+      as &amp;#169;,
+      or as &amp;copy;?
+    </p>
+    <p>Written by &author1;, &author2;, and &author3;.</p>
+    <p gt:escape="no">Escape gallery: operator x&amp;y, standard XML entities &amp; &quot; &apos; &amp; &lt; &gt;, character reference &#xa9;, escaped character reference &amp;#xa9;, entity references &copy; &author1;</p>
+    <p gt:escape="yes">Escape gallery: operator x&amp;y, standard XML entities &amp; &quot; &apos; &amp; &lt; &gt;, character reference &#xa9;, escaped character reference &amp;#xa9;, entity references &copy; &author1;</p>
+  </description>
+  <url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
+  <updatecontact>dueno_at_src.gnome.org</updatecontact>
+</component>
+EOF
+
+cat <<\EOF > fr.po
+# 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"
+"POT-Creation-Date: 2014-03-17 07:36+0900\n"
+"PO-Revision-Date: 2014-03-17 08:40+0900\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"
+
+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 ""
+"Caractères est un utilitaire pour chercher et insérer des caractères "
+"inhabituels. Il vous permet de trouver rapidement le caractère que vous "
+"cherchez par le biais de mots-clés."
+
+#, fuzzy
+msgid ""
+"You can also browse characters by categories, such as Punctuation, Pictures, "
+"etc."
+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 "
+"&#xa9;, as &#169;, or as &copy;?"
+msgstr ""
+"Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML "
+"comme &#xa9;, comme &#169; ou comme &copy; ?"
+
+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 &#xa9;, entity references &copy; "
+"&author1;"
+msgstr ""
+"Exposition d'échappements: operateur x&y, entités XML standard & \" ' & < >, "
+"caractère ©, caractère échappé &#xa9;, entités &copy; &author1;"
+EOF
+
+cat <<\EOF > mf.appdata.xml.ok
+<?xml version="1.0" encoding="UTF-8"?>
+<!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>
+  <licence>CC0</licence>
+  <description>
+    <p xml:lang="fr">Caractères est un utilitaire pour chercher et insérer des caractères inhabituels. Il vous permet de trouver rapidement le caractère que vous cherchez par le biais de mots-clés.</p>
+    <p>
+      You can also browse characters by categories, such as
+      Punctuation, Pictures, etc.
+    </p>
+    <p gt:escape="yes" xml:lang="fr">Saviez-vous que le signe de copyright (©, U+00A9) peut être écrit en HTML comme &amp;#xa9;, comme &amp;#169; ou comme &amp;copy; ?</p>
+    <p xml:lang="fr">Écrit par &author1;, &author2;, et &author3;.</p>
+    <p gt:escape="no" xml:lang="fr">Exposition d'échappements: operateur x&y, entités XML standard & " ' & &lt; &gt;, caractère ©, caractère échappé &amp;#xa9;, entités &copy; &author1;</p>
+    <p gt:escape="yes" xml:lang="fr">Exposition d'échappements: operateur x&amp;y, entités XML standard &amp; " ' &amp; &lt; &gt;, caractère ©, caractère échappé &amp;#xa9;, entités &amp;copy; &amp;author1;</p>
+  </description>
+  <url type="homepage">https://wiki.gnome.org/Design/Apps/CharacterMap</url>
+  <updatecontact>dueno_at_src.gnome.org</updatecontact>
+</component>
+EOF
+
+# Sanity checks for contradicting options.
+
+${MSGFMT} --replace-text fr.po \
+          >/dev/null 2>/dev/null \
+  && Exit 1
+
+${MSGFMT} --xml --template=mf.appdata.xml --replace-text -d po -o mf.appdata.xml.out \
+          >/dev/null 2>/dev/null \
+  && Exit 1
+
+# Proceed to the XML file generation.
+
+${MSGFMT} --xml --template=mf.appdata.xml --replace-text -l fr fr.po -o mf.appdata.xml.out \
+  || Exit 1
+
+: ${DIFF=diff}
+${DIFF} mf.appdata.xml.ok mf.appdata.xml.out
+test $? = 0 || Exit 1