From: Bruno Haible Date: Tue, 28 Oct 2003 16:10:35 +0000 (+0000) Subject: Support for Qt message catalog format and Qt format strings. X-Git-Tag: v0.13~186 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f09ddb5c812f820b8d9f2cf521262fcbfc4732d;p=thirdparty%2Fgettext.git Support for Qt message catalog format and Qt format strings. --- diff --git a/NEWS b/NEWS index 506f075ec..7f19c8ef8 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,14 @@ Version 0.12.2 - September 2003 special kind of format strings used in the GCC sources and marks them as 'gcc-internal-format'. + - C++ with Qt: + + xgettext has a new option --qt that triggers the recognition and marking + of Qt format strings. + + msgfmt has a new option --qt that generates binary message catalogs in + Qt's .qm format. + * Data formats support: - Glade: diff --git a/gettext-tools/doc/ChangeLog b/gettext-tools/doc/ChangeLog index d38e66691..e8be67551 100644 --- a/gettext-tools/doc/ChangeLog +++ b/gettext-tools/doc/ChangeLog @@ -1,3 +1,10 @@ +2003-10-19 Bruno Haible + + * gettext.texi (PO Files): Mention qt-format. + (qt-format): New subsection. + * msgfmt.texi: Document --qt option. + * xgettext.texi: Likewise. + 2003-10-13 Bruno Haible * gettext.texi (PO Files): Mention objc-format, sh-format, perl-format, diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 2ae21aa17..6076ff72c 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -342,6 +342,7 @@ The Translator's View * perl-format:: Perl Format Strings * php-format:: PHP Format Strings * gcc-internal-format:: GCC internal Format Strings +* qt-format:: Qt Format Strings Individual Programming Languages @@ -1263,6 +1264,12 @@ Likewise for PHP, see @ref{php-format}. @kwindex no-gcc-internal-format@r{ flag} Likewise for the GCC sources, see @ref{gcc-internal-format}. +@item qt-format +@kwindex qt-format@r{ flag} +@itemx no-qt-format +@kwindex no-qt-format@r{ flag} +Likewise for Qt, see @ref{qt-format}. + @end table @kwindex msgid_plural @@ -7275,6 +7282,7 @@ strings. * perl-format:: Perl Format Strings * php-format:: PHP Format Strings * gcc-internal-format:: GCC internal Format Strings +* qt-format:: Qt Format Strings @end menu @node c-format, objc-format, Translators for other Languages, Translators for other Languages @@ -7437,7 +7445,7 @@ PHP format strings are described in the documentation of the PHP function @code{sprintf}, in @file{phpdoc/manual/function.sprintf.html} or @uref{http://www.php.net/manual/en/function.sprintf.php}. -@node gcc-internal-format, , php-format, Translators for other Languages +@node gcc-internal-format, qt-format, php-format, Translators for other Languages @subsection GCC internal Format Strings These format strings are used inside the GCC sources. In such a format @@ -7455,6 +7463,14 @@ denotes a programming language, @samp{O} denotes a binary operator, @samp{P} denotes a function parameter, @samp{Q} denotes an assignment operator, @samp{V} denotes a const/volatile qualifier. +@node qt-format, , gcc-internal-format, Translators for other Languages +@subsection Qt Format Strings + +Qt format strings are described in the documentation of the QString class +@uref{file:/usr/lib/qt-3.0.5/doc/html/qstring.html}. +In summary, a directive consists of a @samp{%} followed by a digit. The same +directive cannot occur more than once in a format string. + @node Maintainers for other Languages, List of Programming Languages, Translators for other Languages, Programming Languages @section The Maintainer's View diff --git a/gettext-tools/doc/msgfmt.texi b/gettext-tools/doc/msgfmt.texi index 821ca8855..b072443a3 100644 --- a/gettext-tools/doc/msgfmt.texi +++ b/gettext-tools/doc/msgfmt.texi @@ -44,6 +44,11 @@ Like --java, and assume Java2 (JDK 1.2 or higher). @cindex Tcl mode, and @code{msgfmt} program Tcl mode: generate a tcl/msgcat @file{.msg} file. +@item --qt +@opindex --qt@r{, @code{msgfmt} option} +@cindex Qt mode, and @code{msgfmt} program +Qt mode: generate a Qt @file{.qm} file. + @end table @subsection Output file location diff --git a/gettext-tools/doc/xgettext.texi b/gettext-tools/doc/xgettext.texi index 2c84ee6e3..5204b1f70 100644 --- a/gettext-tools/doc/xgettext.texi +++ b/gettext-tools/doc/xgettext.texi @@ -215,7 +215,14 @@ Understand ANSI C trigraphs for input. @* This option has an effect only with the languages C, C++, ObjectiveC. -@itemx --debug +@item --qt +@opindex --qt@r{, @code{xgettext} option} +@cindex Qt format strings +Recognize Qt format strings. +@* +This option has an effect only with the language C++. + +@item --debug @opindex --debug@r{, @code{xgettext} option} @cindex debugging messages marked as format strings Use the flags @code{c-format} and @code{possible-c-format} to show who was diff --git a/gettext-tools/po/ChangeLog b/gettext-tools/po/ChangeLog index 8ccd55389..dc675b62e 100644 --- a/gettext-tools/po/ChangeLog +++ b/gettext-tools/po/ChangeLog @@ -1,3 +1,7 @@ +2003-10-19 Bruno Haible + + * POTFILES.in: Add src/format-qt.c and src/write-qt.c. + 2003-10-14 Bruno Haible * POTFILES.in: Add src/read-stringtable.c. diff --git a/gettext-tools/po/POTFILES.in b/gettext-tools/po/POTFILES.in index c35255253..6c2eba26a 100644 --- a/gettext-tools/po/POTFILES.in +++ b/gettext-tools/po/POTFILES.in @@ -38,6 +38,7 @@ src/format-perl.c src/format-perl-brace.c src/format-php.c src/format-python.c +src/format-qt.c src/format-sh.c src/format-tcl.c src/format-ycp.c @@ -74,6 +75,7 @@ src/urlget.c src/write-java.c src/write-mo.c src/write-po.c +src/write-qt.c src/write-tcl.c src/x-awk.c src/x-c.c diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 496c6c799..8b58adfd2 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,40 @@ +2003-10-19 Bruno Haible + + * message.h (format_type): New enum value 'format_qt'. + (NFORMATS): Increment. + * message.c (format_language): Add format_qt entry. + (format_language_pretty): Likewise. + * format.h (formatstring_qt): New declaration. + * format-qt.c: New file. + * format.c (formatstring_parsers): Add formatstring_qt. + * xgettext.c (recognize_format_qt): New variable. + (long_options): Add option "--qt". + (main): Handle --qt option. Determine the extractor only after option + processing is complete. + (usage): Document --qt option. + (xgettext_record_flag): Handle format_qt. + (language_to_extractor): For C++, return a different extractor when + --qt was specified. + * write-qt.h: New file. + * write-qt.c: New file. + * msgfmt.c: Include write-qt.h. + (qt_mode): New variable. + (long_options): Add option "--qt". + (main): Handle --qt option. More checks for contradicting options. + Call msgdomain_write_qt. + (usage): Mention Qt mode. + (format_directive_domain): Ignore domain directive if in Qt mode. + * Makefile.am (noinst_HEADERS): Add write-qt.h. + (FORMAT_SOURCE): Add format-qt.c. + (msgfmt_SOURCES): Add write-qt.c. + * Makefile.msvc (OBJECTS): Add format-qt.obj. + (msgfmt_OBJECTS): Add write-qt.obj. + (format-qt.obj, write-qt.obj): New rules. + * Makefile.vms (OBJECTS): Add format-qt.obj. + (msgfmt_OBJECTS): Add write-qt.obj. + (format-qt.obj, write-qt.obj): New rules. + * FILES: Update. + 2003-10-14 Bruno Haible Fix compilation errors in C++ mode. diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES index e67a6a746..677132a0a 100644 --- a/gettext-tools/src/FILES +++ b/gettext-tools/src/FILES @@ -196,6 +196,7 @@ format-perl.c Format string handling for Perl. format-perl-brace.c Format string handling for Perl, braced syntax. format-php.c Format string handling for PHP. format-gcc-internal.c Format string handling GCC internal. +format-qt.c Format string handling for Qt. format.c Table of the language dependent format string handlers. +-------------- The 'msgfmt' program @@ -214,6 +215,9 @@ format.c Table of the language dependent format string handlers. | write-tcl.h | write-tcl.c | Generating Tcl .msg files. +| write-qt.h +| write-qt.c +| Generating Qt .qm files. | msgfmt.c | Main source for the 'msgfmt' program. | diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index fc8cae11c..11ce1577c 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -40,10 +40,10 @@ str-list.h write-po.h write-properties.h write-stringtable.h dir-list.h \ file-list.h po-gram-gen.h po-gram-gen2.h po-hash-gen.h msgl-charset.h \ msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h msgl-english.h msgfmt.h \ msgunfmt.h plural-count.h read-mo.h write-mo.h read-java.h write-java.h \ -read-tcl.h write-tcl.h po-time.h plural-table.h format.h xgettext.h x-c.h \ -x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h x-smalltalk.h x-java.h \ -x-properties.h x-awk.h x-ycp.h x-tcl.h x-perl.h x-php.h x-stringtable.h \ -x-rst.h x-glade.h +read-tcl.h write-tcl.h write-qt.h po-time.h plural-table.h format.h \ +xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \ +x-smalltalk.h x-java.h x-properties.h x-awk.h x-ycp.h x-tcl.h x-perl.h \ +x-php.h x-stringtable.h x-rst.h x-glade.h EXTRA_DIST += FILES project-id ChangeLog.0 @@ -95,7 +95,7 @@ FORMAT_SOURCE = format.c format-invalid.h \ format-c.c format-sh.c format-python.c format-lisp.c format-elisp.c \ format-librep.c format-java.c format-awk.c format-pascal.c format-ycp.c \ format-tcl.c format-perl.c format-perl-brace.c format-php.c \ -format-gcc-internal.c +format-gcc-internal.c format-qt.c # libgettextsrc contains all code that is needed by at least two programs. libgettextsrc_la_SOURCES = \ @@ -116,7 +116,8 @@ LIBUNINAME = ../libuniname/libuniname.a # Source dependencies. msgcmp_SOURCES = msgcmp.c -msgfmt_SOURCES = msgfmt.c write-mo.c write-java.c write-tcl.c plural-eval.c +msgfmt_SOURCES = msgfmt.c write-mo.c write-java.c write-tcl.c write-qt.c \ + plural-eval.c msgmerge_SOURCES = msgmerge.c plural-count.c msgunfmt_SOURCES = msgunfmt.c read-mo.c read-java.c read-tcl.c xgettext_SOURCES = xgettext.c \ diff --git a/gettext-tools/src/Makefile.msvc b/gettext-tools/src/Makefile.msvc index d9c2f922e..d17bfb580 100644 --- a/gettext-tools/src/Makefile.msvc +++ b/gettext-tools/src/Makefile.msvc @@ -145,10 +145,11 @@ OBJECTS = \ format-perl.obj \ format-perl-brace.obj \ format-php.obj \ - format-gcc-internal.obj + format-gcc-internal.obj \ + format-qt.obj msgcmp_OBJECTS = msgcmp.obj -msgfmt_OBJECTS = msgfmt.obj write-mo.obj write-java.obj write-tcl.obj plural-eval.obj +msgfmt_OBJECTS = msgfmt.obj write-mo.obj write-java.obj write-tcl.obj write-qt.obj plural-eval.obj msgmerge_OBJECTS = msgmerge.obj plural-count.obj msgunfmt_OBJECTS = msgunfmt.obj read-mo.obj read-java.obj read-tcl.obj xgettext_OBJECTS = xgettext.obj x-c.obj x-po.obj x-sh.obj x-python.obj x-lisp.obj x-elisp.obj x-librep.obj x-smalltalk.obj x-java.obj x-awk.obj x-ycp.obj x-tcl.obj x-perl.obj x-php.obj x-rst.obj x-glade.obj @@ -293,6 +294,9 @@ format-php.obj : format-php.c format-gcc-internal.obj : format-gcc-internal.c $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c format-gcc-internal.c +format-qt.obj : format-qt.c + $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c format-qt.c + !if !$(DLL) gettextsrc.lib : $(OBJECTS) @@ -327,6 +331,9 @@ write-java.obj : write-java.c write-tcl.obj : write-tcl.c $(CC) $(INCLUDES) $(CFLAGS) -c write-tcl.c +write-qt.obj : write-qt.c + $(CC) $(INCLUDES) $(CFLAGS) -c write-qt.c + plural-eval.obj : plural-eval.c $(CC) $(INCLUDES) $(CFLAGS) -c plural-eval.c diff --git a/gettext-tools/src/Makefile.vms b/gettext-tools/src/Makefile.vms index 2282c7fbc..8344f4a8b 100644 --- a/gettext-tools/src/Makefile.vms +++ b/gettext-tools/src/Makefile.vms @@ -91,10 +91,11 @@ OBJECTS = \ format-perl.obj, \ format-perl-brace.obj, \ format-php.obj, \ - format-gcc-internal.obj + format-gcc-internal.obj \ + format-qt.obj msgcmp_OBJECTS = msgcmp.obj -msgfmt_OBJECTS = msgfmt.obj, write-mo.obj, write-java.obj, write-tcl.obj, plural-eval.obj +msgfmt_OBJECTS = msgfmt.obj, write-mo.obj, write-java.obj, write-tcl.obj, write-qt.obj, plural-eval.obj msgmerge_OBJECTS = msgmerge.obj, plural-count.obj msgunfmt_OBJECTS = msgunfmt.obj, read-mo.obj, read-java.obj, read-tcl.obj xgettext_OBJECTS = xgettext.obj, x-c.obj, x-po.obj, x-sh.obj, x-python.obj, x-lisp.obj, x-elisp.obj, x-librep.obj, x-smalltalk.obj, x-java.obj, x-awk.obj, x-ycp.obj, x-tcl.obj, x-perl.obj, x-php.obj, x-rst.obj, x-glade.obj @@ -237,6 +238,9 @@ format-php.obj : format-php.c format-gcc-internal.obj : format-gcc-internal.c $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) format-gcc-internal.c +format-qt.obj : format-qt.c + $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) format-qt.c + gettextsrc.olb : $(OBJECTS) $(AR) $(AR_FLAGS) gettextsrc.olb $(OBJECTS) @@ -257,6 +261,9 @@ write-java.obj : write-java.c write-tcl.obj : write-tcl.c $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-tcl.c +write-qt.obj : write-qt.c + $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-qt.c + plural-eval.obj : plural-eval.c $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) plural-eval.c diff --git a/gettext-tools/src/format-qt.c b/gettext-tools/src/format-qt.c new file mode 100644 index 000000000..715d10be5 --- /dev/null +++ b/gettext-tools/src/format-qt.c @@ -0,0 +1,230 @@ +/* Qt format strings. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "format.h" +#include "xalloc.h" +#include "xerror.h" +#include "error.h" +#include "error-progname.h" +#include "gettext.h" + +#define _(str) gettext (str) + +/* Qt format strings are processed by QString::arg and are documented in + qt-3.0.5/doc/html/qstring.html. + A directive starts with '%' and is followed by a digit ('0' to '9'). + Each %n must occur only once in the given string. + The first .arg() invocation replaces the %n with the lowest numbered n, + the next .arg() invocation then replaces the %n with the second-lowest + numbered n, and so on. + (This is inherently buggy because a '%' in the first replacement confuses + the second .arg() invocation.) + Although %0 is supported, usually %1 denotes the first argument, %2 the + second argument etc. */ + +struct spec +{ + unsigned int directives; + unsigned int arg_count; + bool args_used[10]; +}; + + +static void * +format_parse (const char *format, char **invalid_reason) +{ + struct spec spec; + struct spec *result; + + spec.directives = 0; + spec.arg_count = 0; + + for (; *format != '\0';) + if (*format++ == '%') + if (*format >= '0' && *format <= '9') + { + /* A directive. */ + unsigned int number; + + spec.directives++; + + number = *format - '0'; + + while (spec.arg_count <= number) + spec.args_used[spec.arg_count++] = false; + if (spec.args_used[number]) + { + *invalid_reason = + xasprintf (_("Multiple references to %%%c."), *format); + goto bad_format; + } + spec.args_used[number] = true; + + format++; + } + + result = (struct spec *) xmalloc (sizeof (struct spec)); + *result = spec; + return result; + + bad_format: + return NULL; +} + +static void +format_free (void *descr) +{ + struct spec *spec = (struct spec *) descr; + + free (spec); +} + +static int +format_get_number_of_directives (void *descr) +{ + struct spec *spec = (struct spec *) descr; + + return spec->directives; +} + +static bool +format_check (const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr, + bool equality, bool noisy, const char *pretty_msgstr) +{ + struct spec *spec1 = (struct spec *) msgid_descr; + struct spec *spec2 = (struct spec *) msgstr_descr; + bool err = false; + unsigned int i; + + for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++) + { + bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]); + bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]); + + /* The translator cannot omit a %n from the msgstr because that would + yield a "Argument missing" warning at runtime. */ + if (arg_used1 != arg_used2) + { + if (noisy) + { + error_with_progname = false; + error_at_line (0, 0, pos->file_name, pos->line_number, + arg_used1 + ? _("a format specification for argument %u doesn't exist in '%s'") + : _("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"), + i, pretty_msgstr); + error_with_progname = true; + } + err = true; + break; + } + } + + return err; +} + + +struct formatstring_parser formatstring_qt = +{ + format_parse, + format_free, + format_get_number_of_directives, + format_check +}; + + +#ifdef TEST + +/* Test program: Print the argument list specification returned by + format_parse for strings read from standard input. */ + +#include +#include "getline.h" + +static void +format_print (void *descr) +{ + struct spec *spec = (struct spec *) descr; + unsigned int i; + + if (spec == NULL) + { + printf ("INVALID"); + return; + } + + printf ("("); + for (i = 0; i < spec->arg_count; i++) + { + if (i > 0) + printf (" "); + if (spec->args_used[i]) + printf ("*"); + else + printf ("_"); + } + printf (")"); +} + +int +main () +{ + for (;;) + { + char *line = NULL; + size_t line_size = 0; + int line_len; + char *invalid_reason; + void *descr; + + line_len = getline (&line, &line_size, stdin); + if (line_len < 0) + break; + if (line_len > 0 && line[line_len - 1] == '\n') + line[--line_len] = '\0'; + + invalid_reason = NULL; + descr = format_parse (line, &invalid_reason); + + format_print (descr); + printf ("\n"); + if (descr == NULL) + printf ("%s\n", invalid_reason); + + free (invalid_reason); + free (line); + } + + return 0; +} + +/* + * For Emacs M-x compile + * Local Variables: + * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt.c ../lib/libgettextlib.la" + * End: + */ + +#endif /* TEST */ diff --git a/gettext-tools/src/format.c b/gettext-tools/src/format.c index 5b7edfcf3..82a439379 100644 --- a/gettext-tools/src/format.c +++ b/gettext-tools/src/format.c @@ -42,5 +42,6 @@ struct formatstring_parser *formatstring_parsers[NFORMATS] = /* format_perl */ &formatstring_perl, /* format_perl_brace */ &formatstring_perl_brace, /* format_php */ &formatstring_php, - /* format_gcc_internal */ &formatstring_gcc_internal + /* format_gcc_internal */ &formatstring_gcc_internal, + /* format_qt */ &formatstring_qt }; diff --git a/gettext-tools/src/format.h b/gettext-tools/src/format.h index 004ff8aef..180c99a05 100644 --- a/gettext-tools/src/format.h +++ b/gettext-tools/src/format.h @@ -78,6 +78,7 @@ extern DLL_VARIABLE struct formatstring_parser formatstring_perl; extern DLL_VARIABLE struct formatstring_parser formatstring_perl_brace; extern DLL_VARIABLE struct formatstring_parser formatstring_php; extern DLL_VARIABLE struct formatstring_parser formatstring_gcc_internal; +extern DLL_VARIABLE struct formatstring_parser formatstring_qt; /* Table of all format string parsers. */ extern DLL_VARIABLE struct formatstring_parser *formatstring_parsers[NFORMATS]; diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c index 17de7225d..e168b57e1 100644 --- a/gettext-tools/src/message.c +++ b/gettext-tools/src/message.c @@ -50,7 +50,8 @@ const char *const format_language[NFORMATS] = /* format_perl */ "perl", /* format_perl_brace */ "perl-brace", /* format_php */ "php", - /* format_gcc_internal */ "gcc-internal" + /* format_gcc_internal */ "gcc-internal", + /* format_qt */ "qt" }; const char *const format_language_pretty[NFORMATS] = @@ -71,7 +72,8 @@ const char *const format_language_pretty[NFORMATS] = /* format_perl */ "Perl", /* format_perl_brace */ "Perl brace", /* format_php */ "PHP", - /* format_gcc_internal */ "GCC internal" + /* format_gcc_internal */ "GCC internal", + /* format_qt */ "Qt" }; diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h index b6d6ff595..085fd3754 100644 --- a/gettext-tools/src/message.h +++ b/gettext-tools/src/message.h @@ -56,9 +56,10 @@ enum format_type format_perl, format_perl_brace, format_php, - format_gcc_internal + format_gcc_internal, + format_qt }; -#define NFORMATS 17 /* Number of format_type enum values. */ +#define NFORMATS 18 /* Number of format_type enum values. */ extern DLL_VARIABLE const char *const format_language[NFORMATS]; extern DLL_VARIABLE const char *const format_language_pretty[NFORMATS]; diff --git a/gettext-tools/src/msgfmt.c b/gettext-tools/src/msgfmt.c index a30b98a05..25931370f 100644 --- a/gettext-tools/src/msgfmt.c +++ b/gettext-tools/src/msgfmt.c @@ -49,6 +49,7 @@ #include "write-mo.h" #include "write-java.h" #include "write-tcl.h" +#include "write-qt.h" #include "gettext.h" #include "message.h" @@ -95,6 +96,9 @@ static bool tcl_mode; static const char *tcl_locale_name; static const char *tcl_base_directory; +/* Qt mode output file specification. */ +static bool qt_mode; + /* We may have more than one input file. Domains with same names in different files have to merged. So we need a list of tables for each output file. */ @@ -164,6 +168,7 @@ static const struct option long_options[] = { "no-hash", no_argument, NULL, CHAR_MAX + 6 }, { "output-file", required_argument, NULL, 'o' }, { "properties-input", no_argument, NULL, 'P' }, + { "qt", no_argument, NULL, CHAR_MAX + 9 }, { "resource", required_argument, NULL, 'r' }, { "statistics", no_argument, &do_statistics, 1 }, { "strict", no_argument, NULL, 'S' }, @@ -317,6 +322,9 @@ main (int argc, char *argv[]) case CHAR_MAX + 8: /* --stringtable-input */ input_syntax = syntax_stringtable; break; + case CHAR_MAX + 9: /* --qt */ + qt_mode = true; + break; default: usage (EXIT_FAILURE); break; @@ -351,6 +359,12 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ if (java_mode && tcl_mode) error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), "--java", "--tcl"); + if (java_mode && qt_mode) + error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), + "--java", "--qt"); + if (tcl_mode && qt_mode) + error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), + "--tcl", "--qt"); if (java_mode) { if (output_file_name != NULL) @@ -415,8 +429,8 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ if (output_file_name != NULL) current_domain = new_domain (output_file_name, - strict_uniforum ? add_mo_suffix (output_file_name) - : output_file_name); + !qt_mode && strict_uniforum ? add_mo_suffix (output_file_name) + : output_file_name); /* Process all given .po files. */ while (argc > optind) @@ -465,6 +479,12 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ tcl_locale_name, tcl_base_directory)) exit_status = EXIT_FAILURE; } + else if (qt_mode) + { + if (msgdomain_write_qt (domain->mlp, canon_encoding, + domain->domain_name, domain->file_name)) + exit_status = EXIT_FAILURE; + } else { if (msgdomain_write_mo (domain->mlp, domain->domain_name, @@ -541,6 +561,8 @@ Operation mode:\n")); --java2 like --java, and assume Java2 (JDK 1.2 or higher)\n")); printf (_("\ --tcl Tcl mode: generate a tcl/msgcat .msg file\n")); + printf (_("\ + --qt Qt mode: generate a Qt .qm file\n")); printf ("\n"); printf (_("\ Output file location:\n")); @@ -1391,7 +1413,7 @@ msgfmt_set_domain (default_po_reader_ty *this, char *name) { /* If no output file was given, we change it with each `domain' directive. */ - if (!java_mode && !tcl_mode && output_file_name == NULL) + if (!java_mode && !tcl_mode && !qt_mode && output_file_name == NULL) { size_t correct; diff --git a/gettext-tools/src/write-qt.c b/gettext-tools/src/write-qt.c new file mode 100644 index 000000000..95fabe350 --- /dev/null +++ b/gettext-tools/src/write-qt.c @@ -0,0 +1,539 @@ +/* Writing Qt .qm files. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "write-qt.h" + +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "xerror.h" +#include "message.h" +#include "po-charset.h" +#include "msgl-iconv.h" +#include "hash-string.h" +#include "utf8-ucs4.h" +#include "xalloc.h" +#include "obstack.h" +#include "binary-io.h" +#include "fwriteerror.h" +#include "exit.h" +#include "gettext.h" + +#define _(str) gettext (str) + +/* Qt .qm files are read by the QTranslator::load() function and written + by the Qt QTranslator::save() function. + + The Qt tool 'msg2qm' uses the latter function and can convert PO files + to .qm files. But since 'msg2qm' is marked as an "old" tool in Qt 3.0.5's + i18n.html documentation and therefore likely to disappear, we provide the + same functionality here. + + The format of .qm files, as reverse engineered from the functions + QTranslator::save(const QString& filename, SaveMode mode) + QTranslator::squeeze(SaveMode mode) + QTranslatorMessage::write(QDataStream& stream, bool strip, Prefix prefix) + elfHash(const char* name) + in qt-3.0.5, is as follows: + + It's a binary data format. Elements are u8 (byte), u16, u32. They are + written in big-endian order. + + The file starts with a magic string of 16 bytes: + 3C B8 64 18 CA EF 9C 95 CD 21 1C BF 60 A1 BD DD + + Then come three sections. Each of the three sections is optional. Each + has this structure: + struct { + u8 section_type; // 0x42 = hashes, 0x69 = messages, 0x2f = contexts + u32 length; // number of bytes of the data + u8 data[length]; + }; + + In the first section, the hashes section, the data has the following + structure: + It's a sorted array of + struct { + u32 hashcode; // elfHash of the concatenation of msgid and + // disambiguating-comment + u32 offset; // offset within the data[] of the messages section + }; + It's sorted in ascending order by hashcode as primary sorting criteria + and - when the hashcodes are the same - by offset as secondary criteria. + + In the second section, the messages section, the data has the following + structure: + It's a sequence of records, each representing a message, in no + particular order. Each record is a sequence of subsections, each + introduced by a particular subsection tag. The possible subsection tags + are (and they usually occur in this order): + - 03: Translation. Followed by the msgstr in UCS-2 or UTF-16 format: + struct { + u32 length; + u16 chars[length/2]; + }; + - 08: Disambiguating-comment. Followed by the NUL-terminated, + ISO-8859-1 encoded, disambiguating-comment string: + struct { + u32 length; // number of bytes including the NUL at the end + u8 chars[length]; + }; + - 06: SourceText, i.e. msgid. Followed by the NUL-terminated, + ISO-8859-1 encoded, msgid: + struct { + u32 length; // number of bytes including the NUL at the end + u8 chars[length]; + }; + - 02: SourceText16, i.e. msgid. Encoded as UCS-2, but must actually + be ISO-8859-1. + struct { + u32 length; + u16 chars[length/2]; + }; + This subsection tag is obsoleted by SourceText. + - 07: Context. Followed by the NUL-terminated, ISO-8859-1 encoded, + context string (usually a C++ class name or empty): + struct { + u32 length; // number of bytes including the NUL at the end + u8 chars[length]; + }; + - 04: Context16. Encoded as UCS-2, but must actually be ISO-8859-1. + struct { + u32 length; + u16 chars[length/2]; + }; + This subsection tag is obsoleted by Context. + - 05: Hash. Followed by + struct { + u32 hashcode; // elfHash of the concatenation of msgid and + // disambiguating-comment + }; + - 01: End. Designates the end of the record. No further data. + Usually the following subsections are written, but some of them are + optional: + - 03: Translation. + - 08: Disambiguating-comment (optional). + - 06: SourceText (optional). + - 07: Context (optional). + - 05: Hash. + - 01: End. + A subsection can be omitted if the value to be output is the same as + for the previous record. + + In the third section, the contexts section, the data contains a hash + table. Quite complicated. + + The elfHash function is the same as our hash_string function, except that + at the end it maps a hash code of 0x00000000 to 0x00000001. + + When we convert from PO file format, all disambiguating-comments and + contexts are empty, and therefore the contexts section can be omitted. */ + + +/* Write a u8 (a single byte) to the output stream. */ +static inline void +write_u8 (FILE *output_file, unsigned char value) +{ + putc (value, output_file); +} + +/* Write a u16 (two bytes) to the output stream. */ +static inline void +write_u16 (FILE *output_file, unsigned short value) +{ + unsigned char data[2]; + + data[0] = (value >> 8) & 0xff; + data[1] = value & 0xff; + + fwrite (data, 2, 1, output_file); +} + +/* Write a u32 (four bytes) to the output stream. */ +static inline void +write_u32 (FILE *output_file, unsigned int value) +{ + unsigned char data[4]; + + data[0] = (value >> 24) & 0xff; + data[1] = (value >> 16) & 0xff; + data[2] = (value >> 8) & 0xff; + data[3] = value & 0xff; + + fwrite (data, 4, 1, output_file); +} + + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +/* Add a u8 (a single byte) to an obstack. */ +static void +append_u8 (struct obstack *mempool, unsigned char value) +{ + unsigned char data[1]; + + data[0] = value; + + obstack_grow (mempool, data, 1); +} + +/* Add a u16 (two bytes) to an obstack. */ +static void +append_u16 (struct obstack *mempool, unsigned short value) +{ + unsigned char data[2]; + + data[0] = (value >> 8) & 0xff; + data[1] = value & 0xff; + + obstack_grow (mempool, data, 2); +} + +/* Add a u32 (four bytes) to an obstack. */ +static void +append_u32 (struct obstack *mempool, unsigned int value) +{ + unsigned char data[4]; + + data[0] = (value >> 24) & 0xff; + data[1] = (value >> 16) & 0xff; + data[2] = (value >> 8) & 0xff; + data[3] = value & 0xff; + + obstack_grow (mempool, data, 4); +} + +/* Add an ISO-8859-1 encoded string to an obstack. */ +static void +append_base_string (struct obstack *mempool, const char *string) +{ + size_t length = strlen (string) + 1; + append_u32 (mempool, length); + obstack_grow (mempool, string, length); +} + +/* Add an UTF-16 encoded string to an obstack. */ +static void +append_unicode_string (struct obstack *mempool, const unsigned short *string, + size_t length) +{ + append_u32 (mempool, length * 2); + for (; length > 0; string++, length--) + append_u16 (mempool, *string); +} + +/* Retrieve a 4-byte integer from memory. */ +static inline unsigned int +peek_u32 (const unsigned char *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +/* Convert an UTF-8 string to ISO-8859-1, without error checking. */ +static char * +conv_to_iso_8859_1 (const char *string) +{ + size_t length = strlen (string); + const char *str = string; + const char *str_limit = string + length; + /* Conversion to ISO-8859-1 can only reduce the number of bytes. */ + char *result = (char *) xmalloc (length + 1); + char *q = result; + + while (str < str_limit) + { + unsigned int uc; + str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str); + /* It has already been verified that the string its in ISO-8859-1. */ + if (!(uc < 0x100)) + abort (); + /* Store as ISO-8859-1. */ + *q++ = (unsigned char) uc; + } + *q = '\0'; + assert (q - result <= length); + + return result; +} + +/* Convert an UTF-8 string to UTF-16, returning its size (number of UTF-16 + codepoints) in *SIZEP. */ +static unsigned short * +conv_to_utf16 (const char *string, size_t *sizep) +{ + size_t length = strlen (string); + const char *str = string; + const char *str_limit = string + length; + /* Conversion to UTF-16 can at most double the number of bytes. */ + unsigned short *result = (unsigned short *) xmalloc (2 * length); + unsigned short *q = result; + + while (str < str_limit) + { + unsigned int uc; + str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str); + if (uc < 0x10000) + /* UCS-2 character. */ + *q++ = (unsigned short) uc; + else + { + /* UTF-16 surrogate. */ + *q++ = 0xd800 + ((uc - 0x10000) >> 10); + *q++ = 0xdc00 + ((uc - 0x10000) & 0x3ff); + } + } + assert (q - result <= 2 * length); + + *sizep = q - result; + return result; +} + +/* Return the Qt hash code of a string. */ +static unsigned int +string_hashcode (const char *str) +{ + unsigned int h; + + h = hash_string (str); + if (h == 0) + h = 1; + return h; +} + +/* Compare two entries of the hashes section. */ +static int +cmp_hashes (const void *va, const void *vb) +{ + const unsigned char *a = (const unsigned char *) va; + const unsigned char *b = (const unsigned char *) vb; + unsigned int a_hashcode = peek_u32 (a); + unsigned int b_hashcode = peek_u32 (b); + + if (a_hashcode != b_hashcode) + return (a_hashcode >= b_hashcode ? 1 : -1); + else + { + unsigned int a_offset = peek_u32 (a + 4); + unsigned int b_offset = peek_u32 (b + 4); + + if (a_offset != b_offset) + return (a_offset >= b_offset ? 1 : -1); + else + return 0; + } +} + + +/* Write a section to the output stream. */ +static void +write_section (FILE *output_file, unsigned char tag, void *data, size_t size) +{ + /* A section can be omitted if it is empty. */ + if (size > 0) + { + write_u8 (output_file, tag); + write_u32 (output_file, size); + fwrite (data, size, 1, output_file); + } +} + + +/* Write an entire .qm file. */ +static void +write_qm (FILE *output_file, message_list_ty *mlp) +{ + static unsigned char magic[16] = + { + 0x3C, 0xB8, 0x64, 0x18, 0xCA, 0xEF, 0x9C, 0x95, + 0xCD, 0x21, 0x1C, 0xBF, 0x60, 0xA1, 0xBD, 0xDD + }; + struct obstack hashes_pool; + struct obstack messages_pool; + size_t j; + + obstack_init (&hashes_pool); + obstack_init (&messages_pool); + + /* Prepare the hashes section and the messages section. */ + for (j = 0; j < mlp->nitems; j++) + { + message_ty *mp = mlp->item[j]; + + /* No need to emit the header entry, it's not needed at runtime. */ + if (mp->msgid[0] != '\0') + { + char *msgid_as_iso_8859_1 = conv_to_iso_8859_1 (mp->msgid); + size_t msgstr_len; + unsigned short *msgstr_as_utf16 = + conv_to_utf16 (mp->msgstr, &msgstr_len); + unsigned int hashcode = string_hashcode (msgid_as_iso_8859_1); + unsigned int offset = obstack_object_size (&messages_pool); + + /* Add a record to the hashes section. */ + append_u32 (&hashes_pool, hashcode); + append_u32 (&hashes_pool, offset); + + /* Add a record to the messages section. */ + + append_u8 (&messages_pool, 0x03); + append_unicode_string (&messages_pool, msgstr_as_utf16, msgstr_len); + + append_u8 (&messages_pool, 0x08); + append_base_string (&messages_pool, ""); + + append_u8 (&messages_pool, 0x06); + append_base_string (&messages_pool, msgid_as_iso_8859_1); + + append_u8 (&messages_pool, 0x07); + append_base_string (&messages_pool, ""); + + append_u8 (&messages_pool, 0x05); + append_u32 (&messages_pool, hashcode); + + append_u8 (&messages_pool, 0x01); + + free (msgstr_as_utf16); + free (msgid_as_iso_8859_1); + } + } + + /* Sort the hashes section. */ + { + size_t nstrings = obstack_object_size (&hashes_pool) / 8; + if (nstrings > 0) + qsort (obstack_base (&hashes_pool), nstrings, 8, cmp_hashes); + } + + /* Write the magic number. */ + fwrite (magic, sizeof (magic), 1, output_file); + + /* Write the hashes section. */ + write_section (output_file, 0x42, obstack_base (&hashes_pool), + obstack_object_size (&hashes_pool)); + + /* Write the messages section. */ + write_section (output_file, 0x69, obstack_base (&messages_pool), + obstack_object_size (&messages_pool)); + + /* Omit the contexts section. */ +#if 0 + write_section (output_file, 0x2f, ...); +#endif + + obstack_free (&messages_pool, NULL); + obstack_free (&hashes_pool, NULL); +} + + +int +msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding, + const char *domain_name, const char *file_name) +{ + FILE *output_file; + + /* If no entry for this domain don't even create the file. */ + if (mlp->nitems != 0) + { + /* Determine whether mlp has plural entries. */ + { + bool has_plural; + size_t j; + + has_plural = false; + for (j = 0; j < mlp->nitems; j++) + if (mlp->item[j]->msgid_plural != NULL) + has_plural = true; + if (has_plural) + { + multiline_error (xstrdup (""), + xstrdup (_("\ +message catalog has plural form translations\n\ +but the Qt message catalog format doesn't support plural handling\n"))); + return 1; + } + } + + /* Convert the messages to Unicode. */ + iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL); + + /* Determine whether mlp has non-ISO-8859-1 msgid entries. */ + { + size_t j; + + for (j = 0; j < mlp->nitems; j++) + { + const char *string = mlp->item[j]->msgid; + + /* An UTF-8 encoded string fits in ISO-8859-1 if and only if all + its bytes are < 0xc4. */ + for (; *string; string++) + if ((unsigned char) *string >= 0xc4) + { + multiline_error (xstrdup (""), + xstrdup (_("\ +message catalog has msgid strings containing characters outside ISO-8859-1\n\ +but the Qt message catalog format supports Unicode only in the translated\n\ +strings, not in the untranslated strings\n"))); + return 1; + } + } + } + + if (strcmp (domain_name, "-") == 0) + { + output_file = stdout; + SET_BINARY (fileno (output_file)); + } + else + { + output_file = fopen (file_name, "wb"); + if (output_file == NULL) + { + error (0, errno, _("error while opening \"%s\" for writing"), + file_name); + return 1; + } + } + + if (output_file != NULL) + { + write_qm (output_file, mlp); + + /* Make sure nothing went wrong. */ + if (fwriteerror (output_file)) + error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"), + file_name); + + if (output_file != stdout) + fclose (output_file); + } + } + + return 0; +} diff --git a/gettext-tools/src/write-qt.h b/gettext-tools/src/write-qt.h new file mode 100644 index 000000000..54c1ef496 --- /dev/null +++ b/gettext-tools/src/write-qt.h @@ -0,0 +1,31 @@ +/* Writing Qt .qm files. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _WRITE_QT_H +#define _WRITE_QT_H + +#include "message.h" + +/* Write a Qt .qm file. mlp is a list containing the messages to be output. + domain_name is the domain name, file_name is the desired file name. + Return 0 if ok, nonzero on error. */ +extern int + msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding, + const char *domain_name, const char *file_name); + +#endif /* _WRITE_QT_H */ diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index ae3d3f814..0e1eabb50 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -149,6 +149,9 @@ static flag_context_list_table_ty flag_table_tcl; static flag_context_list_table_ty flag_table_perl; static flag_context_list_table_ty flag_table_php; +/* If true, recognize Qt format strings. */ +static bool recognize_format_qt; + /* Canonicalized encoding name for all input files. */ const char *xgettext_global_source_encoding; @@ -200,6 +203,7 @@ static const struct option long_options[] = { "output", required_argument, NULL, 'o' }, { "output-dir", required_argument, NULL, 'p' }, { "properties-output", no_argument, NULL, CHAR_MAX + 6 }, + { "qt", no_argument, NULL, CHAR_MAX + 9 }, { "sort-by-file", no_argument, NULL, 'F' }, { "sort-output", no_argument, NULL, 's' }, { "strict", no_argument, NULL, 'S' }, @@ -262,6 +266,7 @@ main (int argc, char *argv[]) const char *files_from = NULL; string_list_ty *file_list; char *output_file = NULL; + const char *language = NULL; extractor_ty extractor = { NULL, NULL, NULL, NULL }; /* Set program name for messages. */ @@ -335,7 +340,7 @@ main (int argc, char *argv[]) } break; case 'C': - extractor = language_to_extractor ("C++"); + language = "C++"; break; case 'd': default_domain = optarg; @@ -390,7 +395,7 @@ main (int argc, char *argv[]) /* Accepted for backward compatibility with 0.10.35. */ break; case 'L': - extractor = language_to_extractor (optarg); + language = optarg; break; case 'm': /* -m takes an optional argument. If none is given "" is assumed. */ @@ -471,6 +476,9 @@ main (int argc, char *argv[]) case CHAR_MAX + 8: /* --flag */ xgettext_record_flag (optarg); break; + case CHAR_MAX + 9: /* --qt */ + recognize_format_qt = true; + break; default: usage (EXIT_FAILURE); /* NOTREACHED */ @@ -521,6 +529,10 @@ xgettext cannot work without keywords to look for")); usage (EXIT_FAILURE); } + /* Determine extractor from language. */ + if (language != NULL) + extractor = language_to_extractor (language); + /* Canonize msgstr prefix/suffix. */ if (msgstr_prefix != NULL && msgstr_suffix == NULL) msgstr_suffix = ""; @@ -787,6 +799,10 @@ Language specific options:\n")); printf (_("\ (only languages C, C++, ObjectiveC)\n")); printf (_("\ + --qt recognize Qt format strings\n")); + printf (_("\ + (only language C++)\n")); + printf (_("\ --debug more detailed formatstring recognition result\n")); printf ("\n"); printf (_("\ @@ -1398,6 +1414,11 @@ xgettext_record_flag (const char *optionstring) name_start, name_end, argnum, value, pass); break; + case format_qt: + flag_context_list_table_insert (&flag_table_c, 0, + name_start, name_end, + argnum, value, pass); + break; default: abort (); } @@ -2115,6 +2136,12 @@ language_to_extractor (const char *name) result.formatstring_parser1 = tp->formatstring_parser1; result.formatstring_parser2 = tp->formatstring_parser2; + /* Handle --qt. It's preferrable to handle this facility here rather + than through an option --language=C++/Qt because the latter would + conflict with the language "C++" regarding the file extensions. */ + if (recognize_format_qt && strcmp (tp->name, "C++") == 0) + result.formatstring_parser2 = &formatstring_qt; + return result; } diff --git a/gettext-tools/tests/ChangeLog b/gettext-tools/tests/ChangeLog index 64d7221ad..9c70bc757 100644 --- a/gettext-tools/tests/ChangeLog +++ b/gettext-tools/tests/ChangeLog @@ -1,3 +1,13 @@ +2003-10-19 Bruno Haible + + * format-qt-1: New file. + * format-qt-2: New file. + * msgfmt-15: New file. + * qttest_pl.po: New file. + * qttest_pl.qm: New file. + * Makefile.am (TESTS): Add format-qt-1, format-qt-2, msgfmt-15. + (EXTRA_DIST): Add qttest_pl.po, qttest_pl.qm. + 2003-10-14 Bruno Haible * xgettext-22: Update expected result. diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 0fdb2897c..d51e3a7cb 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -37,6 +37,7 @@ TESTS = gettext-1 gettext-2 \ msgfilter-1 msgfilter-2 msgfilter-3 \ msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 msgfmt-6 msgfmt-7 \ msgfmt-8 msgfmt-9 msgfmt-10 msgfmt-11 msgfmt-12 msgfmt-13 msgfmt-14 \ + msgfmt-15 \ msggrep-1 msggrep-2 msggrep-3 msggrep-4 msggrep-5 msggrep-6 \ msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \ msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \ @@ -65,6 +66,7 @@ TESTS = gettext-1 gettext-2 \ format-perl-1 format-perl-2 \ format-perl-brace-1 format-perl-brace-2 \ format-perl-mixed-1 format-perl-mixed-2 \ + format-qt-1 format-qt-2 \ format-sh-1 format-sh-2 \ format-tcl-1 format-tcl-2 \ format-ycp-1 format-ycp-2 \ @@ -76,7 +78,7 @@ TESTS = gettext-1 gettext-2 \ EXTRA_DIST += $(TESTS) \ test.mo xg-test1.ok.po mex-test2.ok msguniq-a.in msguniq-a.inp \ - msguniq-a.out ChangeLog.0 + msguniq-a.out qttest_pl.po qttest_pl.qm ChangeLog.0 XGETTEXT = ../src/xgettext diff --git a/gettext-tools/tests/format-qt-1 b/gettext-tools/tests/format-qt-1 new file mode 100755 index 000000000..74df988a1 --- /dev/null +++ b/gettext-tools/tests/format-qt-1 @@ -0,0 +1,65 @@ +#! /bin/sh + +# Test recognition of Qt format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles f-qt-1.data" +cat <<\EOF > f-qt-1.data +# Unrecognized: no argument +"abc%%def" +# Valid: one argument +"abc%1def" +# Valid: one argument +"abc%9def" +# Valid: unterminated +"abc%1def%" +# Valid: non-digit +"abc%1def%x" +# Valid: zero +"abc%1def%0" +# Valid: permutation +"abc%2def%1" +# Invalid: multiple uses of same argument +"abc%2def%1ghi%2" +EOF + +: ${XGETTEXT=xgettext} +n=0 +while read comment; do + read string + n=`expr $n + 1` + tmpfiles="$tmpfiles f-qt-1-$n.in f-qt-1-$n.po" + cat < f-qt-1-$n.in +_(${string}); +EOF + ${XGETTEXT} -L C++ --qt -k_ -o f-qt-1-$n.po f-qt-1-$n.in || exit 1 + test -f f-qt-1-$n.po || exit 1 + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if grep qt-format f-qt-1-$n.po > /dev/null; then + : + else + fail=yes + fi + else + if grep qt-format f-qt-1-$n.po > /dev/null; then + fail=yes + else + : + fi + fi + if test -n "$fail"; then + echo "Format string recognition error:" 1>&2 + cat f-qt-1-$n.in 1>&2 + echo "Got:" 1>&2 + cat f-qt-1-$n.po 1>&2 + exit 1 + fi + rm -f f-qt-1-$n.in f-qt-1-$n.po +done < f-qt-1.data + +rm -fr $tmpfiles + +exit 0 diff --git a/gettext-tools/tests/format-qt-2 b/gettext-tools/tests/format-qt-2 new file mode 100755 index 000000000..e0118679b --- /dev/null +++ b/gettext-tools/tests/format-qt-2 @@ -0,0 +1,73 @@ +#! /bin/sh + +# Test checking of Qt format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles f-qt-2.data" +cat <<\EOF > f-qt-2.data +# Valid: %% doesn't count +msgid "abc%%def" +msgstr "xyz" +# Invalid: invalid msgstr +msgid "abc%1def" +msgstr "xyz%1%1" +# Valid: same arguments +msgid "abc%2def" +msgstr "xyz%2" +# Valid: permutation +msgid "abc%3%1%2def" +msgstr "xyz%2%1%3" +# Invalid: too few arguments +msgid "abc%2def%1" +msgstr "xyz%1" +# Invalid: too many arguments +msgid "abc%1def" +msgstr "xyz%1uvw%2" +# Invalid: missing non-final argument +msgid "abc%2def%1" +msgstr "xyz%2" +# Invalid: added non-final argument +msgid "abc%2def" +msgstr "xyz%1%2" +EOF + +: ${MSGFMT=msgfmt} +n=0 +while read comment; do + read msgid_line + read msgstr_line + n=`expr $n + 1` + tmpfiles="$tmpfiles f-qt-2-$n.po f-qt-2-$n.mo" + cat < f-qt-2-$n.po +#, qt-format +${msgid_line} +${msgstr_line} +EOF + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if ${MSGFMT} --check-format -o f-qt-2-$n.mo f-qt-2-$n.po; then + : + else + fail=yes + fi + else + ${MSGFMT} --check-format -o f-qt-2-$n.mo f-qt-2-$n.po 2> /dev/null + if test $? = 1; then + : + else + fail=yes + fi + fi + if test -n "$fail"; then + echo "Format string checking error:" 1>&2 + cat f-qt-2-$n.po 1>&2 + exit 1 + fi + rm -f f-qt-2-$n.po f-qt-2-$n.mo +done < f-qt-2.data + +rm -fr $tmpfiles + +exit 0 diff --git a/gettext-tools/tests/msgfmt-15 b/gettext-tools/tests/msgfmt-15 new file mode 100755 index 000000000..76158e9a9 --- /dev/null +++ b/gettext-tools/tests/msgfmt-15 @@ -0,0 +1,19 @@ +#! /bin/sh + +# Test output in Qt .qm format. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles mf-test15.qm" +: ${MSGFMT=msgfmt} +${MSGFMT} --qt ${top_srcdir}/tests/qttest_pl.po -o mf-test15.qm +test $? = 0 || { rm -fr $tmpfiles; exit 1; } + +: ${CMP=cmp} +${CMP} ${top_srcdir}/tests/qttest_pl.qm mf-test15.qm >/dev/null 2>/dev/null +result=$? + +rm -fr $tmpfiles + +exit $result diff --git a/gettext-tools/tests/qttest_pl.po b/gettext-tools/tests/qttest_pl.po new file mode 100644 index 000000000..d90ab8a61 --- /dev/null +++ b/gettext-tools/tests/qttest_pl.po @@ -0,0 +1,26 @@ +# Polish translations for hello-cplusplus-qt package. +# Copyright (C) 2003 Yoyodyne, Inc. +# This file is distributed under the same license as the hello-cplusplus-qt package. +# Bruno Haible , 2003. +# +msgid "" +msgstr "" +"Project-Id-Version: hello-cplusplus-qt 0\n" +"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n" +"POT-Creation-Date: 2003-10-20 10:14+0200\n" +"PO-Revision-Date: 2003-10-20 10:13+0200\n" +"Last-Translator: Bruno Haible \n" +"Language-Team: Polish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" + +#: hello.cc:45 +msgid "Written by François Pinard." +msgstr "Program napisa³ François Pinard." + +#: hello.cc:52 +msgid "error %1." +msgstr "b³±d %1." diff --git a/gettext-tools/tests/qttest_pl.qm b/gettext-tools/tests/qttest_pl.qm new file mode 100644 index 000000000..2cdffcc1a Binary files /dev/null and b/gettext-tools/tests/qttest_pl.qm differ