summaryrefslogtreecommitdiff
path: root/gettext-tools/src
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2015-10-05 17:18:41 +0900
committerDaiki Ueno <ueno@gnu.org>2015-10-15 13:09:43 +0900
commit2d1143a6ac1d52a6d946eefe9ae31cfecc9e371e (patch)
tree96946aecb67f956fa50f2b1cd1b986089fd4eb09 /gettext-tools/src
parent8701164b866f926568dad22eb221fea8438fb9f1 (diff)
downloadgettext-wip/ueno/its2.tar.gz
msgfmt: Support XML file mergingwip/ueno/its2
* gettext-tools/src/Makefile.am (noinst_HEADERS): Add write-xml.h. (msgfmt_SOURCES): Add write-xml.c. * gettext-tools/src/its.c (its_merge_context_merge_node): New function. (its_merge_context_merge): New function. (its_merge_context_alloc): New function. (its_merge_context_write): New function. (its_merge_context_free): New function. * gettext-tools/src/its.h (its_merge_context_ty): New type. * gettext-tools/src/msgfmt.c: Include "its.h", "locating-rule.h", and "write-xml.h". (SIZEOF): New macro. (xml_mode, xml_locale_name, xml_template_name, xml_base_directory, xml_language, xml_its_rules): New variable. (long_options): Add --language and --xml. (main): Handle new options. (usage): Document new options. (msgfmt_xml_bulk): New function. * gettext-tools/src/write-xml.c: New file. * gettext-tools/src/write-xml.h: New file. * gettext-tools/doc/gettext.texi: Mention XML file merging use-case. * gettext-tools/doc/msgfmt.texi: Mention --xml option. * gettext-tools/tests/msgfmt-xml-1: New file. * gettext-tools/tests/msgfmt-xml-2: New file. * gettext-tools/tests/Makefile.am (TESTS): Add new tests.
Diffstat (limited to 'gettext-tools/src')
-rw-r--r--gettext-tools/src/Makefile.am4
-rw-r--r--gettext-tools/src/its.c131
-rw-r--r--gettext-tools/src/its.h12
-rw-r--r--gettext-tools/src/msgfmt.c208
-rw-r--r--gettext-tools/src/write-xml.c107
-rw-r--r--gettext-tools/src/write-xml.h52
6 files changed, 507 insertions, 7 deletions
diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am
index 20a5877cb..f380e431f 100644
--- a/gettext-tools/src/Makefile.am
+++ b/gettext-tools/src/Makefile.am
@@ -51,6 +51,7 @@ read-resources.h write-resources.h \
read-tcl.h write-tcl.h \
write-qt.h \
read-desktop.h write-desktop.h \
+write-xml.h \
po-time.h plural-table.h lang-table.h format.h filters.h \
xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \
x-scheme.h x-smalltalk.h x-java.h x-properties.h x-csharp.h x-awk.h x-ycp.h \
@@ -164,7 +165,8 @@ msgcmp_SOURCES += msgl-fsearch.c
msgfmt_SOURCES = msgfmt.c
msgfmt_SOURCES += \
write-mo.c write-java.c write-csharp.c write-resources.c write-tcl.c \
- write-qt.c write-desktop.c ../../gettext-runtime/intl/hash-string.c
+ write-qt.c write-desktop.c write-xml.c \
+ ../../gettext-runtime/intl/hash-string.c
if !WOE32DLL
msgmerge_SOURCES = msgmerge.c
else
diff --git a/gettext-tools/src/its.c b/gettext-tools/src/its.c
index 585e9845a..6843aac7f 100644
--- a/gettext-tools/src/its.c
+++ b/gettext-tools/src/its.c
@@ -1810,3 +1810,134 @@ its_rule_list_extract (its_rule_list_ty *rules,
free (nodes.items);
xmlFreeDoc (doc);
}
+
+struct its_merge_context_ty
+{
+ its_rule_list_ty *rules;
+ xmlDoc *doc;
+ struct its_node_list_ty nodes;
+};
+
+static void
+its_merge_context_merge_node (struct its_merge_context_ty *context,
+ xmlNode *node,
+ const char *language,
+ message_list_ty *mlp)
+{
+ if (node->type == XML_ELEMENT_NODE)
+ {
+ struct its_value_list_ty *values;
+ const char *value;
+ char *msgid = NULL, *msgctxt = NULL;
+ enum its_whitespace_type_ty whitespace;
+ bool no_escape;
+
+ values = its_rule_list_eval (context->rules, node);
+
+ value = its_value_list_get_value (values, "space");
+ if (value && strcmp (value, "preserve") == 0)
+ whitespace = ITS_WHITESPACE_PRESERVE;
+ else if (value && strcmp (value, "trim") == 0)
+ whitespace = ITS_WHITESPACE_TRIM;
+ else
+ whitespace = ITS_WHITESPACE_NORMALIZE;
+
+ value = its_value_list_get_value (values, "escape");
+ no_escape = value != NULL && strcmp (value, "no") == 0;
+
+ value = its_value_list_get_value (values, "contextPointer");
+ if (value)
+ msgctxt = _its_get_content (context->rules, node, value,
+ ITS_WHITESPACE_PRESERVE, no_escape);
+
+ value = its_value_list_get_value (values, "textPointer");
+ if (value)
+ msgid = _its_get_content (context->rules, node, value,
+ ITS_WHITESPACE_PRESERVE, no_escape);
+ its_value_list_destroy (values);
+ free (values);
+
+ if (msgid == NULL)
+ msgid = _its_collect_text_content (node, whitespace, no_escape);
+ if (*msgid != '\0')
+ {
+ message_ty *mp;
+
+ mp = message_list_search (mlp, msgctxt, msgid);
+ if (mp && *mp->msgstr != '\0')
+ {
+ xmlNode *translated;
+
+ translated = xmlNewNode (node->ns, node->name);
+ xmlSetProp (translated, BAD_CAST "xml:lang", BAD_CAST language);
+
+ xmlNodeAddContent (translated, BAD_CAST mp->msgstr);
+ xmlAddNextSibling (node, translated);
+ }
+ }
+ free (msgctxt);
+ free (msgid);
+ }
+}
+
+void
+its_merge_context_merge (its_merge_context_ty *context,
+ const char *language,
+ message_list_ty *mlp)
+{
+ size_t i;
+
+ for (i = 0; i < context->nodes.nitems; i++)
+ its_merge_context_merge_node (context, context->nodes.items[i],
+ language,
+ mlp);
+}
+
+struct its_merge_context_ty *
+its_merge_context_alloc (its_rule_list_ty *rules,
+ const char *filename)
+{
+ xmlDoc *doc;
+ struct its_merge_context_ty *result;
+
+ doc = xmlReadFile (filename, NULL,
+ XML_PARSE_NONET
+ | XML_PARSE_NOWARNING
+ | XML_PARSE_NOBLANKS
+ | XML_PARSE_NOERROR);
+ if (doc == NULL)
+ {
+ xmlError *err = xmlGetLastError ();
+ error (0, 0, _("cannot read %s: %s"), filename, err->message);
+ return NULL;
+ }
+
+ its_rule_list_apply (rules, doc);
+
+ result = XMALLOC (struct its_merge_context_ty);
+ result->rules = rules;
+ result->doc = doc;
+
+ /* Collect translatable nodes. */
+ memset (&result->nodes, 0, sizeof (struct its_node_list_ty));
+ its_rule_list_extract_nodes (result->rules,
+ &result->nodes,
+ xmlDocGetRootElement (result->doc));
+
+ return result;
+}
+
+void
+its_merge_context_write (struct its_merge_context_ty *context,
+ FILE *fp)
+{
+ xmlDocFormatDump (fp, context->doc, 1);
+}
+
+void
+its_merge_context_free (struct its_merge_context_ty *context)
+{
+ xmlFreeDoc (context->doc);
+ free (context->nodes.items);
+ free (context);
+}
diff --git a/gettext-tools/src/its.h b/gettext-tools/src/its.h
index d26bbcc16..8d597f51e 100644
--- a/gettext-tools/src/its.h
+++ b/gettext-tools/src/its.h
@@ -66,6 +66,18 @@ extern void its_rule_list_extract (its_rule_list_ty *rules,
msgdomain_list_ty *mdlp,
its_extract_callback_ty callback);
+typedef struct its_merge_context_ty its_merge_context_ty;
+
+extern its_merge_context_ty *
+ its_merge_context_alloc (its_rule_list_ty *rules, const char *filename);
+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);
+
+extern void its_merge_context_write (its_merge_context_ty *context,
+ FILE *fp);
+
#ifdef __cplusplus
}
#endif
diff --git a/gettext-tools/src/msgfmt.c b/gettext-tools/src/msgfmt.c
index 3dfafdc6f..e74ab0613 100644
--- a/gettext-tools/src/msgfmt.c
+++ b/gettext-tools/src/msgfmt.c
@@ -50,6 +50,7 @@
#include "write-tcl.h"
#include "write-qt.h"
#include "write-desktop.h"
+#include "write-xml.h"
#include "propername.h"
#include "message.h"
#include "open-catalog.h"
@@ -62,10 +63,14 @@
#include "msgl-check.h"
#include "msgl-iconv.h"
#include "concat-filename.h"
+#include "its.h"
+#include "locating-rule.h"
#include "gettext.h"
#define _(str) gettext (str)
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
/* Contains exit status for case in which no premature exit occurs. */
static int exit_status;
@@ -111,6 +116,14 @@ static const char *desktop_base_directory;
static hash_table desktop_keywords;
static bool desktop_default_keywords = true;
+/* XML mode output file specification. */
+static bool xml_mode;
+static const char *xml_locale_name;
+static const char *xml_template_name;
+static const char *xml_base_directory;
+static const char *xml_language;
+static its_rule_list_ty *xml_its_rules;
+
/* We may have more than one input file. Domains with same names in
different files have to merged. So we need a list of tables for
each output file. */
@@ -181,6 +194,7 @@ static const struct option long_options[] =
{ "java", no_argument, NULL, 'j' },
{ "java2", no_argument, NULL, CHAR_MAX + 5 },
{ "keyword", required_argument, NULL, 'k' },
+ { "language", required_argument, NULL, 'L' },
{ "locale", required_argument, NULL, 'l' },
{ "no-hash", no_argument, NULL, CHAR_MAX + 6 },
{ "output-file", required_argument, NULL, 'o' },
@@ -197,6 +211,7 @@ static const struct option long_options[] =
{ "use-untranslated", no_argument, NULL, CHAR_MAX + 12 },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
+ { "xml", no_argument, NULL, 'x' },
{ NULL, 0, NULL, 0 }
};
@@ -216,6 +231,10 @@ static int msgfmt_desktop_bulk (const char *directory,
const char *template_file_name,
hash_table *keywords,
const char *file_name);
+static int msgfmt_xml_bulk (const char *directory,
+ const char *template_file_name,
+ its_rule_list_ty *its_rules,
+ const char *file_name);
int
@@ -252,8 +271,8 @@ main (int argc, char *argv[])
/* Ensure that write errors on stdout are detected. */
atexit (close_stdout);
- while ((opt = getopt_long (argc, argv, "a:cCd:D:fhjl:o:Pr:vV", long_options,
- NULL))
+ while ((opt = getopt_long (argc, argv, "a:cCd:D:fhjl:L:o:Pr:vVx",
+ long_options, NULL))
!= EOF)
switch (opt)
{
@@ -281,6 +300,7 @@ main (int argc, char *argv[])
csharp_base_directory = optarg;
tcl_base_directory = optarg;
desktop_base_directory = optarg;
+ xml_base_directory = optarg;
break;
case 'D':
dir_list_append (optarg);
@@ -313,6 +333,10 @@ main (int argc, char *argv[])
csharp_locale_name = optarg;
tcl_locale_name = optarg;
desktop_locale_name = optarg;
+ xml_locale_name = optarg;
+ break;
+ case 'L':
+ xml_language = optarg;
break;
case 'o':
output_file_name = optarg;
@@ -333,6 +357,9 @@ main (int argc, char *argv[])
case 'V':
do_version = true;
break;
+ case 'x':
+ xml_mode = true;
+ break;
case CHAR_MAX + 1: /* --check-accelerators */
check_accelerators = true;
if (optarg != NULL)
@@ -402,6 +429,7 @@ main (int argc, char *argv[])
break;
case CHAR_MAX + 16: /* --template=TEMPLATE */
desktop_template_name = optarg;
+ xml_template_name = optarg;
break;
default:
usage (EXIT_FAILURE);
@@ -428,12 +456,16 @@ There is NO WARRANTY, to the extent permitted by law.\n\
usage (EXIT_SUCCESS);
/* Test whether we have a .po file name as argument. */
- if (optind >= argc && !(desktop_mode && desktop_base_directory))
+ if (optind >= argc
+ && !(desktop_mode && desktop_base_directory)
+ && !(xml_mode && xml_base_directory))
{
error (EXIT_SUCCESS, 0, _("no input file given"));
usage (EXIT_FAILURE);
}
- if (optind < argc && desktop_mode && desktop_base_directory)
+ if (optind < argc
+ && ((desktop_mode && desktop_base_directory)
+ || (xml_mode && xml_base_directory)))
{
error (EXIT_SUCCESS, 0,
_("no input file should be given if %s and %s are specified"),
@@ -449,10 +481,11 @@ There is NO WARRANTY, to the extent permitted by law.\n\
| (csharp_resources_mode ? 4 : 0)
| (tcl_mode ? 8 : 0)
| (qt_mode ? 16 : 0)
- | (desktop_mode ? 32 : 0);
+ | (desktop_mode ? 32 : 0)
+ | (xml_mode ? 64 : 0);
static const char *mode_options[] =
{ "--java", "--csharp", "--csharp-resources", "--tcl", "--qt",
- "--desktop" };
+ "--desktop", "--xml" };
/* More than one bit set? */
if (modes & (modes - 1))
{
@@ -558,6 +591,34 @@ There is NO WARRANTY, to the extent permitted by law.\n\
usage (EXIT_FAILURE);
}
}
+ else if (xml_mode)
+ {
+ if (xml_template_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"--template template\" specification"),
+ "--xml");
+ usage (EXIT_FAILURE);
+ }
+ if (output_file_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-o file\" specification"),
+ "--xml");
+ usage (EXIT_FAILURE);
+ }
+ if (xml_base_directory != NULL && xml_locale_name != NULL)
+ error (EXIT_FAILURE, 0,
+ _("%s and %s are mutually exclusive in %s"),
+ "-d", "-l", "--xml");
+ if (xml_base_directory == NULL && xml_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--xml");
+ usage (EXIT_FAILURE);
+ }
+ }
else
{
if (java_resource_name != NULL)
@@ -600,6 +661,80 @@ There is NO WARRANTY, to the extent permitted by law.\n\
exit (exit_status);
}
+ if (xml_mode)
+ {
+ const char *gettextdatadir;
+ char *versioned_gettextdatadir;
+ char *its_dirs[2] = { NULL, NULL };
+ locating_rule_list_ty *its_locating_rules;
+ const char *its_basename;
+ size_t i;
+
+ /* Make it possible to override the locator file location. This
+ is necessary for running the testsuite before "make
+ install". */
+ gettextdatadir = getenv ("GETTEXTDATADIR");
+ if (gettextdatadir == NULL || gettextdatadir[0] == '\0')
+ gettextdatadir = relocate (GETTEXTDATADIR);
+
+ its_dirs[0] = xconcatenated_filename (gettextdatadir, "its", NULL);
+
+ versioned_gettextdatadir =
+ xasprintf ("%s%s", relocate (GETTEXTDATADIR), PACKAGE_SUFFIX);
+ its_dirs[1] = xconcatenated_filename (versioned_gettextdatadir, "its",
+ NULL);
+ free (versioned_gettextdatadir);
+
+ its_locating_rules = locating_rule_list_alloc ();
+ for (i = 0; i < SIZEOF (its_dirs); i++)
+ locating_rule_list_add_from_directory (its_locating_rules, its_dirs[i]);
+
+ its_basename = locating_rule_list_locate (its_locating_rules,
+ xml_template_name,
+ xml_language);
+
+ if (its_basename != NULL)
+ {
+ size_t j;
+
+ xml_its_rules = its_rule_list_alloc ();
+ for (j = 0; j < SIZEOF (its_dirs); j++)
+ {
+ char *its_filename =
+ xconcatenated_filename (its_dirs[j], its_basename, NULL);
+ struct stat statbuf;
+ bool ok = false;
+
+ if (stat (its_filename, &statbuf) == 0)
+ ok = its_rule_list_add_from_file (xml_its_rules, its_filename);
+ free (its_filename);
+ if (ok)
+ break;
+ }
+ if (j == SIZEOF (its_dirs))
+ {
+ its_rule_list_free (xml_its_rules);
+ xml_its_rules = NULL;
+ }
+ }
+ locating_rule_list_free (its_locating_rules);
+
+ if (xml_its_rules == NULL)
+ error (EXIT_FAILURE, 0, _("cannot locate ITS rules for %s"),
+ xml_template_name);
+ }
+
+ /* Bulk processing mode for XML files.
+ Process all .po files in desktop_base_directory. */
+ if (xml_mode && xml_base_directory)
+ {
+ exit_status = msgfmt_xml_bulk (xml_base_directory,
+ xml_template_name,
+ xml_its_rules,
+ output_file_name);
+ exit (exit_status);
+ }
+
/* The -o option determines the name of the domain and therefore
the output file. */
if (output_file_name != NULL)
@@ -705,6 +840,15 @@ There is NO WARRANTY, to the extent permitted by law.\n\
if (desktop_keywords.table != NULL)
hash_destroy (&desktop_keywords);
}
+ else if (xml_mode)
+ {
+ if (msgdomain_write_xml (domain->mlp, canon_encoding,
+ xml_locale_name,
+ xml_template_name,
+ xml_its_rules,
+ domain->file_name))
+ exit_status = EXIT_FAILURE;
+ }
else
{
if (msgdomain_write_mo (domain->mlp, domain->domain_name,
@@ -810,6 +954,8 @@ Operation mode:\n"));
--qt Qt mode: generate a Qt .qm file\n"));
printf (_("\
--desktop Desktop Entry mode: generate a .desktop file\n"));
+ printf (_("\
+ --xml XML mode: generate XML file\n"));
printf ("\n");
printf (_("\
Output file location:\n"));
@@ -876,6 +1022,22 @@ 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");
printf (_("\
+XML mode options:\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ -L, --language=NAME recognise the specified XML language\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+ --template=TEMPLATE a .desktop file used as a template\n"));
+ printf (_("\
+ -d DIRECTORY base directory of .po files\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");
+ printf (_("\
Input file syntax:\n"));
printf (_("\
-P, --properties-input input files are in Java .properties syntax\n"));
@@ -1519,3 +1681,37 @@ msgfmt_desktop_bulk (const char *directory,
return status;
}
+
+/* Helper function to support 'bulk' operation mode of --xml.
+ This reads all .po files in DIRECTORY and merges them into an
+ XML file FILE_NAME. Currently it does not support some
+ options available in 'iterative' mode, such as --statistics. */
+static int
+msgfmt_xml_bulk (const char *directory,
+ const char *template_file_name,
+ its_rule_list_ty *its_rules,
+ const char *file_name)
+{
+ msgfmt_operand_list_ty operands;
+ int nerrors, status;
+
+ msgfmt_operand_list_init (&operands);
+
+ /* Read all .po files. */
+ nerrors = msgfmt_operand_list_add_from_directory (&operands, directory);
+ if (nerrors > 0)
+ {
+ msgfmt_operand_list_destroy (&operands);
+ return 1;
+ }
+
+ /* Write the messages into .xml file. */
+ status = msgdomain_write_xml_bulk (&operands,
+ template_file_name,
+ its_rules,
+ file_name);
+
+ msgfmt_operand_list_destroy (&operands);
+
+ return status;
+}
diff --git a/gettext-tools/src/write-xml.c b/gettext-tools/src/write-xml.c
new file mode 100644
index 000000000..38e319580
--- /dev/null
+++ b/gettext-tools/src/write-xml.c
@@ -0,0 +1,107 @@
+/* Writing XML files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014-2015
+ 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
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-xml.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "error.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "fwriteerror.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+int
+msgdomain_write_xml_bulk (msgfmt_operand_list_ty *operands,
+ const char *template_file_name,
+ its_rule_list_ty *its_rules,
+ const char *file_name)
+{
+ its_merge_context_ty *context;
+ size_t i;
+ FILE *fp;
+
+ if (strcmp (file_name, "-") == 0)
+ fp = stdout;
+ else
+ {
+ fp = fopen (file_name, "wb");
+ if (fp == NULL)
+ {
+ error (0, errno, _("cannot create output file \"%s\""),
+ file_name);
+ return 1;
+ }
+ }
+
+ context = its_merge_context_alloc (its_rules, template_file_name);
+ for (i = 0; i < operands->nitems; i++)
+ its_merge_context_merge (context,
+ operands->items[i].language,
+ operands->items[i].mlp);
+ its_merge_context_write (context, fp);
+ its_merge_context_free (context);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (fp))
+ {
+ error (0, errno, _("error while writing \"%s\" file"),
+ file_name);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+msgdomain_write_xml (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *locale_name,
+ const char *template_file_name,
+ its_rule_list_ty *its_rules,
+ const char *file_name)
+{
+ msgfmt_operand_ty operand;
+ msgfmt_operand_list_ty operands;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Create a single-element operands and run the bulk operation on it. */
+ operand.language = (char *) locale_name;
+ operand.mlp = mlp;
+ operands.nitems = 1;
+ operands.items = &operand;
+
+ return msgdomain_write_xml_bulk (&operands,
+ template_file_name,
+ its_rules,
+ file_name);
+}
diff --git a/gettext-tools/src/write-xml.h b/gettext-tools/src/write-xml.h
new file mode 100644
index 000000000..e62778913
--- /dev/null
+++ b/gettext-tools/src/write-xml.h
@@ -0,0 +1,52 @@
+/* Reading XML files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014-2015
+ 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
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_XML_H
+#define _WRITE_XML_H
+
+#include "its.h"
+#include "msgfmt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Write an XML file. mlp is a list containing the messages
+ to be output. locale_name is the locale name. template_file_name
+ is the template file. file_name is the output file. Return 0 if
+ ok, nonzero on error. */
+extern int
+ msgdomain_write_xml (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *locale_name,
+ const char *template_file_name,
+ its_rule_list_ty *its_rules,
+ 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,
+ const char *file_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _WRITE_XML_H */