1 /* Writing NeXTstep/GNUstep .strings 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-stringtable.h"
32 #include "msgl-ascii.h"
33 #include "msgl-iconv.h"
34 #include "po-charset.h"
38 /* The format of NeXTstep/GNUstep .strings files is documented in
39 gnustep-base-1.8.0/Tools/make_strings/Using.txt
40 and in the comments of method propertyListFromStringsFileFormat in
41 gnustep-base-1.8.0/Source/NSString.m
42 In summary, it's a Objective-C like file with pseudo-assignments of the form
44 where the key is the msgid and the value is the msgstr.
47 /* Handling of comments: We copy all comments from the PO file to the
48 .strings file. This is not really needed; it's a service for translators
49 who don't like PO files and prefer to maintain the .strings file. */
51 /* Since the interpretation of text files in GNUstep depends on the locale's
52 encoding if they don't have a BOM, we choose one of three encodings with
53 a BOM: UCS-2BE, UCS-2LE, UTF-8. Since the first two of these don't cope
54 with all of Unicode and we don't know whether GNUstep will switch to
55 UTF-16 instead of UCS-2, we use UTF-8 with BOM. BOMs are bad because they
56 get in the way when concatenating files, but here we have no choice. */
58 /* Writes a key or value to the file, without newline. */
60 write_escaped_string (FILE *fp
, const char *str
)
62 const char *str_limit
= str
+ strlen (str
);
65 while (str
< str_limit
)
67 unsigned char c
= (unsigned char) *str
++;
89 else if (c
== '\\' || c
== '"')
100 /* Writes a message to the file. */
102 write_message (FILE *fp
, const message_ty
*mp
, size_t page_width
, bool debug
)
104 /* Print translator comment if available. */
105 if (mp
->comment
!= NULL
)
109 for (j
= 0; j
< mp
->comment
->nitems
; ++j
)
111 const char *s
= mp
->comment
->item
[j
];
113 /* Test whether it is safe to output the comment in C style, or
114 whether we need C++ style for it. */
115 if (strstr (s
, "*/") == NULL
)
118 if (*s
!= '\0' && *s
!= '\n' && *s
!= ' ')
128 if (*s
!= '\0' && *s
!= '\n' && *s
!= ' ')
130 e
= strchr (s
, '\n');
138 fwrite (s
, 1, e
- s
, fp
);
147 /* Print xgettext extracted comments. */
148 if (mp
->comment_dot
!= NULL
)
152 for (j
= 0; j
< mp
->comment_dot
->nitems
; ++j
)
154 const char *s
= mp
->comment_dot
->item
[j
];
156 /* Test whether it is safe to output the comment in C style, or
157 whether we need C++ style for it. */
158 if (strstr (s
, "*/") == NULL
)
160 fputs ("/* Comment: ", fp
);
171 if (first
|| (*s
!= '\0' && *s
!= '\n' && *s
!= ' '))
174 fputs ("Comment: ", fp
);
175 e
= strchr (s
, '\n');
183 fwrite (s
, 1, e
- s
, fp
);
194 /* Print the file position comments. */
195 if (mp
->filepos_count
!= 0)
199 for (j
= 0; j
< mp
->filepos_count
; ++j
)
201 lex_pos_ty
*pp
= &mp
->filepos
[j
];
202 char *cp
= pp
->file_name
;
203 while (cp
[0] == '.' && cp
[1] == '/')
205 fprintf (fp
, "/* File: %s:%ld */\n", cp
, (long) pp
->line_number
);
209 /* Print flag information in special comment. */
210 if (mp
->is_fuzzy
|| mp
->msgstr
[0] == '\0')
211 fputs ("/* Flag: untranslated */\n", fp
);
213 fputs ("/* Flag: unmatched */\n", fp
);
216 for (i
= 0; i
< NFORMATS
; i
++)
217 if (significant_format_p (mp
->is_format
[i
]))
219 fputs ("/* Flag:", fp
);
220 fputs (make_format_description_string (mp
->is_format
[i
],
221 format_language
[i
], debug
),
227 /* Now write the untranslated string and the translated string. */
228 write_escaped_string (fp
, mp
->msgid
);
230 if (mp
->msgstr
[0] != '\0')
234 /* Output the msgid as value, so that at runtime the untranslated
235 string is returned. */
236 write_escaped_string (fp
, mp
->msgid
);
238 /* Output the msgstr as a comment, so that at runtime
239 propertyListFromStringsFileFormat ignores it. */
240 if (strstr (mp
->msgstr
, "*/") == NULL
)
242 fputs (" /* = ", fp
);
243 write_escaped_string (fp
, mp
->msgstr
);
248 fputs ("; // = ", fp
);
249 write_escaped_string (fp
, mp
->msgstr
);
253 write_escaped_string (fp
, mp
->msgstr
);
257 /* Output the msgid as value, so that at runtime the untranslated
258 string is returned. */
259 write_escaped_string (fp
, mp
->msgid
);
266 /* Writes an entire message list to the file. */
268 write_stringtable (FILE *fp
, message_list_ty
*mlp
, const char *canon_encoding
,
269 size_t page_width
, bool debug
)
274 /* Convert the messages to Unicode. */
275 iconv_message_list (mlp
, canon_encoding
, po_charset_utf8
, NULL
);
277 /* Output the BOM. */
278 if (!is_ascii_message_list (mlp
))
279 fputs ("\xef\xbb\xbf", fp
);
281 /* Loop through the messages. */
283 for (j
= 0; j
< mlp
->nitems
; ++j
)
285 const message_ty
*mp
= mlp
->item
[j
];
287 if (mp
->msgid_plural
== NULL
)
292 write_message (fp
, mp
, page_width
, debug
);
299 /* Output the contents of a PO file in .strings syntax. */
301 msgdomain_list_print_stringtable (msgdomain_list_ty
*mdlp
, FILE *fp
,
302 size_t page_width
, bool debug
)
304 message_list_ty
*mlp
;
306 if (mdlp
->nitems
== 1)
307 mlp
= mdlp
->item
[0]->messages
;
309 mlp
= message_list_alloc (false);
310 write_stringtable (fp
, mlp
, mdlp
->encoding
, page_width
, debug
);