bug-gettext
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-gettext] [PATCHv3 2/2] msgfmt: Add support for Desktop Entry files


From: Daiki Ueno
Subject: [bug-gettext] [PATCHv3 2/2] msgfmt: Add support for Desktop Entry files
Date: Fri, 4 Apr 2014 12:51:27 +0900

---
 gettext-tools/doc/msgfmt.texi        |  58 +++++++
 gettext-tools/src/Makefile.am        |   4 +-
 gettext-tools/src/msgfmt.c           | 327 ++++++++++++++++++++++++++++++++++-
 gettext-tools/src/write-desktop.c    | 225 ++++++++++++++++++++++++
 gettext-tools/src/write-desktop.h    |  51 ++++++
 gettext-tools/tests/Makefile.am      |   1 +
 gettext-tools/tests/msgfmt-desktop-1 |  85 +++++++++
 gettext-tools/tests/msgfmt-desktop-2 | 149 ++++++++++++++++
 8 files changed, 894 insertions(+), 6 deletions(-)
 create mode 100644 gettext-tools/src/write-desktop.c
 create mode 100644 gettext-tools/src/write-desktop.h
 create mode 100755 gettext-tools/tests/msgfmt-desktop-1
 create mode 100755 gettext-tools/tests/msgfmt-desktop-2

diff --git a/gettext-tools/doc/msgfmt.texi b/gettext-tools/doc/msgfmt.texi
index df34c27..402bc1a 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.
 
address@hidden --desktop
address@hidden address@hidden, @code{msgfmt} option}
address@hidden 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.
 
address@hidden Desktop Entry mode operations
+
address@hidden @samp
address@hidden address@hidden
address@hidden address@hidden, @code{msgfmt} option}
+Specify a .desktop file used as a template.
+
address@hidden address@hidden
address@hidden address@hidden
address@hidden address@hidden, @code{msgfmt} option}
address@hidden address@hidden, @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.
+
address@hidden -l @var{locale}
address@hidden address@hidden
address@hidden address@hidden, @code{msgfmt} option}
address@hidden address@hidden, @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}.
+
address@hidden -d @var{directory}
address@hidden address@hidden, @code{msgfmt} option}
+Specify the base directory of @file{.msg} message catalogs.
+
address@hidden table
+
+To generate a @samp{.desktop} file for a single locale, you can use it
+as follows.
+
address@hidden
+msgfmt --desktop address@hidden address@hidden \
+  -o @var{file} @var{filename}.po @dots{}
address@hidden 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
address@hidden file from multiple @samp{.po} files under a directory,
+specify the directory with the @samp{-d} option.
+
address@hidden
+msgfmt --desktop address@hidden -d @var{directory} -o @var{file}
address@hidden 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 d43afdc..25947c3 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 e414346..3fa17aa 100644
--- a/gettext-tools/src/msgfmt.c
+++ b/gettext-tools/src/msgfmt.c
@@ -26,6 +26,9 @@
 #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"
@@ -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 0000000..cb953d0
--- /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 <address@hidden>.
+
+   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;
+}
diff --git a/gettext-tools/src/write-desktop.h 
b/gettext-tools/src/write-desktop.h
new file mode 100644
index 0000000..028b441
--- /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 <address@hidden>.
+
+   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 */
diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am
index 6d71a23..5d38255 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 0000000..18ca696
--- /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 <address@hidden>, 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 <address@hidden>\n"
+"Language-Team: LANGUAGE <address@hidden>\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 0000000..86a4ab4
--- /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 <address@hidden>, 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 <address@hidden>\n"
+"Language-Team: LANGUAGE <address@hidden>\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 <address@hidden>, 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 <address@hidden>\n"
+"Language-Team: LANGUAGE <address@hidden>\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
-- 
1.8.4.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]