From: Daiki Ueno Date: Sun, 16 Mar 2014 13:06:23 +0000 (+0900) Subject: msgfmt: Add support for Desktop Entry files X-Git-Tag: v0.19~123 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b89555d83d334e48ce4cb2b4304e4f3c0982926e;p=thirdparty%2Fgettext.git msgfmt: Add support for Desktop Entry files --- diff --git a/gettext-tools/doc/msgfmt.texi b/gettext-tools/doc/msgfmt.texi index df34c275a..402bc1a76 100644 --- a/gettext-tools/doc/msgfmt.texi +++ b/gettext-tools/doc/msgfmt.texi @@ -60,6 +60,11 @@ Tcl mode: generate a tcl/msgcat @file{.msg} file. @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 @@ -162,6 +167,59 @@ 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 + +@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 diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index d43afdcf1..25947c354 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -48,7 +48,7 @@ read-csharp.h write-csharp.h \ 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 \ @@ -161,7 +161,7 @@ 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 ../../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 diff --git a/gettext-tools/src/msgfmt.c b/gettext-tools/src/msgfmt.c index e41434645..3fa17aa67 100644 --- a/gettext-tools/src/msgfmt.c +++ b/gettext-tools/src/msgfmt.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include "closeout.h" #include "str-list.h" @@ -45,6 +48,7 @@ #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" @@ -52,8 +56,11 @@ #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) @@ -95,6 +102,14 @@ static const char *tcl_base_directory; /* 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. */ @@ -158,11 +173,13 @@ static const struct option long_options[] = { "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' }, @@ -174,6 +191,7 @@ static const struct option long_options[] = { "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' }, @@ -193,6 +211,11 @@ static struct msg_domain *new_domain (const char *name, const char *file_name); 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 @@ -257,6 +280,7 @@ main (int argc, char *argv[]) java_class_directory = optarg; csharp_base_directory = optarg; tcl_base_directory = optarg; + desktop_base_directory = optarg; break; case 'D': dir_list_append (optarg); @@ -270,10 +294,25 @@ main (int argc, char *argv[]) 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; @@ -358,6 +397,12 @@ main (int argc, char *argv[]) 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; @@ -383,11 +428,18 @@ 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) + 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. */ { @@ -396,9 +448,11 @@ There is NO WARRANTY, to the extent permitted by law.\n\ | (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)) { @@ -476,6 +530,34 @@ There is NO WARRANTY, to the extent permitted by law.\n\ 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) @@ -498,6 +580,26 @@ There is NO WARRANTY, to the extent permitted by law.\n\ } } + 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) @@ -591,6 +693,18 @@ There is NO WARRANTY, to the extent permitted by law.\n\ 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, @@ -694,6 +808,8 @@ Operation mode:\n")); --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")); @@ -743,6 +859,23 @@ The -l and -d options are mandatory. The .msg file is written in the\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")); @@ -930,7 +1063,7 @@ msgfmt_set_domain (default_catalog_reader_ty *this, char *name) /* 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; @@ -1134,3 +1267,189 @@ read_catalog_file_msgfmt (char *filename, catalog_input_format_ty input_syntax) 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; +} diff --git a/gettext-tools/src/write-desktop.c b/gettext-tools/src/write-desktop.c new file mode 100644 index 000000000..cb953d0e8 --- /dev/null +++ b/gettext-tools/src/write-desktop.c @@ -0,0 +1,225 @@ +/* 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 . + + 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "write-desktop.h" + +#include +#include +#include +#include +#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; +} diff --git a/gettext-tools/src/write-desktop.h b/gettext-tools/src/write-desktop.h new file mode 100644 index 000000000..028b441f9 --- /dev/null +++ b/gettext-tools/src/write-desktop.h @@ -0,0 +1,51 @@ +/* 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 . + + 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 . */ + +#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 */ diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 6d71a230a..5d3825519 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -48,6 +48,7 @@ TESTS = gettext-1 gettext-2 gettext-3 gettext-4 gettext-5 gettext-6 gettext-7 \ 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 \ diff --git a/gettext-tools/tests/msgfmt-desktop-1 b/gettext-tools/tests/msgfmt-desktop-1 new file mode 100755 index 000000000..18ca696ed --- /dev/null +++ b/gettext-tools/tests/msgfmt-desktop-1 @@ -0,0 +1,85 @@ +#! /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 , 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 \n" +"Language-Team: LANGUAGE \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 diff --git a/gettext-tools/tests/msgfmt-desktop-2 b/gettext-tools/tests/msgfmt-desktop-2 new file mode 100755 index 000000000..86a4ab4a7 --- /dev/null +++ b/gettext-tools/tests/msgfmt-desktop-2 @@ -0,0 +1,149 @@ +#! /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 , 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 \n" +"Language-Team: LANGUAGE \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 , 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 \n" +"Language-Team: LANGUAGE \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