@cindex Qt mode, and @code{msgfmt} program
Qt mode: generate a Qt @file{.qm} file.
+@item --desktop
+@opindex --desktop@r{, @code{msgfmt} option}
+@cindex Desktop Entry mode, and @code{msgfmt} program
+Desktop Entry mode: generate a @file{.desktop} file.
+
@end table
@subsection Output file location
The @samp{-l} and @samp{-d} options are mandatory. The @file{.msg} file is
written in the specified directory.
+@subsection Desktop Entry mode operations
+
+@table @samp
+@item --template=@var{template}
+@opindex --template@r{, @code{msgfmt} option}
+Specify a .desktop file used as a template.
+
+@item -k[@var{keywordspec}]
+@itemx --keyword[=@var{keywordspec}]
+@opindex -k@r{, @code{msgfmt} option}
+@opindex --keyword@r{, @code{msgfmt} option}
+Specify @var{keywordspec} as an additional keyword to be looked for.
+Without a @var{keywordspec}, the option means to not use default keywords.
+
+@item -l @var{locale}
+@itemx --locale=@var{locale}
+@opindex -l@r{, @code{msgfmt} option}
+@opindex --locale@r{, @code{msgfmt} option}
+Specify the locale name, either a language specification of the form @var{ll}
+or a combined language and country specification of the form @var{ll_CC}.
+
+@item -d @var{directory}
+@opindex -d@r{, @code{msgfmt} option}
+Specify the base directory of @file{.msg} message catalogs.
+
+@end table
+
+To generate a @samp{.desktop} file for a single locale, you can use it
+as follows.
+
+@example
+msgfmt --desktop --template=@var{template} --locale=@var{locale} \
+ -o @var{file} @var{filename}.po @dots{}
+@end example
+
+On the other hand, when using msgfmt from a Makefile, it is cumbersome
+to loop over all locales under a particular directory. msgfmt
+provides a special operation mode for this use-case. To generate a
+@samp{.desktop} file from multiple @samp{.po} files under a directory,
+specify the directory with the @samp{-d} option.
+
+@example
+msgfmt --desktop --template=@var{template} -d @var{directory} -o @var{file}
+@end example
+
+msgfmt first reads the @samp{LINGUAS} file under @var{directory}, and
+then processes all @samp{.po} files listed there. You can also limit
+the locales to a subset, through the @samp{LINGUAS} environment
+variable.
+
+For either operation modes, the @samp{-o} and @samp{--template}
+options are mandatory.
+
@subsection Input file syntax
@table @samp
read-resources.h write-resources.h \
read-tcl.h write-tcl.h \
write-qt.h \
-read-desktop.h \
+read-desktop.h write-desktop.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 \
msgfmt_SOURCES = msgfmt.c
msgfmt_SOURCES += \
write-mo.c write-java.c write-csharp.c write-resources.c write-tcl.c \
- write-qt.c ../../gettext-runtime/intl/hash-string.c
+ write-qt.c write-desktop.c ../../gettext-runtime/intl/hash-string.c
if !WOE32DLL
msgmerge_SOURCES = msgmerge.c
else
#include <stdlib.h>
#include <string.h>
#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
#include "closeout.h"
#include "str-list.h"
#include "write-resources.h"
#include "write-tcl.h"
#include "write-qt.h"
+#include "write-desktop.h"
#include "propername.h"
#include "message.h"
#include "open-catalog.h"
#include "read-po.h"
#include "read-properties.h"
#include "read-stringtable.h"
+#include "read-desktop.h"
#include "po-charset.h"
#include "msgl-check.h"
+#include "msgl-iconv.h"
+#include "concat-filename.h"
#include "gettext.h"
#define _(str) gettext (str)
/* Qt mode output file specification. */
static bool qt_mode;
+/* Desktop Entry mode output file specification. */
+static bool desktop_mode;
+static const char *desktop_locale_name;
+static const char *desktop_template_name;
+static const char *desktop_base_directory;
+static hash_table desktop_keywords;
+static bool desktop_default_keywords = true;
+
/* 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. */
{ "check-header", no_argument, NULL, CHAR_MAX + 4 },
{ "csharp", no_argument, NULL, CHAR_MAX + 10 },
{ "csharp-resources", no_argument, NULL, CHAR_MAX + 11 },
+ { "desktop", no_argument, NULL, CHAR_MAX + 15 },
{ "directory", required_argument, NULL, 'D' },
{ "endianness", required_argument, NULL, CHAR_MAX + 13 },
{ "help", no_argument, NULL, 'h' },
{ "java", no_argument, NULL, 'j' },
{ "java2", no_argument, NULL, CHAR_MAX + 5 },
+ { "keyword", required_argument, NULL, 'k' },
{ "locale", required_argument, NULL, 'l' },
{ "no-hash", no_argument, NULL, CHAR_MAX + 6 },
{ "output-file", required_argument, NULL, 'o' },
{ "strict", no_argument, NULL, 'S' },
{ "stringtable-input", no_argument, NULL, CHAR_MAX + 8 },
{ "tcl", no_argument, NULL, CHAR_MAX + 7 },
+ { "template", required_argument, NULL, CHAR_MAX + 16 },
{ "use-fuzzy", no_argument, NULL, 'f' },
{ "use-untranslated", no_argument, NULL, CHAR_MAX + 12 },
{ "verbose", no_argument, NULL, 'v' },
static bool is_nonobsolete (const message_ty *mp);
static void read_catalog_file_msgfmt (char *filename,
catalog_input_format_ty input_syntax);
+static string_list_ty *get_languages (const char *directory);
+static int msgfmt_desktop_bulk (const char *directory,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name);
int
java_class_directory = optarg;
csharp_base_directory = optarg;
tcl_base_directory = optarg;
+ desktop_base_directory = optarg;
break;
case 'D':
dir_list_append (optarg);
case 'j':
java_mode = true;
break;
+ case 'k':
+ if (optarg == NULL)
+ desktop_default_keywords = false;
+ else
+ {
+ if (desktop_keywords.table == NULL)
+ {
+ hash_init (&desktop_keywords, 100);
+ desktop_default_keywords = false;
+ }
+
+ desktop_add_keyword (&desktop_keywords, optarg, false);
+ }
+ break;
case 'l':
java_locale_name = optarg;
csharp_locale_name = optarg;
tcl_locale_name = optarg;
+ desktop_locale_name = optarg;
break;
case 'o':
output_file_name = optarg;
case CHAR_MAX + 14: /* --source */
java_output_source = true;
break;
+ case CHAR_MAX + 15: /* --desktop */
+ desktop_mode = true;
+ break;
+ case CHAR_MAX + 16: /* --template=TEMPLATE */
+ desktop_template_name = optarg;
+ break;
default:
usage (EXIT_FAILURE);
break;
usage (EXIT_SUCCESS);
/* Test whether we have a .po file name as argument. */
- if (optind >= argc)
+ if (optind >= argc && !(desktop_mode && desktop_base_directory))
{
error (EXIT_SUCCESS, 0, _("no input file given"));
usage (EXIT_FAILURE);
}
+ if (optind < argc && desktop_mode && desktop_base_directory)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("no input file should be given if %s and %s are specified"),
+ "--desktop", "-d");
+ usage (EXIT_FAILURE);
+ }
/* Check for contradicting options. */
{
| (csharp_mode ? 2 : 0)
| (csharp_resources_mode ? 4 : 0)
| (tcl_mode ? 8 : 0)
- | (qt_mode ? 16 : 0);
+ | (qt_mode ? 16 : 0)
+ | (desktop_mode ? 32 : 0);
static const char *mode_options[] =
- { "--java", "--csharp", "--csharp-resources", "--tcl", "--qt" };
+ { "--java", "--csharp", "--csharp-resources", "--tcl", "--qt",
+ "--desktop" };
/* More than one bit set? */
if (modes & (modes - 1))
{
usage (EXIT_FAILURE);
}
}
+ else if (desktop_mode)
+ {
+ if (desktop_template_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"--template template\" specification"),
+ "--desktop");
+ usage (EXIT_FAILURE);
+ }
+ if (output_file_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-o file\" specification"),
+ "--desktop");
+ usage (EXIT_FAILURE);
+ }
+ if (desktop_base_directory != NULL && desktop_locale_name != NULL)
+ error (EXIT_FAILURE, 0,
+ _("%s and %s are mutually exclusive in %s"),
+ "-d", "-l", "--desktop");
+ if (desktop_base_directory == NULL && desktop_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--desktop");
+ usage (EXIT_FAILURE);
+ }
+ }
else
{
if (java_resource_name != NULL)
}
}
+ if (desktop_mode && desktop_default_keywords)
+ {
+ if (desktop_keywords.table == NULL)
+ hash_init (&desktop_keywords, 100);
+ desktop_add_default_keywords (&desktop_keywords);
+ }
+
+ /* Bulk processing mode for .desktop files.
+ Process all .po files in desktop_base_directory. */
+ if (desktop_mode && desktop_base_directory)
+ {
+ exit_status = msgfmt_desktop_bulk (desktop_base_directory,
+ desktop_template_name,
+ &desktop_keywords,
+ output_file_name);
+ if (desktop_keywords.table != NULL)
+ hash_destroy (&desktop_keywords);
+ exit (exit_status);
+ }
+
/* The -o option determines the name of the domain and therefore
the output file. */
if (output_file_name != NULL)
domain->domain_name, domain->file_name))
exit_status = EXIT_FAILURE;
}
+ else if (desktop_mode)
+ {
+ if (msgdomain_write_desktop (domain->mlp, canon_encoding,
+ desktop_locale_name,
+ desktop_template_name,
+ &desktop_keywords,
+ domain->file_name))
+ exit_status = EXIT_FAILURE;
+
+ if (desktop_keywords.table != NULL)
+ hash_destroy (&desktop_keywords);
+ }
else
{
if (msgdomain_write_mo (domain->mlp, domain->domain_name,
--tcl Tcl mode: generate a tcl/msgcat .msg file\n"));
printf (_("\
--qt Qt mode: generate a Qt .qm file\n"));
+ printf (_("\
+ --desktop Desktop Entry mode: generate a .desktop file\n"));
printf ("\n");
printf (_("\
Output file location:\n"));
specified directory.\n"));
printf ("\n");
printf (_("\
+Desktop Entry mode options:\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\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 (_("\
+ -kWORD, --keyword=WORD look for WORD as an additional keyword\n\
+ -k, --keyword do not to use default keywords\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"));
/* If no output file was given, we change it with each 'domain'
directive. */
if (!java_mode && !csharp_mode && !csharp_resources_mode && !tcl_mode
- && !qt_mode && output_file_name == NULL)
+ && !qt_mode && !desktop_mode && output_file_name == NULL)
{
size_t correct;
if (fp != stdin)
fclose (fp);
}
+
+/* Compute the languages list by reading the "LINGUAS" envvar or the
+ LINGUAS file under DIRECTORY. */
+static string_list_ty *
+get_languages (const char *directory)
+{
+ char *envval;
+ string_list_ty *languages;
+
+ languages = string_list_alloc ();
+ envval = getenv ("LINGUAS");
+ if (envval)
+ {
+ char *saveptr;
+ for (; ; envval = NULL)
+ {
+ char *language = strtok_r (envval, " \t", &saveptr);
+
+ if (!language)
+ break;
+
+ string_list_append_unique (languages, language);
+ free (language);
+ }
+ }
+ else
+ {
+ char *linguas_file_name;
+ struct stat statbuf;
+ FILE *fp;
+ size_t line_len = 0;
+ char *line_buf = NULL;
+
+ linguas_file_name = xconcatenated_filename (directory, "LINGUAS", NULL);
+ if (stat (linguas_file_name, &statbuf) < 0)
+ {
+ error (EXIT_SUCCESS, 0, _("%s does not exist"),
+ linguas_file_name);
+ string_list_free (languages);
+ free (linguas_file_name);
+ return NULL;
+ }
+
+ fp = fopen (linguas_file_name, "r");
+ if (fp == NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s exists but cannot read"),
+ linguas_file_name);
+ string_list_free (languages);
+ free (linguas_file_name);
+ return NULL;
+ }
+
+ while (!feof (fp))
+ {
+ /* Read next line from file. */
+ int len = getline (&line_buf, &line_len, fp);
+
+ /* In case of an error leave loop. */
+ if (len < 0)
+ break;
+
+ /* Remove trailing '\n' and trailing whitespace. */
+ if (len > 0 && line_buf[len - 1] == '\n')
+ line_buf[--len] = '\0';
+ while (len > 0
+ && (line_buf[len - 1] == ' '
+ || line_buf[len - 1] == '\t'
+ || line_buf[len - 1] == '\r'))
+ line_buf[--len] = '\0';
+
+ /* Test if we have to ignore the line. */
+ if (*line_buf == '\0' || *line_buf == '#')
+ continue;
+
+ string_list_append_unique (languages, line_buf);
+ }
+
+ free (line_buf);
+ fclose (fp);
+ free (linguas_file_name);
+ }
+
+ return languages;
+}
+
+/* Helper function to support 'bulk' operation mode of --desktop.
+ This reads all .po files in DIRECTORY and merges them into a
+ .desktop file FILE_NAME. Currently it does not support some
+ options available in 'iterative' mode, such as --statistics. */
+static int
+msgfmt_desktop_bulk (const char *directory,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ string_list_ty *languages = NULL;
+ message_list_ty **messages = NULL;
+ void *saved_dir_list;
+ int retval = 0;
+ size_t i;
+
+ languages = get_languages (directory);
+ if (!languages)
+ {
+ retval = EXIT_FAILURE;
+ goto out;
+ }
+
+ /* Reset the directory search list so only .po files under DIRECTORY
+ will be read. */
+ saved_dir_list = dir_list_save_reset ();
+ dir_list_append (directory);
+
+ /* Read all .po files. */
+ messages = XNMALLOC (languages->nitems, message_list_ty *);
+ for (i = 0; i < languages->nitems; i++)
+ {
+ const char *language = languages->item[i];
+ char *input_file_name;
+ int nerrors;
+
+ current_domain = new_domain (file_name, file_name);
+
+ input_file_name = xconcatenated_filename ("", language, ".po");
+ read_catalog_file_msgfmt (input_file_name, &input_format_po);
+ free (input_file_name);
+
+ /* The domain directive is not supported by --desktop mode.
+ Thus, domain_list should always contain a single domain. */
+ assert (current_domain == domain_list && domain_list->next == NULL);
+ messages[i] = current_domain->mlp;
+ free (current_domain);
+ current_domain = domain_list = NULL;
+
+ /* Remove obsolete messages. They were only needed for duplicate
+ checking. */
+ message_list_remove_if_not (messages[i], is_nonobsolete);
+
+ /* Perform all kinds of checks: plural expressions, format
+ strings, ... */
+ nerrors =
+ check_message_list (messages[i],
+ /* Untranslated and fuzzy messages have already
+ been dealt with during parsing, see below in
+ msgfmt_frob_new_message. */
+ 0, 0,
+ 1, check_format_strings, check_header,
+ check_compatibility,
+ check_accelerators, accelerator_char);
+
+ /* Exit with status 1 on any error. */
+ if (nerrors > 0)
+ {
+ error (0, 0,
+ ngettext ("found %d fatal error", "found %d fatal errors",
+ nerrors),
+ nerrors);
+ retval = EXIT_FAILURE;
+ goto out;
+ }
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (messages[i], NULL, po_charset_utf8, NULL);
+ }
+
+ /* Write the messages into .desktop file. */
+ if (msgdomain_write_desktop_bulk (languages,
+ messages,
+ template_file_name,
+ keywords,
+ file_name))
+ {
+ retval = EXIT_FAILURE;
+ goto out;
+ }
+
+ out:
+ dir_list_restore (saved_dir_list);
+ for (i = 0; i < languages->nitems; i++)
+ message_list_free (messages[i], 0);
+ free (messages);
+ string_list_free (languages);
+
+ return retval;
+}
--- /dev/null
+/* Writing Desktop Entry files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014 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-desktop.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 "read-desktop.h"
+#include "fwriteerror.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+typedef struct msgfmt_desktop_reader_ty msgfmt_desktop_reader_ty;
+struct msgfmt_desktop_reader_ty
+{
+ DESKTOP_READER_TY
+ string_list_ty *languages;
+ message_list_ty **messages;
+ hash_table *keywords;
+ FILE *output_file;
+};
+
+static void
+msgfmt_desktop_handle_group (struct desktop_reader_ty *reader,
+ const char *group)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ fprintf (msgfmt_reader->output_file, "[%s]\n", group);
+}
+
+static void
+msgfmt_desktop_handle_pair (desktop_reader_ty *reader,
+ lex_pos_ty *key_pos,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+ void *keyword_value;
+
+ if (!locale)
+ {
+ /* Write translated pair, if any. */
+ if (hash_find_entry (msgfmt_reader->keywords, key, strlen (key),
+ &keyword_value) == 0)
+ {
+ bool is_list = (bool) keyword_value;
+ char *unescaped = desktop_unescape_string (value, is_list);
+ size_t i;
+
+ for (i = 0; i < msgfmt_reader->languages->nitems; i++)
+ {
+ const char *language = msgfmt_reader->languages->item[i];
+ message_list_ty *mlp = msgfmt_reader->messages[i];
+ message_ty *mp;
+
+ mp = message_list_search (mlp, NULL, unescaped);
+ if (mp && *mp->msgstr != '\0')
+ {
+ char *escaped;
+
+ escaped = desktop_escape_string (mp->msgstr, is_list);
+ fprintf (msgfmt_reader->output_file,
+ "%s[%s]=%s\n",
+ key, language, escaped);
+ free (escaped);
+ }
+ }
+ free (unescaped);
+ }
+
+ /* Write untranslated pair. */
+ fprintf (msgfmt_reader->output_file, "%s=%s\n", key, value);
+ }
+ else
+ /* Preserve already translated pair. */
+ fprintf (msgfmt_reader->output_file, "%s[%s]=%s\n", key, locale, value);
+}
+
+static void
+msgfmt_desktop_handle_comment (struct desktop_reader_ty *reader, const char *s)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ fputc ('#', msgfmt_reader->output_file);
+ fputs (s, msgfmt_reader->output_file);
+ fputc ('\n', msgfmt_reader->output_file);
+}
+
+static void
+msgfmt_desktop_handle_text (struct desktop_reader_ty *reader, const char *s)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ fputs (s, msgfmt_reader->output_file);
+ fputc ('\n', msgfmt_reader->output_file);
+}
+
+desktop_reader_class_ty msgfmt_methods =
+ {
+ sizeof (msgfmt_desktop_reader_ty),
+ NULL,
+ NULL,
+ msgfmt_desktop_handle_group,
+ msgfmt_desktop_handle_pair,
+ msgfmt_desktop_handle_comment,
+ msgfmt_desktop_handle_text
+ };
+
+int
+msgdomain_write_desktop_bulk (string_list_ty *languages,
+ message_list_ty **messages,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ desktop_reader_ty *reader;
+ msgfmt_desktop_reader_ty *msgfmt_reader;
+ FILE *template_file;
+
+ reader = desktop_reader_alloc (&msgfmt_methods);
+ msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ msgfmt_reader->languages = languages;
+ msgfmt_reader->messages = messages;
+ msgfmt_reader->keywords = keywords;
+
+ if (strcmp (file_name, "-") == 0)
+ msgfmt_reader->output_file = stdout;
+ else
+ {
+ msgfmt_reader->output_file = fopen (file_name, "w");
+ if (msgfmt_reader->output_file == NULL)
+ {
+ desktop_reader_free (reader);
+ error (EXIT_SUCCESS,
+ errno, _("error while opening \"%s\" for writing"),
+ file_name);
+ return 1;
+ }
+ }
+
+ template_file = fopen (template_file_name, "r");
+ if (template_file == NULL)
+ {
+ desktop_reader_free (reader);
+ error (EXIT_SUCCESS,
+ errno, _("error while opening \"%s\" for reading"),
+ template_file_name);
+ return 1;
+ }
+
+ desktop_parse (reader, template_file, template_file_name, template_file_name);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (msgfmt_reader->output_file))
+ error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+ file_name);
+
+ desktop_reader_free (reader);
+
+ return 0;
+}
+
+int
+msgdomain_write_desktop (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *locale_name,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ string_list_ty *languages;
+ message_list_ty **messages;
+ int retval;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ languages = string_list_alloc ();
+ string_list_append (languages, locale_name);
+
+ messages = XNMALLOC (1, message_list_ty *);
+ messages[0] = mlp;
+
+ retval = msgdomain_write_desktop_bulk (languages,
+ messages,
+ template_file_name,
+ keywords,
+ file_name);
+
+ string_list_free (languages);
+ free (messages);
+
+ return retval;
+}
--- /dev/null
+/* Reading Desktop Entry files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014 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_DESKTOP_H
+#define _WRITE_DESKTOP_H
+
+#include "message.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Write a Desktop Entry 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_desktop (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *locale_name,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name);
+
+extern int
+ msgdomain_write_desktop_bulk (string_list_ty *languages,
+ message_list_ty **messages,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _WRITE_DESKTOP_H */
msgfmt-15 msgfmt-16 msgfmt-17 \
msgfmt-properties-1 \
msgfmt-qt-1 msgfmt-qt-2 \
+ msgfmt-desktop-1 msgfmt-desktop-2 \
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 \
--- /dev/null
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test iterative mode of msgfmt --desktop.
+
+cat <<\EOF > mf.desktop
+[Desktop Entry]
+Type=Application
+Name =Foo
+Comment[foo]=Already translated comment
+Comment= \sThis is a \nmultiline comment; for testing
+Keywords=Keyword1;Keyword2;Key\;word3;
+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"
+
+#: xg.desktop:4
+msgid "Foo"
+msgstr ""
+"French\n"
+"foo"
+
+#: xg.desktop:5
+msgid ""
+" This is a \n"
+"multiline comment; for testing"
+msgstr ""
+"French \n"
+"comment"
+
+#: xg.desktop:7
+msgid "Keyword1;Keyword2;Key\\;word3;"
+msgstr "one;two;thr\\;ee;"
+EOF
+
+cat <<\EOF > mf.desktop.ok
+[Desktop Entry]
+Type=Application
+Name[fr]=French\nfoo
+Name=Foo
+Comment[foo]=Already translated comment
+Comment[fr]=French \ncomment
+Comment=\sThis is a \nmultiline comment; for testing
+Keywords[fr]=one;two;thr\;ee;
+Keywords=Keyword1;Keyword2;Key\;word3;
+EOF
+
+# Sanity checks for contradicting options.
+
+${MSGFMT} --desktop --template=mf.desktop -l fr fr.po \
+ >/dev/null 2>/dev/null \
+ && exit 1
+
+${MSGFMG} --desktop --template=mf.desktop fr.po -o mf.desktop.out \
+ >/dev/null 2>/dev/null \
+ && exit 1
+
+# Proceed to the .desktop file generation.
+
+${MSGFMT} --desktop --template=mf.desktop -l fr fr.po -o mf.desktop.out \
+ || exit 1
+
+: ${DIFF=diff}
+${DIFF} mf.desktop.ok mf.desktop.out
+result=$?
+
+exit $result
--- /dev/null
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test 'bulk' mode of Desktop Entry support.
+
+cat <<\EOF > mf.desktop
+[Desktop Entry]
+Type=Application
+Name =Foo
+Comment[foo]=Already translated comment
+Comment= \sThis is a \nmultiline comment; for testing
+Keywords=Keyword1;Keyword2;Key\;word3;
+EOF
+
+test -d po || mkdir po
+
+cat <<\EOF > po/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"
+
+#: xg.desktop:4
+msgid "Foo"
+msgstr ""
+"French\n"
+"foo"
+
+#: xg.desktop:5
+msgid ""
+" This is a \n"
+"multiline comment; for testing"
+msgstr ""
+"French \n"
+"comment"
+
+#: xg.desktop:7
+msgid "Keyword1;Keyword2;Key\\;word3;"
+msgstr "one;two;thr\\;ee;"
+EOF
+
+cat <<\EOF > po/de.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"
+
+#: xg.desktop:4
+msgid "Foo"
+msgstr ""
+"German\n"
+"foo"
+
+#: xg.desktop:5
+msgid ""
+" This is a \n"
+"multiline comment; for testing"
+msgstr ""
+"German \n"
+"comment"
+
+#: xg.desktop:7
+msgid "Keyword1;Keyword2;Key\\;word3;"
+msgstr "one;two;thr\\;ee;"
+EOF
+
+cat <<\EOF > mf.desktop.ok
+[Desktop Entry]
+Type=Application
+Name[de]=German\nfoo
+Name[fr]=French\nfoo
+Name=Foo
+Comment[foo]=Already translated comment
+Comment[de]=German \ncomment
+Comment[fr]=French \ncomment
+Comment=\sThis is a \nmultiline comment; for testing
+Keywords[de]=one;two;thr\;ee;
+Keywords[fr]=one;two;thr\;ee;
+Keywords=Keyword1;Keyword2;Key\;word3;
+EOF
+
+# Sanity checks for contradicting options.
+
+${MSGFMT} --desktop --template=mf.desktop -d po -o mf.desktop.out \
+ >/dev/null 2>/dev/null \
+ exit 1
+
+test -d po/LINGUAS || mkdir po/LINGUAS
+
+${MSGFMT} --desktop --template=mf.desktop -d po -o mf.desktop.out \
+ >/dev/null 2>/dev/null \
+ exit 1
+
+rm -fr po/LINGUAS
+
+cat <<\EOF > po/LINGUAS
+de
+fr
+EOF
+
+${MSGFMT} --desktop --template=mf.desktop -d po \
+ >/dev/null 2>/dev/null \
+ && exit 1
+
+${MSGFMG} --desktop --template=mf.desktop -d po -o mf.desktop.out -l fr \
+ >/dev/null 2>/dev/null \
+ && exit 1
+
+${MSGFMG} --desktop --template=mf.desktop -d po -o mf.desktop.out po/fr.po \
+ >/dev/null 2>/dev/null \
+ && exit 1
+
+# Proceed to the .desktop file generation.
+
+${MSGFMT} --desktop --template=mf.desktop -d po -o mf.desktop.out || exit 1
+
+: ${DIFF=diff}
+${DIFF} mf.desktop.ok mf.desktop.out
+result=$?
+
+exit $result