From: Bruno Haible Date: Fri, 5 Jan 2018 22:42:46 +0000 (+0100) Subject: Add support for .rsj files. X-Git-Tag: v0.20~431 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60a684004c0b3db3c348624a9d5b19a37024a7bf;p=thirdparty%2Fgettext.git Add support for .rsj files. * gettext-tools/src/x-rst.h (EXTENSIONS_RST, SCANNERS_RST): Add RSJ scanner. (extract_rsj): New declaration. * gettext-tools/src/x-rst.c: Implement extraction from .rsj files. * gettext-tools/src/xgettext.c (usage): Mention RSJ along with RST. * gettext-tools/tests/xgettext-rst-1: New file. * gettext-tools/tests/xgettext-rst-2: New file. * gettext-tools/tests/Makefile.am (TESTS): Add them. * gettext-tools/tests/lang-pascal: Extract from .rsj file, if the compiler generated it. * gettext-tools/doc/gettext.texi (RST): Mention that .rsj files are supported as well. * gettext-tools/doc/xgettext.texi (Choice of input file language): Mention RSJ along with RST. * NEWS: Mention the change. --- diff --git a/NEWS b/NEWS index d13901722..a1eeeb0cb 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ npgettext, dnpgettext, dcnpgettext). o better detection of question mark and slash as operators (as opposed to regular expression delimiters). + - Pascal: + xgettext can now extract strings from .rsj files, produced by the + Free Pascal compiler version 3.0.0 or newer. Version 0.19.8 - June 2016 diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 046214828..dfb1fd353 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -12283,6 +12283,11 @@ gettext @node RST, Glade, POT, List of Data Formats @subsection Resource String Table @cindex RST +@cindex RSJ + +RST is the format of resource string table files of the Free Pascal compiler +versions older than 3.0.0. RSJ is the new format of resource string table +files, created by the Free Pascal compiler version 3.0.0 or newer. @table @asis @item RPMs @@ -12292,7 +12297,7 @@ fpk fp-compiler @item File extension -@code{rst} +@code{rst}, @code{rsj} @item Extractor @code{xgettext}, @code{rstconv} diff --git a/gettext-tools/doc/xgettext.texi b/gettext-tools/doc/xgettext.texi index e2700d9f5..dc67fe6c8 100644 --- a/gettext-tools/doc/xgettext.texi +++ b/gettext-tools/doc/xgettext.texi @@ -73,8 +73,8 @@ are @code{C}, @code{C++}, @code{ObjectiveC}, @code{PO}, @code{Shell}, @code{Python}, @code{Lisp}, @code{EmacsLisp}, @code{librep}, @code{Scheme}, @code{Smalltalk}, @code{Java}, @code{JavaProperties}, @code{C#}, @code{awk}, @code{YCP}, @code{Tcl}, @code{Perl}, @code{PHP}, @code{GCC-source}, -@code{NXStringTable}, @code{RST}, @code{Glade}, @code{Lua}, @code{JavaScript}, -@code{Vala}, @code{GSettings}, @code{Desktop}. +@code{NXStringTable}, @code{RST}, @code{RSJ}, @code{Glade}, @code{Lua}, +@code{JavaScript}, @code{Vala}, @code{GSettings}, @code{Desktop}. @item -C @itemx --c++ diff --git a/gettext-tools/src/x-rst.c b/gettext-tools/src/x-rst.c index acaadf453..5a387a112 100644 --- a/gettext-tools/src/x-rst.c +++ b/gettext-tools/src/x-rst.c @@ -1,5 +1,5 @@ -/* xgettext RST backend. - Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. +/* xgettext RST/RSJ backend. + Copyright (C) 2001-2003, 2005-2009, 2015-2016, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2001. @@ -24,11 +24,13 @@ #include "x-rst.h" #include +#include #include #include #include #include "c-ctype.h" +#include "po-charset.h" #include "message.h" #include "xgettext.h" #include "error.h" @@ -51,8 +53,10 @@ This backend attempts to be functionally equivalent to the 'rstconv' program, part of the Free Pascal run time library, written by - Sebastian Guenther. Except that the locations are output as - "ModuleName.ConstName", not "ModuleName:ConstName". + Sebastian Guenther. Except that + * the locations are output as "ModuleName.ConstName", + not "ModuleName:ConstName", + * we add the flag '#, object-pascal-format' where appropriate. */ void @@ -234,3 +238,455 @@ extract_rst (FILE *f, real_filename); } } + + +/* RSJ stands for Resource String Table in JSON. + + An RSJ file is a JSON file that contains several string definitions. + It has the format (modulo whitespace) + { + "version": 1, + "strings": + [ + { + "hash": , + "name": , + "sourcebytes": [ ... ], + "value": + }, + ... + ] + } + The sourcebytes array contains the original source bytes, in the + source encoding (not guaranteed to be ISO-8859-1, see + ). + + This backend attempts to be functionally equivalent to the 'rstconv' + program, part of the Free Pascal run time library, written by + Sebastian Guenther. Except that + * we use the "value" as msgid, not the "sourcebytes", + * the locations are output as "ModuleName.ConstName", + not "ModuleName:ConstName", + * we add the flag '#, object-pascal-format' where appropriate. + */ + +/* For the JSON syntax, refer to RFC 8259. */ + +/* ======================== Reading of characters. ======================== */ + +/* Real filename, used in error messages about the input file. */ +static const char *real_file_name; + +/* Logical filename and line number, used to label the extracted messages. */ +static char *logical_file_name; +static int line_number; + +/* The input file stream. */ +static FILE *fp; + + +/* 1. line_number handling. */ + +static int +phase1_getc () +{ + int c = getc (fp); + + if (c == EOF) + { + if (ferror (fp)) + error (EXIT_FAILURE, errno, _("error while reading \"%s\""), + real_file_name); + return EOF; + } + + if (c == '\n') + line_number++; + + return c; +} + +/* Supports only one pushback character. */ +static void +phase1_ungetc (int c) +{ + if (c != EOF) + { + if (c == '\n') + --line_number; + + ungetc (c, fp); + } +} + + +/* 2. Skipping whitespace. */ + +/* Tests whether a phase1_getc() result is JSON whitespace. */ +static inline bool +is_whitespace (int c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static int +phase2_getc () +{ + int c; + + do + c = phase1_getc (); + while (is_whitespace (c)); + + return c; +} + +static void +phase2_ungetc (int c) +{ + phase1_ungetc (c); +} + + +/* ========================== Reading of tokens. ========================== */ + +/* Result of parsing a token. */ + +enum parse_result +{ + pr_parsed, /* successfully parsed */ + pr_none, /* the next token is of a different type */ + pr_syntax /* syntax error inside the token */ +}; + +static char *buffer; +static int bufmax; + +/* Parses an integer. Returns it in buffer, of length bufmax. + Returns pr_parsed or pr_none. */ +static enum parse_result +parse_integer () +{ + int c; + int bufpos; + + c = phase2_getc (); + bufpos = 0; + for (;;) + { + if (bufpos >= bufmax) + { + bufmax = 2 * bufmax + 10; + buffer = xrealloc (buffer, bufmax); + } + if (!(c >= '0' && c <= '9')) + break; + buffer[bufpos++] = c; + c = phase1_getc (); + } + phase1_ungetc (c); + buffer[bufpos] = '\0'; + return (bufpos == 0 ? pr_none : pr_parsed); +} + +static struct mixed_string_buffer *stringbuf; + +/* Parses a string. Returns it in stringbuf, in UTF-8 encoding. + Returns a parse_result. */ +static enum parse_result +parse_string () +{ + int c; + + c = phase2_getc (); + if (c != '"') + { + phase2_ungetc (c); + return pr_none; + } + stringbuf = mixed_string_buffer_alloc (lc_string, + logical_file_name, + line_number); + for (;;) + { + c = phase1_getc (); + /* Keep line_number in sync. */ + stringbuf->line_number = line_number; + if (c == EOF || (c >= 0 && c < 0x20)) + return pr_syntax; + if (c == '"') + break; + if (c == '\\') + { + c = phase1_getc (); + if (c == 'u') + { + unsigned int n = 0; + int i; + + for (i = 0; i < 4; i++) + { + c = phase1_getc (); + + if (c >= '0' && c <= '9') + n = (n << 4) + (c - '0'); + else if (c >= 'A' && c <= 'F') + n = (n << 4) + (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + n = (n << 4) + (c - 'a' + 10); + else + return pr_syntax; + } + mixed_string_buffer_append_unicode (stringbuf, n); + } + else + { + switch (c) + { + case '"': + case '\\': + case '/': + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + default: + return pr_syntax; + } + mixed_string_buffer_append_char (stringbuf, c); + } + } + else + mixed_string_buffer_append_char (stringbuf, c); + } + return pr_parsed; +} + +void +extract_rsj (FILE *f, + const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp) +{ + message_list_ty *mlp = mdlp->item[0]->messages; + int c; + + fp = f; + real_file_name = real_filename; + logical_file_name = xstrdup (logical_filename); + line_number = 1; + + /* JSON is always in UTF-8. */ + xgettext_current_source_encoding = po_charset_utf8; + + /* Parse the initial opening brace. */ + c = phase2_getc (); + if (c != '{') + goto invalid_json; + + c = phase2_getc (); + if (c != '}') + { + phase2_ungetc (c); + for (;;) + { + /* Parse a string. */ + char *s1; + if (parse_string () != pr_parsed) + goto invalid_json; + s1 = mixed_string_buffer_done (stringbuf); + + /* Parse a colon. */ + c = phase2_getc (); + if (c != ':') + goto invalid_json; + + if (strcmp (s1, "version") == 0) + { + /* Parse an integer. */ + if (parse_integer () != pr_parsed) + goto invalid_rsj; + if (strcmp (buffer, "1") != 0) + goto invalid_rsj_version; + } + else if (strcmp (s1, "strings") == 0) + { + /* Parse an array. */ + c = phase2_getc (); + if (c != '[') + goto invalid_rsj; + + c = phase2_getc (); + if (c != ']') + { + phase2_ungetc (c); + for (;;) + { + char *location = NULL; + char *msgid = NULL; + lex_pos_ty pos; + + /* Parse an object. */ + c = phase2_getc (); + if (c != '{') + goto invalid_rsj; + + c = phase2_getc (); + if (c != '}') + { + phase2_ungetc (c); + for (;;) + { + /* Parse a string. */ + char *s2; + if (parse_string () != pr_parsed) + goto invalid_json; + s2 = mixed_string_buffer_done (stringbuf); + + /* Parse a colon. */ + c = phase2_getc (); + if (c != ':') + goto invalid_json; + + if (strcmp (s2, "hash") == 0) + { + /* Parse an integer. */ + if (parse_integer () != pr_parsed) + goto invalid_rsj; + } + else if (strcmp (s2, "name") == 0) + { + /* Parse a string. */ + enum parse_result r = parse_string (); + if (r == pr_none) + goto invalid_rsj; + if (r == pr_syntax || location != NULL) + goto invalid_json; + location = mixed_string_buffer_done (stringbuf); + } + else if (strcmp (s2, "sourcebytes") == 0) + { + /* Parse an array. */ + c = phase2_getc (); + if (c != '[') + goto invalid_rsj; + + c = phase2_getc (); + if (c != ']') + { + phase2_ungetc (c); + for (;;) + { + /* Parse an integer. */ + if (parse_integer () != pr_parsed) + goto invalid_rsj; + + /* Parse a comma. */ + c = phase2_getc (); + if (c == ']') + break; + if (c != ',') + goto invalid_json; + } + } + } + else if (strcmp (s2, "value") == 0) + { + /* Parse a string. */ + enum parse_result r = parse_string (); + if (r == pr_none) + goto invalid_rsj; + if (r == pr_syntax || msgid != NULL) + goto invalid_json; + msgid = mixed_string_buffer_done (stringbuf); + } + else + goto invalid_rsj; + + free (s2); + + /* Parse a comma. */ + c = phase2_getc (); + if (c == '}') + break; + if (c != ',') + goto invalid_json; + } + } + + if (location == NULL || msgid == NULL) + goto invalid_rsj; + + pos.file_name = location; + pos.line_number = (size_t)(-1); + + remember_a_message (mlp, NULL, msgid, null_context, &pos, + NULL, NULL); + + /* Parse a comma. */ + c = phase2_getc (); + if (c == ']') + break; + if (c != ',') + goto invalid_json; + } + } + } + else + goto invalid_rsj; + + /* Parse a comma. */ + c = phase2_getc (); + if (c == '}') + break; + if (c != ',') + goto invalid_json; + } + } + + /* Seen the closing brace. */ + c = phase2_getc (); + if (c != EOF) + goto invalid_json; + + fp = NULL; + real_file_name = NULL; + logical_file_name = NULL; + line_number = 0; + + return; + + invalid_json: + error_with_progname = false; + error (EXIT_FAILURE, 0, _("%s:%d: invalid JSON syntax"), + logical_filename, line_number); + error_with_progname = true; + return; + + invalid_rsj: + error_with_progname = false; + error (EXIT_FAILURE, 0, _("%s:%d: invalid RSJ syntax"), + logical_filename, line_number); + error_with_progname = true; + return; + + invalid_rsj_version: + error_with_progname = false; + error (EXIT_FAILURE, 0, + _("%s:%d: invalid RSJ version. Only version 1 is supported."), + logical_filename, line_number); + error_with_progname = true; + return; +} diff --git a/gettext-tools/src/x-rst.h b/gettext-tools/src/x-rst.h index 751549830..657e70de4 100644 --- a/gettext-tools/src/x-rst.h +++ b/gettext-tools/src/x-rst.h @@ -1,5 +1,5 @@ -/* xgettext RST backend. - Copyright (C) 2001-2003, 2006, 2015-2016 Free Software Foundation, Inc. +/* xgettext RST/RSJ backend. + Copyright (C) 2001-2003, 2006, 2015-2016, 2018 Free Software Foundation, Inc. Written by Bruno Haible , 2001. This program is free software: you can redistribute it and/or modify @@ -29,10 +29,13 @@ extern "C" { #define EXTENSIONS_RST \ { "rst", "RST" }, \ + { "rsj", "RSJ" }, \ #define SCANNERS_RST \ { "RST", extract_rst, \ - NULL, &formatstring_pascal, NULL, NULL }, \ + NULL, &formatstring_pascal, NULL, NULL }, \ + { "RSJ", extract_rsj, \ + NULL, &formatstring_pascal, NULL, NULL }, \ /* Scan an RST file and add its translatable strings to mdlp. */ extern void extract_rst (FILE *fp, const char *real_filename, @@ -40,6 +43,12 @@ extern void extract_rst (FILE *fp, const char *real_filename, flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); +/* Scan an RSJ file and add its translatable strings to mdlp. */ +extern void extract_rsj (FILE *fp, const char *real_filename, + const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp); + #ifdef __cplusplus } diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index a80ee51ac..08b52b451 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -1,5 +1,5 @@ /* Extracts strings from C source file to Uniforum style .po file. - Copyright (C) 1995-1998, 2000-2016 Free Software Foundation, Inc. + Copyright (C) 1995-1998, 2000-2016, 2018 Free Software Foundation, Inc. Written by Ulrich Drepper , April 1995. This program is free software: you can redistribute it and/or modify @@ -1079,8 +1079,8 @@ Choice of input file language:\n")); (C, C++, ObjectiveC, PO, Shell, Python, Lisp,\n\ EmacsLisp, librep, Scheme, Smalltalk, Java,\n\ JavaProperties, C#, awk, YCP, Tcl, Perl, PHP,\n\ - GCC-source, NXStringTable, RST, Glade, Lua,\n\ - JavaScript, Vala, Desktop)\n")); + GCC-source, NXStringTable, RST, RSJ, Glade,\n\ + Lua, JavaScript, Vala, Desktop)\n")); printf (_("\ -C, --c++ shorthand for --language=C++\n")); printf (_("\ diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 11ed2c9bb..4f870d900 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -97,6 +97,7 @@ TESTS = gettext-1 gettext-2 gettext-3 gettext-4 gettext-5 gettext-6 gettext-7 \ xgettext-php-1 xgettext-php-2 xgettext-php-3 xgettext-php-4 \ xgettext-po-1 xgettext-po-2 \ xgettext-properties-1 \ + xgettext-rst-1 xgettext-rst-2 \ xgettext-python-1 xgettext-python-2 xgettext-python-3 \ xgettext-python-4 \ xgettext-scheme-1 xgettext-scheme-2 xgettext-scheme-3 \ diff --git a/gettext-tools/tests/lang-pascal b/gettext-tools/tests/lang-pascal index a6781e8d8..cd2e95715 100755 --- a/gettext-tools/tests/lang-pascal +++ b/gettext-tools/tests/lang-pascal @@ -34,7 +34,13 @@ EOF } : ${XGETTEXT=xgettext} -${XGETTEXT} -o pascalprog.tmp --omit-header --add-location pascalprog.rst || Exit 1 +# fpc 3.0.0 or newer produces a .rsj file instead of a .rst file. +if test -f pascalprog.rsj; then + suffix=rsj +else + suffix=rst +fi +${XGETTEXT} -o pascalprog.tmp --omit-header --add-location pascalprog.${suffix} || Exit 1 LC_ALL=C tr -d '\r' < pascalprog.tmp > pascalprog.pot || Exit 1 cat < pascalprog.ok diff --git a/gettext-tools/tests/xgettext-rst-1 b/gettext-tools/tests/xgettext-rst-1 new file mode 100644 index 000000000..be1efad7b --- /dev/null +++ b/gettext-tools/tests/xgettext-rst-1 @@ -0,0 +1,56 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test extractor of Free Pascal .rst files. + +cat <<\EOF > xg-rst-1.rst + +# hash value = 153469889 +hello.hello_world='Hello, world!' + + +# hash value = 1323310 +hello.running_as='This program is running as process number %d.' + +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} -o xg-rst-1.tmp xg-rst-1.rst || Exit 1 +# Don't simplify this to "grep ... < xg-rst-1.tmp", otherwise OpenBSD 4.0 grep +# only outputs "Binary file (standard input) matches". +cat xg-rst-1.tmp | grep -v 'POT-Creation-Date' | LC_ALL=C tr -d '\r' > xg-rst-1.po + +cat <<\EOF > xg-rst-1.ok +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: hello.hello_world +msgid "Hello, world!" +msgstr "" + +#: hello.running_as +#, object-pascal-format +msgid "This program is running as process number %d." +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-rst-1.ok xg-rst-1.po +result=$? + +exit $result diff --git a/gettext-tools/tests/xgettext-rst-2 b/gettext-tools/tests/xgettext-rst-2 new file mode 100644 index 000000000..a3bf3e717 --- /dev/null +++ b/gettext-tools/tests/xgettext-rst-2 @@ -0,0 +1,117 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test extractor of Free Pascal .rsj files. + +cat <<\EOF > xg-rst-2-oldstyle.pas +program oldstyle; + +resourcestring + testcase = 'Böse Bübchen ärgern über die Maßen'; + +begin +end. +EOF + +cat <<\EOF > xg-rst-2-hello.pas +program hello; +{$codepage utf8} + +resourcestring + hello_world = 'Hello, world!'; + running_as = 'This program is running as process number %d.'; + russian = 'Russian (Русский): Здравствуйте'; + vietnamese = 'Vietnamese (Tiếng Việt): Chào bạn'; + japanese = 'Japanese (日本語): こんにちは'; + thai = 'Thai (ภาษาไทย): สวัสดีครับ'; + { fpc 3.0.0 chokes on this: "Error: UTF-8 code greater than 65535 found" } + script = '𝒞'; + +begin +end. +EOF + +# Result of "iconv -f UTF-8 -t ISO-8859-1 < xg-rst-2-oldstyle.pas > oldstyle.pas ; ppcx64 oldstyle.pas" +cat <<\EOF > xg-rst-2-oldstyle.rsj +{"version":1,"strings":[ +{"hash":197750254,"name":"oldstyle.testcase","sourcebytes":[66,246,115,101,32,66,252,98,99,104,101,110,32,228,114,103,101,114,110,32,252,98,101,114,32,100,105,101,32,77,97,223,101,110],"value":"B\u00F6se B\u00FCbchen \u00E4rgern \u00FCber die Ma\u00DFen"} +]} +EOF + +# Expected result of "ppcx64 xg-rst-2-hello.pas" +cat <<\EOF > xg-rst-2-hello.rsj +{"version":1,"strings":[ +{"hash":153469889,"name":"hello.hello_world","sourcebytes":[72,101,108,108,111,44,32,119,111,114,108,100,33],"value":"Hello, world!"}, +{"hash":1323310,"name":"hello.running_as","sourcebytes":[84,104,105,115,32,112,114,111,103,114,97,109,32,105,115,32,114,117,110,110,105,110,103,32,97,115,32,112,114,111,99,101,115,115,32,110,117,109,98,101,114,32,37,100,46],"value":"This program is running as process number %d."}, +{"hash":8471413,"name":"hello.russian","sourcebytes":[82,117,115,115,105,97,110,32,40,208,160,209,131,209,129,209,129,208,186,208,184,208,185,41,58,32,208,151,208,180,209,128,208,176,208,178,209,129,209,130,208,178,209,131,208,185,209,130,208,181],"value":"Russian (\u0420\u0443\u0441\u0441\u043A\u0438\u0439): \u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435"}, +{"hash":12693150,"name":"hello.vietnamese","sourcebytes":[86,105,101,116,110,97,109,101,115,101,32,40,84,105,225,186,191,110,103,32,86,105,225,187,135,116,41,58,32,67,104,195,160,111,32,98,225,186,161,110],"value":"Vietnamese (Ti\u1EBFng Vi\u1EC7t): Ch\u00E0o b\u1EA1n"}, +{"hash":48190495,"name":"hello.japanese","sourcebytes":[74,97,112,97,110,101,115,101,32,40,230,151,165,230,156,172,232,170,158,41,58,32,227,129,147,227,130,147,227,129,171,227,129,161,227,129,175],"value":"Japanese (\u65E5\u672C\u8A9E): \u3053\u3093\u306B\u3061\u306F"}, +{"hash":121047034,"name":"hello.thai","sourcebytes":[84,104,97,105,32,40,224,184,160,224,184,178,224,184,169,224,184,178,224,185,132,224,184,151,224,184,162,41,58,32,224,184,170,224,184,167,224,184,177,224,184,170,224,184,148,224,184,181,224,184,132,224,184,163,224,184,177,224,184,154],"value":"Thai (\u0E20\u0E32\u0E29\u0E32\u0E44\u0E17\u0E22): \u0E2A\u0E27\u0E31\u0E2A\u0E14\u0E35\u0E04\u0E23\u0E31\u0E1A"}, +{"hash":123456789,"name":"hello.script","sourcebytes":[240,157,146,158],"value":"\uD835\uDC9E"} +]} +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} -o xg-rst-2.tmp xg-rst-2-oldstyle.rsj xg-rst-2-hello.rsj || Exit 1 +# Don't simplify this to "grep ... < xg-rst-2.tmp", otherwise OpenBSD 4.0 grep +# only outputs "Binary file (standard input) matches". +cat xg-rst-2.tmp | grep -v 'POT-Creation-Date' | LC_ALL=C tr -d '\r' > xg-rst-2.po + +cat <<\EOF > xg-rst-2.ok +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: oldstyle.testcase +msgid "Böse Bübchen ärgern über die Maßen" +msgstr "" + +#: hello.hello_world +msgid "Hello, world!" +msgstr "" + +#: hello.running_as +#, object-pascal-format +msgid "This program is running as process number %d." +msgstr "" + +#: hello.russian +msgid "Russian (Русский): Здравствуйте" +msgstr "" + +#: hello.vietnamese +msgid "Vietnamese (Tiếng Việt): Chào bạn" +msgstr "" + +#: hello.japanese +msgid "Japanese (日本語): こんにちは" +msgstr "" + +#: hello.thai +msgid "Thai (ภาษาไทย): สวัสดีครับ" +msgstr "" + +#: hello.script +msgid "𝒞" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-rst-2.ok xg-rst-2.po +result=$? + +exit $result