• 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 NeXTstep/GNUstep .strings files.
2   Copyright (C) 2003, 2006-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-stringtable.h"
24
25#include <stdbool.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include "message.h"
30#include "msgl-ascii.h"
31#include "msgl-iconv.h"
32#include "po-charset.h"
33#include "c-strstr.h"
34#include "ostream.h"
35#include "xvasprintf.h"
36#include "write-po.h"
37
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
43          "key" = "value";
44   where the key is the msgid and the value is the msgstr.
45 */
46
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.  */
50
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.  */
57
58/* Writes a key or value to the stream, without newline.  */
59static void
60write_escaped_string (ostream_t stream, const char *str)
61{
62  const char *str_limit = str + strlen (str);
63
64  ostream_write_str (stream, "\"");
65  while (str < str_limit)
66    {
67      unsigned char c = (unsigned char) *str++;
68
69      if (c == '\t')
70	ostream_write_str (stream, "\\t");
71      else if (c == '\n')
72	ostream_write_str (stream, "\\n");
73      else if (c == '\r')
74	ostream_write_str (stream, "\\r");
75      else if (c == '\f')
76	ostream_write_str (stream, "\\f");
77      else if (c == '\\' || c == '"')
78	{
79	  char seq[2];
80	  seq[0] = '\\';
81	  seq[1] = c;
82	  ostream_write_mem (stream, seq, 2);
83	}
84      else
85	{
86	  char seq[1];
87	  seq[0] = c;
88	  ostream_write_mem (stream, seq, 1);
89	}
90    }
91  ostream_write_str (stream, "\"");
92}
93
94/* Writes a message to the stream.  */
95static void
96write_message (ostream_t stream, const message_ty *mp,
97	       size_t page_width, bool debug)
98{
99  /* Print translator comment if available.  */
100  if (mp->comment != NULL)
101    {
102      size_t j;
103
104      for (j = 0; j < mp->comment->nitems; ++j)
105	{
106	  const char *s = mp->comment->item[j];
107
108	  /* Test whether it is safe to output the comment in C style, or
109	     whether we need C++ style for it.  */
110	  if (c_strstr (s, "*/") == NULL)
111	    {
112	      ostream_write_str (stream, "/*");
113	      if (*s != '\0' && *s != '\n')
114		ostream_write_str (stream, " ");
115	      ostream_write_str (stream, s);
116	      ostream_write_str (stream, " */\n");
117	    }
118	  else
119	    do
120	      {
121		const char *e;
122		ostream_write_str (stream, "//");
123		if (*s != '\0' && *s != '\n')
124		  ostream_write_str (stream, " ");
125		e = strchr (s, '\n');
126		if (e == NULL)
127		  {
128		    ostream_write_str (stream, s);
129		    s = NULL;
130		  }
131		else
132		  {
133		    ostream_write_mem (stream, s, e - s);
134		    s = e + 1;
135		  }
136		ostream_write_str (stream, "\n");
137	      }
138	    while (s != NULL);
139	}
140    }
141
142  /* Print xgettext extracted comments.  */
143  if (mp->comment_dot != NULL)
144    {
145      size_t j;
146
147      for (j = 0; j < mp->comment_dot->nitems; ++j)
148	{
149	  const char *s = mp->comment_dot->item[j];
150
151	  /* Test whether it is safe to output the comment in C style, or
152	     whether we need C++ style for it.  */
153	  if (c_strstr (s, "*/") == NULL)
154	    {
155	      ostream_write_str (stream, "/* Comment: ");
156	      ostream_write_str (stream, s);
157	      ostream_write_str (stream, " */\n");
158	    }
159	  else
160	    {
161	      bool first = true;
162	      do
163		{
164		  const char *e;
165		  ostream_write_str (stream, "//");
166		  if (first || (*s != '\0' && *s != '\n'))
167		    ostream_write_str (stream, " ");
168		  if (first)
169		    ostream_write_str (stream, "Comment: ");
170		  e = strchr (s, '\n');
171		  if (e == NULL)
172		    {
173		      ostream_write_str (stream, s);
174		      s = NULL;
175		    }
176		  else
177		    {
178		      ostream_write_mem (stream, s, e - s);
179		      s = e + 1;
180		    }
181		  ostream_write_str (stream, "\n");
182		  first = false;
183		}
184	      while (s != NULL);
185	    }
186	}
187    }
188
189  /* Print the file position comments.  */
190  if (mp->filepos_count != 0)
191    {
192      size_t j;
193
194      for (j = 0; j < mp->filepos_count; ++j)
195	{
196	  lex_pos_ty *pp = &mp->filepos[j];
197	  char *cp = pp->file_name;
198	  char *str;
199
200	  while (cp[0] == '.' && cp[1] == '/')
201	    cp += 2;
202	  str = xasprintf ("/* File: %s:%ld */\n", cp, (long) pp->line_number);
203	  ostream_write_str (stream, str);
204	  free (str);
205	}
206    }
207
208  /* Print flag information in special comment.  */
209  if (mp->is_fuzzy || mp->msgstr[0] == '\0')
210    ostream_write_str (stream, "/* Flag: untranslated */\n");
211  if (mp->obsolete)
212    ostream_write_str (stream, "/* Flag: unmatched */\n");
213  {
214    size_t i;
215    for (i = 0; i < NFORMATS; i++)
216      if (significant_format_p (mp->is_format[i]))
217	{
218	  ostream_write_str (stream, "/* Flag: ");
219	  ostream_write_str (stream,
220			     make_format_description_string (mp->is_format[i],
221							     format_language[i],
222							     debug));
223	  ostream_write_str (stream, " */\n");
224	}
225  }
226
227  /* Now write the untranslated string and the translated string.  */
228  write_escaped_string (stream, mp->msgid);
229  ostream_write_str (stream, " = ");
230  if (mp->msgstr[0] != '\0')
231    {
232      if (mp->is_fuzzy)
233	{
234	  /* Output the msgid as value, so that at runtime the untranslated
235	     string is returned.  */
236	  write_escaped_string (stream, mp->msgid);
237
238	  /* Output the msgstr as a comment, so that at runtime
239	     propertyListFromStringsFileFormat ignores it.  */
240	  if (c_strstr (mp->msgstr, "*/") == NULL)
241	    {
242	      ostream_write_str (stream, " /* = ");
243	      write_escaped_string (stream, mp->msgstr);
244	      ostream_write_str (stream, " */");
245	    }
246	  else
247	    {
248	      ostream_write_str (stream, "; // = ");
249	      write_escaped_string (stream, mp->msgstr);
250	    }
251	}
252      else
253	write_escaped_string (stream, mp->msgstr);
254    }
255  else
256    {
257      /* Output the msgid as value, so that at runtime the untranslated
258	 string is returned.  */
259      write_escaped_string (stream, mp->msgid);
260    }
261  ostream_write_str (stream, ";");
262
263  ostream_write_str (stream, "\n");
264}
265
266/* Writes an entire message list to the stream.  */
267static void
268write_stringtable (ostream_t stream, message_list_ty *mlp,
269		   const char *canon_encoding, size_t page_width, bool debug)
270{
271  bool blank_line;
272  size_t j;
273
274  /* Convert the messages to Unicode.  */
275  iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
276
277  /* Output the BOM.  */
278  if (!is_ascii_message_list (mlp))
279    ostream_write_str (stream, "\xef\xbb\xbf");
280
281  /* Loop through the messages.  */
282  blank_line = false;
283  for (j = 0; j < mlp->nitems; ++j)
284    {
285      const message_ty *mp = mlp->item[j];
286
287      if (mp->msgid_plural == NULL)
288	{
289	  if (blank_line)
290	    ostream_write_str (stream, "\n");
291
292	  write_message (stream, mp, page_width, debug);
293
294	  blank_line = true;
295	}
296    }
297}
298
299/* Output the contents of a PO file in .strings syntax.  */
300static void
301msgdomain_list_print_stringtable (msgdomain_list_ty *mdlp, ostream_t stream,
302				  size_t page_width, bool debug)
303{
304  message_list_ty *mlp;
305
306  if (mdlp->nitems == 1)
307    mlp = mdlp->item[0]->messages;
308  else
309    mlp = message_list_alloc (false);
310  write_stringtable (stream, mlp, mdlp->encoding, page_width, debug);
311}
312
313/* Describes a PO file in .strings syntax.  */
314const struct catalog_output_format output_format_stringtable =
315{
316  msgdomain_list_print_stringtable,	/* print */
317  true,					/* requires_utf8 */
318  false,				/* supports_color */
319  false,				/* supports_multiple_domains */
320  false,				/* supports_contexts */
321  false,				/* supports_plurals */
322  false,				/* alternative_is_po */
323  false					/* alternative_is_java_class */
324};
325