diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-12-24 07:38:37 +0000 |
---|---|---|
committer | <> | 2015-02-02 12:02:29 +0000 |
commit | 482840e61f86ca321838a91e902c41d40c098bbb (patch) | |
tree | 01ea2e242fd2792d19fe192476601587901db794 /gettext-tools/src/write-stringtable.c | |
download | gettext-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.c | 335 |
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 */ +}; |