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:
+2003-10-19 Bruno Haible <bruno@clisp.org>
+
+ * gettext.texi (PO Files): Mention qt-format.
+ (qt-format): New subsection.
+ * msgfmt.texi: Document --qt option.
+ * xgettext.texi: Likewise.
+
2003-10-13 Bruno Haible <bruno@clisp.org>
* gettext.texi (PO Files): Mention objc-format, sh-format, perl-format,
* 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
@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
* 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
@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
@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
@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
@*
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
+2003-10-19 Bruno Haible <bruno@clisp.org>
+
+ * POTFILES.in: Add src/format-qt.c and src/write-qt.c.
+
2003-10-14 Bruno Haible <bruno@clisp.org>
* POTFILES.in: Add src/read-stringtable.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
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
+2003-10-19 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
Fix compilation errors in C++ mode.
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
| 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.
|
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
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 = \
# 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 \
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
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)
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
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
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)
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
--- /dev/null
+/* Qt format strings.
+ Copyright (C) 2003 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 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 <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#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 <stdio.h>
+#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 */
/* 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
};
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];
/* 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] =
/* format_perl */ "Perl",
/* format_perl_brace */ "Perl brace",
/* format_php */ "PHP",
- /* format_gcc_internal */ "GCC internal"
+ /* format_gcc_internal */ "GCC internal",
+ /* format_qt */ "Qt"
};
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];
#include "write-mo.h"
#include "write-java.h"
#include "write-tcl.h"
+#include "write-qt.h"
#include "gettext.h"
#include "message.h"
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. */
{ "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' },
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;
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)
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)
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,
--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"));
{
/* 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;
--- /dev/null
+/* Writing Qt .qm files.
+ Copyright (C) 2003 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 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 <config.h>
+#endif
+
+/* Specification. */
+#include "write-qt.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}
--- /dev/null
+/* Writing Qt .qm files.
+ Copyright (C) 2003 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 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 */
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;
{ "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' },
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. */
}
break;
case 'C':
- extractor = language_to_extractor ("C++");
+ language = "C++";
break;
case 'd':
default_domain = optarg;
/* 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. */
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 */
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 = "";
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 (_("\
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 ();
}
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;
}
+2003-10-19 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* xgettext-22: Update expected result.
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 \
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 \
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
--- /dev/null
+#! /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 <<EOF > 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
--- /dev/null
+#! /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 <<EOF > 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
--- /dev/null
+#! /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
--- /dev/null
+# 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 <bruno@clisp.org>, 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 <bruno@clisp.org>\n"
+"Language-Team: Polish <pl@li.org>\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."