summaryrefslogtreecommitdiff
path: root/gettext-tools/src/write-stringtable.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-12-24 07:38:37 +0000
committer <>2015-02-02 12:02:29 +0000
commit482840e61f86ca321838a91e902c41d40c098bbb (patch)
tree01ea2e242fd2792d19fe192476601587901db794 /gettext-tools/src/write-stringtable.c
downloadgettext-tarball-482840e61f86ca321838a91e902c41d40c098bbb.tar.gz
Imported from /home/lorry/working-area/delta_gettext-tarball/gettext-0.19.4.tar.xz.gettext-0.19.4
Diffstat (limited to 'gettext-tools/src/write-stringtable.c')
-rw-r--r--gettext-tools/src/write-stringtable.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/gettext-tools/src/write-stringtable.c b/gettext-tools/src/write-stringtable.c
new file mode 100644
index 0000000..34e171f
--- /dev/null
+++ b/gettext-tools/src/write-stringtable.c
@@ -0,0 +1,335 @@
+/* Writing NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003, 2006-2008 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-stringtable.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "msgl-ascii.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "c-strstr.h"
+#include "ostream.h"
+#include "xvasprintf.h"
+#include "write-po.h"
+
+/* The format of NeXTstep/GNUstep .strings files is documented in
+ gnustep-base-1.8.0/Tools/make_strings/Using.txt
+ and in the comments of method propertyListFromStringsFileFormat in
+ gnustep-base-1.8.0/Source/NSString.m
+ In summary, it's a Objective-C like file with pseudo-assignments of the form
+ "key" = "value";
+ where the key is the msgid and the value is the msgstr.
+ */
+
+/* Handling of comments: We copy all comments from the PO file to the
+ .strings file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .strings file. */
+
+/* Since the interpretation of text files in GNUstep depends on the locale's
+ encoding if they don't have a BOM, we choose one of three encodings with
+ a BOM: UCS-2BE, UCS-2LE, UTF-8. Since the first two of these don't cope
+ with all of Unicode and we don't know whether GNUstep will switch to
+ UTF-16 instead of UCS-2, we use UTF-8 with BOM. BOMs are bad because they
+ get in the way when concatenating files, but here we have no choice. */
+
+/* Writes a key or value to the stream, without newline. */
+static void
+write_escaped_string (ostream_t stream, const char *str)
+{
+ const char *str_limit = str + strlen (str);
+
+ ostream_write_str (stream, "\"");
+ while (str < str_limit)
+ {
+ unsigned char c = (unsigned char) *str++;
+
+ if (c == '\t')
+ ostream_write_str (stream, "\\t");
+ else if (c == '\n')
+ ostream_write_str (stream, "\\n");
+ else if (c == '\r')
+ ostream_write_str (stream, "\\r");
+ else if (c == '\f')
+ ostream_write_str (stream, "\\f");
+ else if (c == '\\' || c == '"')
+ {
+ char seq[2];
+ seq[0] = '\\';
+ seq[1] = c;
+ ostream_write_mem (stream, seq, 2);
+ }
+ else
+ {
+ char seq[1];
+ seq[0] = c;
+ ostream_write_mem (stream, seq, 1);
+ }
+ }
+ ostream_write_str (stream, "\"");
+}
+
+/* Writes a message to the stream. */
+static void
+write_message (ostream_t stream, const message_ty *mp,
+ size_t page_width, bool debug)
+{
+ /* Print translator comment if available. */
+ if (mp->comment != NULL)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->comment->nitems; ++j)
+ {
+ const char *s = mp->comment->item[j];
+
+ /* Test whether it is safe to output the comment in C style, or
+ whether we need C++ style for it. */
+ if (c_strstr (s, "*/") == NULL)
+ {
+ ostream_write_str (stream, "/*");
+ if (*s != '\0' && *s != '\n')
+ ostream_write_str (stream, " ");
+ ostream_write_str (stream, s);
+ ostream_write_str (stream, " */\n");
+ }
+ else
+ do
+ {
+ const char *e;
+ ostream_write_str (stream, "//");
+ if (*s != '\0' && *s != '\n')
+ ostream_write_str (stream, " ");
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ ostream_write_str (stream, s);
+ s = NULL;
+ }
+ else
+ {
+ ostream_write_mem (stream, s, e - s);
+ s = e + 1;
+ }
+ ostream_write_str (stream, "\n");
+ }
+ while (s != NULL);
+ }
+ }
+
+ /* Print xgettext extracted comments. */
+ if (mp->comment_dot != NULL)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->comment_dot->nitems; ++j)
+ {
+ const char *s = mp->comment_dot->item[j];
+
+ /* Test whether it is safe to output the comment in C style, or
+ whether we need C++ style for it. */
+ if (c_strstr (s, "*/") == NULL)
+ {
+ ostream_write_str (stream, "/* Comment: ");
+ ostream_write_str (stream, s);
+ ostream_write_str (stream, " */\n");
+ }
+ else
+ {
+ bool first = true;
+ do
+ {
+ const char *e;
+ ostream_write_str (stream, "//");
+ if (first || (*s != '\0' && *s != '\n'))
+ ostream_write_str (stream, " ");
+ if (first)
+ ostream_write_str (stream, "Comment: ");
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ ostream_write_str (stream, s);
+ s = NULL;
+ }
+ else
+ {
+ ostream_write_mem (stream, s, e - s);
+ s = e + 1;
+ }
+ ostream_write_str (stream, "\n");
+ first = false;
+ }
+ while (s != NULL);
+ }
+ }
+ }
+
+ /* Print the file position comments. */
+ if (mp->filepos_count != 0)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->filepos_count; ++j)
+ {
+ lex_pos_ty *pp = &mp->filepos[j];
+ const char *cp = pp->file_name;
+ char *str;
+
+ while (cp[0] == '.' && cp[1] == '/')
+ cp += 2;
+ str = xasprintf ("/* File: %s:%ld */\n", cp, (long) pp->line_number);
+ ostream_write_str (stream, str);
+ free (str);
+ }
+ }
+
+ /* Print flag information in special comment. */
+ if (mp->is_fuzzy || mp->msgstr[0] == '\0')
+ ostream_write_str (stream, "/* Flag: untranslated */\n");
+ if (mp->obsolete)
+ ostream_write_str (stream, "/* Flag: unmatched */\n");
+ {
+ size_t i;
+ for (i = 0; i < NFORMATS; i++)
+ if (significant_format_p (mp->is_format[i]))
+ {
+ ostream_write_str (stream, "/* Flag: ");
+ ostream_write_str (stream,
+ make_format_description_string (mp->is_format[i],
+ format_language[i],
+ debug));
+ ostream_write_str (stream, " */\n");
+ }
+ }
+ if (has_range_p (mp->range))
+ {
+ char *string;
+
+ ostream_write_str (stream, "/* Flag: ");
+ string = make_range_description_string (mp->range);
+ ostream_write_str (stream, string);
+ free (string);
+ ostream_write_str (stream, " */\n");
+ }
+
+ /* Now write the untranslated string and the translated string. */
+ write_escaped_string (stream, mp->msgid);
+ ostream_write_str (stream, " = ");
+ if (mp->msgstr[0] != '\0')
+ {
+ if (mp->is_fuzzy)
+ {
+ /* Output the msgid as value, so that at runtime the untranslated
+ string is returned. */
+ write_escaped_string (stream, mp->msgid);
+
+ /* Output the msgstr as a comment, so that at runtime
+ propertyListFromStringsFileFormat ignores it. */
+ if (c_strstr (mp->msgstr, "*/") == NULL)
+ {
+ ostream_write_str (stream, " /* = ");
+ write_escaped_string (stream, mp->msgstr);
+ ostream_write_str (stream, " */");
+ }
+ else
+ {
+ ostream_write_str (stream, "; // = ");
+ write_escaped_string (stream, mp->msgstr);
+ }
+ }
+ else
+ write_escaped_string (stream, mp->msgstr);
+ }
+ else
+ {
+ /* Output the msgid as value, so that at runtime the untranslated
+ string is returned. */
+ write_escaped_string (stream, mp->msgid);
+ }
+ ostream_write_str (stream, ";");
+
+ ostream_write_str (stream, "\n");
+}
+
+/* Writes an entire message list to the stream. */
+static void
+write_stringtable (ostream_t stream, message_list_ty *mlp,
+ const char *canon_encoding, size_t page_width, bool debug)
+{
+ bool blank_line;
+ size_t j;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Output the BOM. */
+ if (!is_ascii_message_list (mlp))
+ ostream_write_str (stream, "\xef\xbb\xbf");
+
+ /* Loop through the messages. */
+ blank_line = false;
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ const message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural == NULL)
+ {
+ if (blank_line)
+ ostream_write_str (stream, "\n");
+
+ write_message (stream, mp, page_width, debug);
+
+ blank_line = true;
+ }
+ }
+}
+
+/* Output the contents of a PO file in .strings syntax. */
+static void
+msgdomain_list_print_stringtable (msgdomain_list_ty *mdlp, ostream_t stream,
+ size_t page_width, bool debug)
+{
+ message_list_ty *mlp;
+
+ if (mdlp->nitems == 1)
+ mlp = mdlp->item[0]->messages;
+ else
+ mlp = message_list_alloc (false);
+ write_stringtable (stream, mlp, mdlp->encoding, page_width, debug);
+}
+
+/* Describes a PO file in .strings syntax. */
+const struct catalog_output_format output_format_stringtable =
+{
+ msgdomain_list_print_stringtable, /* print */
+ true, /* requires_utf8 */
+ false, /* supports_color */
+ false, /* supports_multiple_domains */
+ false, /* supports_contexts */
+ false, /* supports_plurals */
+ false, /* sorts_obsoletes_to_end */
+ false, /* alternative_is_po */
+ false /* alternative_is_java_class */
+};