1 /* Writing Java .properties files.
2 Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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.
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.
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/>. */
23 #include "write-properties.h"
33 #include "msgl-ascii.h"
34 #include "msgl-iconv.h"
35 #include "po-charset.h"
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
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. */
52 /* Converts a string to JAVA encoding (with \uxxxx sequences for non-ASCII
55 conv_to_java (const char *string)
57 /* We cannot use iconv to "JAVA" because not all iconv() implementations
58 know about the "JAVA" encoding. */
59 static const char hexdigit[] = "0123456789abcdef";
63 if (is_ascii_string (string))
68 const char *str = string;
69 const char *str_limit = str + strlen (str);
71 while (str < str_limit)
74 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
75 length += (uc <= 0x007f ? 1 : uc < 0x10000 ? 6 : 12);
79 result = XNMALLOC (length + 1, char);
82 char *newstr = result;
83 const char *str = string;
84 const char *str_limit = str + strlen (str);
86 while (str < str_limit)
89 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
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. */
96 else if (uc < 0x10000)
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]);
106 /* UTF-16 surrogate: two 'char's. */
107 ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
108 ucs4_t 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]);
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]);
125 /* Writes a key or value to the stream, without newline. */
127 write_escaped_string (ostream_t stream, const char *str, bool in_key)
129 static const char hexdigit[] = "0123456789abcdef";
130 const char *str_limit = str + strlen (str);
133 while (str < str_limit)
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. */
150 /* Possible comment introducers must be escaped. */
151 || uc == '#' || uc == '!'
152 /* Key terminators must be escaped. */
153 || uc == '=' || uc == ':')
158 ostream_write_mem (stream, seq, 2);
160 else if (uc >= 0x0020 && uc <= 0x007e)
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. */
168 ostream_write_mem (stream, seq, 1);
170 else if (uc < 0x10000)
172 /* Single UCS-2 'char' */
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);
184 /* UTF-16 surrogate: two 'char's. */
185 ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
186 ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
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);
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);
207 /* Writes a message to the stream. */
209 write_message (ostream_t stream, const message_ty *mp,
210 size_t page_width, bool debug)
212 /* Print translator comment if available. */
213 message_print_comment (mp, stream);
215 /* Print xgettext extracted comments. */
216 message_print_comment_dot (mp, stream);
218 /* Print the file position comments. */
219 message_print_comment_filepos (mp, stream, false, page_width);
221 /* Print flag information in special comment. */
222 message_print_comment_flags (mp, stream, debug);
224 /* Put a comment mark if the message is the header or untranslated or
227 || mp->msgstr[0] == '\0'
228 || (mp->is_fuzzy && !is_header (mp)))
229 ostream_write_str (stream, "!");
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);
236 ostream_write_str (stream, "\n");
239 /* Writes an entire message list to the stream. */
241 write_properties (ostream_t stream, message_list_ty *mlp,
242 const char *canon_encoding, size_t page_width, bool debug)
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)
251 message_ty *mp = mlp->item[j];
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]);
261 /* Loop through the messages. */
263 for (j = 0; j < mlp->nitems; ++j)
265 const message_ty *mp = mlp->item[j];
267 if (mp->msgid_plural == NULL && !mp->obsolete)
270 ostream_write_str (stream, "\n");
272 write_message (stream, mp, page_width, debug);
279 /* Output the contents of a PO file in Java .properties syntax. */
281 msgdomain_list_print_properties (msgdomain_list_ty *mdlp, ostream_t stream,
282 size_t page_width, bool debug)
284 message_list_ty *mlp;
286 if (mdlp->nitems == 1)
287 mlp = mdlp->item[0]->messages;
289 mlp = message_list_alloc (false);
290 write_properties (stream, mlp, mdlp->encoding, page_width, debug);
293 /* Describes a PO file in Java .properties syntax. */
294 const struct catalog_output_format output_format_properties =
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 false, /* sorts_obsoletes_to_end */
303 true, /* alternative_is_po */
304 true /* alternative_is_java_class */