Support for Qt message catalog format and Qt format strings.

This commit is contained in:
Bruno Haible
2003-10-28 16:10:35 +00:00
parent 06784fb763
commit 2f09ddb5c8
28 changed files with 1177 additions and 23 deletions
+8
View File
@@ -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:
+7
View File
@@ -1,3 +1,10 @@
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,
+17 -1
View File
@@ -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
+5
View File
@@ -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
+8 -1
View File
@@ -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
+4
View File
@@ -1,3 +1,7 @@
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.
+2
View File
@@ -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
+37
View File
@@ -1,3 +1,40 @@
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.
+4
View File
@@ -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.
|
+7 -6
View File
@@ -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 \
+9 -2
View File
@@ -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
+9 -2
View File
@@ -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
+230
View File
@@ -0,0 +1,230 @@
/* 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 */
+2 -1
View File
@@ -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
};
+1
View File
@@ -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];
+4 -2
View File
@@ -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"
};
+3 -2
View File
@@ -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];
+25 -3
View File
@@ -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;
+539
View File
@@ -0,0 +1,539 @@
/* 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;
}
+31
View File
@@ -0,0 +1,31 @@
/* 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 */
+29 -2
View File
@@ -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;
}
+10
View File
@@ -1,3 +1,13 @@
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.
+3 -1
View File
@@ -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
+65
View File
@@ -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 <<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
+73
View File
@@ -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 <<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
+19
View File
@@ -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
+26
View File
@@ -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 <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."
Binary file not shown.