mirror of
https://github.com/openharmony/third_party_gettext.git
synced 2026-07-19 21:26:06 -04:00
Support for Qt message catalog format and Qt format strings.
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Executable
+65
@@ -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
|
||||
Executable
+73
@@ -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
|
||||
Executable
+19
@@ -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
|
||||
@@ -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.
Reference in New Issue
Block a user