• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* Writing Java .properties files.
2   Copyright (C) 2003, 2005-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
22/* Specification.  */
23#include "write-properties.h"
24
25#include <errno.h>
26#include <stdbool.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "error.h"
32#include "message.h"
33#include "msgl-ascii.h"
34#include "msgl-iconv.h"
35#include "po-charset.h"
36#include "unistr.h"
37#include "ostream.h"
38#include "write-po.h"
39#include "xalloc.h"
40
41/* The format of the Java .properties files is documented in the JDK
42   documentation for class java.util.Properties.  In the case of .properties
43   files for PropertyResourceBundle, for each message, the msgid becomes the
44   key (left-hand side) and the msgstr becomes the value (right-hand side)
45   of a "key=value" line.  Messages with plurals are not supported in this
46   format.  */
47
48/* Handling of comments: We copy all comments from the PO file to the
49   .properties file. This is not really needed; it's a service for translators
50   who don't like PO files and prefer to maintain the .properties file.  */
51
52/* Converts a string to JAVA encoding (with \uxxxx sequences for non-ASCII
53   characters).  */
54static const char *
55conv_to_java (const char *string)
56{
57  /* We cannot use iconv to "JAVA" because not all iconv() implementations
58     know about the "JAVA" encoding.  */
59  static const char hexdigit[] = "0123456789abcdef";
60  size_t length;
61  char *result;
62
63  if (is_ascii_string (string))
64    return string;
65
66  length = 0;
67  {
68    const char *str = string;
69    const char *str_limit = str + strlen (str);
70
71    while (str < str_limit)
72      {
73	unsigned int uc;
74	str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
75	length += (uc <= 0x007f ? 1 : uc < 0x10000 ? 6 : 12);
76      }
77  }
78
79  result = XNMALLOC (length + 1, char);
80
81  {
82    char *newstr = result;
83    const char *str = string;
84    const char *str_limit = str + strlen (str);
85
86    while (str < str_limit)
87      {
88	unsigned int uc;
89	str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
90	if (uc <= 0x007f)
91	  /* ASCII characters can be output literally.
92	     We could treat non-ASCII ISO-8859-1 characters (0x0080..0x00FF)
93	     the same way, but there is no point in doing this; Sun's
94	     nativetoascii doesn't do it either.  */
95	  *newstr++ = uc;
96	else if (uc < 0x10000)
97	  {
98	    /* Single UCS-2 'char'  */
99	    sprintf (newstr, "\\u%c%c%c%c",
100		     hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
101		     hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
102	    newstr += 6;
103	  }
104	else
105	  {
106	    /* UTF-16 surrogate: two 'char's.  */
107	    unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10);
108	    unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
109	    sprintf (newstr, "\\u%c%c%c%c",
110		     hexdigit[(uc1 >> 12) & 0x0f], hexdigit[(uc1 >> 8) & 0x0f],
111		     hexdigit[(uc1 >> 4) & 0x0f], hexdigit[uc1 & 0x0f]);
112	    newstr += 6;
113	    sprintf (newstr, "\\u%c%c%c%c",
114		     hexdigit[(uc2 >> 12) & 0x0f], hexdigit[(uc2 >> 8) & 0x0f],
115		     hexdigit[(uc2 >> 4) & 0x0f], hexdigit[uc2 & 0x0f]);
116	    newstr += 6;
117	  }
118      }
119    *newstr = '\0';
120  }
121
122  return result;
123}
124
125/* Writes a key or value to the stream, without newline.  */
126static void
127write_escaped_string (ostream_t stream, const char *str, bool in_key)
128{
129  static const char hexdigit[] = "0123456789abcdef";
130  const char *str_limit = str + strlen (str);
131  bool first = true;
132
133  while (str < str_limit)
134    {
135      unsigned int uc;
136      str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
137      /* Whitespace must be escaped.  */
138      if (uc == 0x0020 && (first || in_key))
139	ostream_write_str (stream, "\\ ");
140      else if (uc == 0x0009)
141	ostream_write_str (stream, "\\t");
142      else if (uc == 0x000a)
143	ostream_write_str (stream, "\\n");
144      else if (uc == 0x000d)
145	ostream_write_str (stream, "\\r");
146      else if (uc == 0x000c)
147	ostream_write_str (stream, "\\f");
148      else if (/* Backslash must be escaped.  */
149	       uc == '\\'
150	       /* Possible comment introducers must be escaped.  */
151	       || uc == '#' || uc == '!'
152	       /* Key terminators must be escaped.  */
153	       || uc == '=' || uc == ':')
154	{
155	  char seq[2];
156	  seq[0] = '\\';
157	  seq[1] = uc;
158	  ostream_write_mem (stream, seq, 2);
159	}
160      else if (uc >= 0x0020 && uc <= 0x007e)
161	{
162	  /* ASCII characters can be output literally.
163	     We could treat non-ASCII ISO-8859-1 characters (0x0080..0x00FF)
164	     the same way, but there is no point in doing this; Sun's
165	     nativetoascii doesn't do it either.  */
166	  char seq[1];
167	  seq[0] = uc;
168	  ostream_write_mem (stream, seq, 1);
169	}
170      else if (uc < 0x10000)
171	{
172	  /* Single UCS-2 'char'  */
173	  char seq[6];
174	  seq[0] = '\\';
175	  seq[1] = 'u';
176	  seq[2] = hexdigit[(uc >> 12) & 0x0f];
177	  seq[3] = hexdigit[(uc >> 8) & 0x0f];
178	  seq[4] = hexdigit[(uc >> 4) & 0x0f];
179	  seq[5] = hexdigit[uc & 0x0f];
180	  ostream_write_mem (stream, seq, 6);
181	}
182      else
183	{
184	  /* UTF-16 surrogate: two 'char's.  */
185	  unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10);
186	  unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
187	  char seq[6];
188	  seq[0] = '\\';
189	  seq[1] = 'u';
190	  seq[2] = hexdigit[(uc1 >> 12) & 0x0f];
191	  seq[3] = hexdigit[(uc1 >> 8) & 0x0f];
192	  seq[4] = hexdigit[(uc1 >> 4) & 0x0f];
193	  seq[5] = hexdigit[uc1 & 0x0f];
194	  ostream_write_mem (stream, seq, 6);
195	  seq[0] = '\\';
196	  seq[1] = 'u';
197	  seq[2] = hexdigit[(uc2 >> 12) & 0x0f];
198	  seq[3] = hexdigit[(uc2 >> 8) & 0x0f];
199	  seq[4] = hexdigit[(uc2 >> 4) & 0x0f];
200	  seq[5] = hexdigit[uc2 & 0x0f];
201	  ostream_write_mem (stream, seq, 6);
202	}
203      first = false;
204    }
205}
206
207/* Writes a message to the stream.  */
208static void
209write_message (ostream_t stream, const message_ty *mp,
210	       size_t page_width, bool debug)
211{
212  /* Print translator comment if available.  */
213  message_print_comment (mp, stream);
214
215  /* Print xgettext extracted comments.  */
216  message_print_comment_dot (mp, stream);
217
218  /* Print the file position comments.  */
219  message_print_comment_filepos (mp, stream, false, page_width);
220
221  /* Print flag information in special comment.  */
222  message_print_comment_flags (mp, stream, debug);
223
224  /* Put a comment mark if the message is the header or untranslated or
225     fuzzy.  */
226  if (is_header (mp)
227      || mp->msgstr[0] == '\0'
228      || (mp->is_fuzzy && !is_header (mp)))
229    ostream_write_str (stream, "!");
230
231  /* Now write the untranslated string and the translated string.  */
232  write_escaped_string (stream, mp->msgid, true);
233  ostream_write_str (stream, "=");
234  write_escaped_string (stream, mp->msgstr, false);
235
236  ostream_write_str (stream, "\n");
237}
238
239/* Writes an entire message list to the stream.  */
240static void
241write_properties (ostream_t stream, message_list_ty *mlp,
242		  const char *canon_encoding, size_t page_width, bool debug)
243{
244  bool blank_line;
245  size_t j, i;
246
247  /* Convert the messages to Unicode.  */
248  iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
249  for (j = 0; j < mlp->nitems; ++j)
250    {
251      message_ty *mp = mlp->item[j];
252
253      if (mp->comment != NULL)
254	for (i = 0; i < mp->comment->nitems; ++i)
255	  mp->comment->item[i] = conv_to_java (mp->comment->item[i]);
256      if (mp->comment_dot != NULL)
257	for (i = 0; i < mp->comment_dot->nitems; ++i)
258	  mp->comment_dot->item[i] = conv_to_java (mp->comment_dot->item[i]);
259    }
260
261  /* Loop through the messages.  */
262  blank_line = false;
263  for (j = 0; j < mlp->nitems; ++j)
264    {
265      const message_ty *mp = mlp->item[j];
266
267      if (mp->msgid_plural == NULL && !mp->obsolete)
268	{
269	  if (blank_line)
270	    ostream_write_str (stream, "\n");
271
272	  write_message (stream, mp, page_width, debug);
273
274	  blank_line = true;
275	}
276    }
277}
278
279/* Output the contents of a PO file in Java .properties syntax.  */
280static void
281msgdomain_list_print_properties (msgdomain_list_ty *mdlp, ostream_t stream,
282				 size_t page_width, bool debug)
283{
284  message_list_ty *mlp;
285
286  if (mdlp->nitems == 1)
287    mlp = mdlp->item[0]->messages;
288  else
289    mlp = message_list_alloc (false);
290  write_properties (stream, mlp, mdlp->encoding, page_width, debug);
291}
292
293/* Describes a PO file in Java .properties syntax.  */
294const struct catalog_output_format output_format_properties =
295{
296  msgdomain_list_print_properties,	/* print */
297  true,					/* requires_utf8 */
298  false,				/* supports_color */
299  false,				/* supports_multiple_domains */
300  false,				/* supports_contexts */
301  false,				/* supports_plurals */
302  true,					/* alternative_is_po */
303  true					/* alternative_is_java_class */
304};
305