1 /* Writing Java .properties files.
2 Copyright (C) 2003 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 2, or (at your option)
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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include "write-properties.h"
34 #include "msgl-ascii.h"
35 #include "msgl-iconv.h"
36 #include "po-charset.h"
37 #include "utf8-ucs4.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
= (char *) xmalloc (length
+ 1);
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 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]);
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 file, without newline. */
127 write_escaped_string (FILE *fp
, 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
))
143 else if (uc
== 0x0009)
148 else if (uc
== 0x000a)
153 else if (uc
== 0x000d)
158 else if (uc
== 0x000c)
163 else if (/* Backslash must be escaped. */
165 /* Possible comment introducers must be escaped. */
166 || uc
== '#' || uc
== '!'
167 /* Key terminators must be escaped. */
168 || uc
== '=' || uc
== ':')
173 else if (uc
>= 0x0020 && uc
<= 0x007e)
175 /* ASCII characters can be output literally.
176 We could treat non-ASCII ISO-8859-1 characters (0x0080..0x00FF)
177 the same way, but there is no point in doing this; Sun's
178 nativetoascii doesn't do it either. */
181 else if (uc
< 0x10000)
183 /* Single UCS-2 'char' */
184 fprintf (fp
, "\\u%c%c%c%c",
185 hexdigit
[(uc
>> 12) & 0x0f], hexdigit
[(uc
>> 8) & 0x0f],
186 hexdigit
[(uc
>> 4) & 0x0f], hexdigit
[uc
& 0x0f]);
190 /* UTF-16 surrogate: two 'char's. */
191 unsigned int uc1
= 0xd800 + ((uc
- 0x10000) >> 10);
192 unsigned int uc2
= 0xdc00 + ((uc
- 0x10000) & 0x3ff);
193 fprintf (fp
, "\\u%c%c%c%c",
194 hexdigit
[(uc1
>> 12) & 0x0f], hexdigit
[(uc1
>> 8) & 0x0f],
195 hexdigit
[(uc1
>> 4) & 0x0f], hexdigit
[uc1
& 0x0f]);
196 fprintf (fp
, "\\u%c%c%c%c",
197 hexdigit
[(uc2
>> 12) & 0x0f], hexdigit
[(uc2
>> 8) & 0x0f],
198 hexdigit
[(uc2
>> 4) & 0x0f], hexdigit
[uc2
& 0x0f]);
204 /* Writes a message to the file. */
206 write_message (FILE *fp
, const message_ty
*mp
, size_t page_width
, bool debug
)
208 /* Print translator comment if available. */
209 message_print_comment (mp
, fp
);
211 /* Print xgettext extracted comments. */
212 message_print_comment_dot (mp
, fp
);
214 /* Print the file position comments. */
215 message_print_comment_filepos (mp
, fp
, false, page_width
);
217 /* Print flag information in special comment. */
218 message_print_comment_flags (mp
, fp
, debug
);
220 /* Put a comment mark if the message is the header or untranslated or
222 if (mp
->msgid
[0] == '\0'
223 || mp
->msgstr
[0] == '\0'
224 || (mp
->is_fuzzy
&& mp
->msgid
[0] != '\0'))
227 /* Now write the untranslated string and the translated string. */
228 write_escaped_string (fp
, mp
->msgid
, true);
230 write_escaped_string (fp
, mp
->msgstr
, false);
235 /* Writes an entire message list to the file. */
237 write_properties (FILE *fp
, message_list_ty
*mlp
, const char *canon_encoding
,
238 size_t page_width
, bool debug
)
243 /* Convert the messages to Unicode. */
244 iconv_message_list (mlp
, canon_encoding
, po_charset_utf8
, NULL
);
245 for (j
= 0; j
< mlp
->nitems
; ++j
)
247 message_ty
*mp
= mlp
->item
[j
];
249 if (mp
->comment
!= NULL
)
250 for (i
= 0; i
< mp
->comment
->nitems
; ++i
)
251 mp
->comment
->item
[i
] = conv_to_java (mp
->comment
->item
[i
]);
252 if (mp
->comment_dot
!= NULL
)
253 for (i
= 0; i
< mp
->comment_dot
->nitems
; ++i
)
254 mp
->comment_dot
->item
[i
] = conv_to_java (mp
->comment_dot
->item
[i
]);
257 /* Loop through the messages. */
259 for (j
= 0; j
< mlp
->nitems
; ++j
)
261 const message_ty
*mp
= mlp
->item
[j
];
263 if (mp
->msgid_plural
== NULL
&& !mp
->obsolete
)
268 write_message (fp
, mp
, page_width
, debug
);
275 /* Output the contents of a PO file in Java .properties syntax. */
277 msgdomain_list_print_properties (msgdomain_list_ty
*mdlp
, FILE *fp
,
278 size_t page_width
, bool debug
)
280 message_list_ty
*mlp
;
282 if (mdlp
->nitems
== 1)
283 mlp
= mdlp
->item
[0]->messages
;
285 mlp
= message_list_alloc (false);
286 write_properties (fp
, mlp
, mdlp
->encoding
, page_width
, debug
);