+2009-01-27 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_qt_plural'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_qt_plural entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_qt_plural): New declaration.
+ * format-qt-plural.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_qt_plural.
+ * xgettext.h (struct flag_context_ty): Add fields is_format3,
+ pass_format3.
+ * xgettext.c (struct extractor_ty): Add field formatstring_parser3.
+ (inherited_context, flag_context_list_table_insert): Handle the new
+ flag_context_ty fields.
+ (xgettext_record_flag): Handle format_qt_plural.
+ (current_formatstring_parser3): New variable.
+ (extract_from_file): Initialize it.
+ (set_format_flags_from_context): Handle the new flag_context_ty fields.
+ (remember_a_message, remember_a_message_plural): Handle
+ current_formatstring_parser3. Avoid adding a c-format flag to a message
+ already flagged as qt-plural-format.
+ (arglist_parser_remember): Allow argnum1 and argnum2 in the call shape
+ to be the same.
+ (arglist_parser_done): Add special recognition of qt-plural-format
+ strings.
+ (language_to_extractor): Set the formatstring_parser3 in the result.
+ * Makefile.am (FORMAT_SOURCE): Add format-qt-plural.c.
+ * FILES: Update.
+
2009-01-27 Bruno Haible <bruno@clisp.org>
* plural-table.c (plural_table): Put Turkish under nplurals=2.
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-qt-plural.c Format string handling for Qt plural forms.
format-kde.c Format string handling for KDE.
format-boost.c Format string handling for Boost.
format.c Table of the language dependent format string handlers.
format-c.c format-sh.c format-python.c format-lisp.c format-elisp.c \
format-librep.c format-scheme.c format-java.c format-csharp.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-qt.c format-kde.c format-boost.c
+format-php.c format-gcc-internal.c format-qt.c format-qt-plural.c \
+format-kde.c format-boost.c
# libgettextsrc contains all code that is needed by at least two programs.
libgettextsrc_la_SOURCES = \
--- /dev/null
+/* Qt plural format strings.
+ Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2009.
+
+ 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt plural format strings are processed by QObject::tr and are documented in
+ qt-x11-opensource-src-4.3.1/doc/html/qobject.html#tr.
+ A directive
+ - starts with '%',
+ - is optionally followed by 'L' (a no-op),
+ - is followed by 'n'.
+ Every directive is replaced by the numeric argument N passed to QObject::tr.
+ */
+
+struct spec
+{
+ /* Number of format directives. */
+ unsigned int directives;
+};
+
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ const char *dir_start = format - 1;
+
+ if (*format == 'L')
+ format++;
+ if (*format == 'n')
+ {
+ /* A directive. */
+ FDI_SET (dir_start, FMTDIR_START);
+ spec.directives++;
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+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 (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ /* Check the argument is used. */
+ if ((spec1->directives == 0 && spec2->directives > 0)
+ || (equality && spec1->directives > 0 && spec2->directives == 0))
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in 'msgid' and '%s' does not match"),
+ pretty_msgstr);
+ err = true;
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_qt_plural =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ if (spec->directives > 0)
+ 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, false, NULL, &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 --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt-plural.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
/* Format strings.
- Copyright (C) 2001-2008 Free Software Foundation, Inc.
+ Copyright (C) 2001-2009 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software: you can redistribute it and/or modify
/* format_php */ &formatstring_php,
/* format_gcc_internal */ &formatstring_gcc_internal,
/* format_qt */ &formatstring_qt,
+ /* format_qt_plural */ &formatstring_qt_plural,
/* format_kde */ &formatstring_kde,
/* format_boost */ &formatstring_boost
};
/* Format strings.
- Copyright (C) 2001-2008 Free Software Foundation, Inc.
+ Copyright (C) 2001-2009 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software: you can redistribute it and/or modify
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;
+extern DLL_VARIABLE struct formatstring_parser formatstring_qt_plural;
extern DLL_VARIABLE struct formatstring_parser formatstring_kde;
extern DLL_VARIABLE struct formatstring_parser formatstring_boost;
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2008 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
/* format_php */ "php",
/* format_gcc_internal */ "gcc-internal",
/* format_qt */ "qt",
+ /* format_qt_plursl */ "qt-plural",
/* format_kde */ "kde",
/* format_boost */ "boost"
};
/* format_php */ "PHP",
/* format_gcc_internal */ "GCC internal",
/* format_qt */ "Qt",
+ /* format_qt_plural */ "Qt plural",
/* format_kde */ "KDE",
/* format_boost */ "Boost"
};
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2008 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
format_php,
format_gcc_internal,
format_qt,
+ format_qt_plural,
format_kde,
format_boost
};
-#define NFORMATS 22 /* Number of format_type enum values. */
+#define NFORMATS 23 /* 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];
flag_context_list_table_ty *flag_table;
struct formatstring_parser *formatstring_parser1;
struct formatstring_parser *formatstring_parser2;
+ struct formatstring_parser *formatstring_parser3;
};
result.is_format2 = outer_context.is_format2;
result.pass_format2 = false;
}
+ if (result.pass_format3)
+ {
+ result.is_format3 = outer_context.is_format3;
+ result.pass_format3 = false;
+ }
return result;
}
list->flags.is_format2 = value;
list->flags.pass_format2 = pass;
break;
+ case 2:
+ list->flags.is_format3 = value;
+ list->flags.pass_format3 = pass;
+ break;
default:
abort ();
}
list->flags.is_format2 = value;
list->flags.pass_format2 = pass;
break;
+ case 2:
+ list->flags.is_format3 = value;
+ list->flags.pass_format3 = pass;
+ break;
default:
abort ();
}
list->flags.is_format2 = value;
list->flags.pass_format2 = pass;
break;
+ case 2:
+ list->flags.is_format3 = value;
+ list->flags.pass_format3 = pass;
+ break;
default:
abort ();
}
list->flags.is_format2 = value;
list->flags.pass_format2 = pass;
break;
+ case 2:
+ list->flags.is_format3 = value;
+ list->flags.pass_format3 = pass;
+ break;
default:
abort ();
}
name_start, name_end,
argnum, value, pass);
break;
+ case format_qt_plural:
+ flag_context_list_table_insert (&flag_table_cxx_qt, 2,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
case format_kde:
flag_context_list_table_insert (&flag_table_cxx_kde, 1,
name_start, name_end,
NULL if the language has no notion of format strings. */
static struct formatstring_parser *current_formatstring_parser1;
static struct formatstring_parser *current_formatstring_parser2;
+static struct formatstring_parser *current_formatstring_parser3;
static void
current_formatstring_parser1 = extractor.formatstring_parser1;
current_formatstring_parser2 = extractor.formatstring_parser2;
+ current_formatstring_parser3 = extractor.formatstring_parser3;
extractor.func (fp, real_file_name, logical_file_name, extractor.flag_table,
mdlp);
{
size_t i;
- if (context.is_format1 != undecided || context.is_format2 != undecided)
+ if (context.is_format1 != undecided
+ || context.is_format2 != undecided
+ || context.is_format3 != undecided)
for (i = 0; i < NFORMATS; i++)
{
if (is_format[i] == undecided)
if (formatstring_parsers[i] == current_formatstring_parser2
&& context.is_format2 != undecided)
is_format[i] = (enum is_format) context.is_format2;
+ if (formatstring_parsers[i] == current_formatstring_parser3
+ && context.is_format3 != undecided)
+ is_format[i] = (enum is_format) context.is_format3;
}
if (possible_format_p (is_format[i]))
{
{
if (is_format[i] == undecided
&& (formatstring_parsers[i] == current_formatstring_parser1
- || formatstring_parsers[i] == current_formatstring_parser2)
+ || formatstring_parsers[i] == current_formatstring_parser2
+ || formatstring_parsers[i] == current_formatstring_parser3)
/* But avoid redundancy: objc-format is stronger than c-format. */
&& !(i == format_c && possible_format_p (is_format[format_objc]))
&& !(i == format_objc && possible_format_p (is_format[format_c]))
/* Avoid flagging a string as c-format when it's known to be a
- qt-format or kde-format or boost-format string. */
+ qt-format or qt-plural-format or kde-format or boost-format
+ string. */
&& !(i == format_c
&& (possible_format_p (is_format[format_qt])
+ || possible_format_p (is_format[format_qt_plural])
|| possible_format_p (is_format[format_kde])
|| possible_format_p (is_format[format_boost]))))
{
msgid_plural. This is a heuristic. */
for (i = 0; i < NFORMATS; i++)
if ((formatstring_parsers[i] == current_formatstring_parser1
- || formatstring_parsers[i] == current_formatstring_parser2)
+ || formatstring_parsers[i] == current_formatstring_parser2
+ || formatstring_parsers[i] == current_formatstring_parser3)
&& (mp->is_format[i] == undecided || mp->is_format[i] == possible)
/* But avoid redundancy: objc-format is stronger than c-format. */
&& !(i == format_c
&& !(i == format_objc
&& possible_format_p (mp->is_format[format_c]))
/* Avoid flagging a string as c-format when it's known to be a
- qt-format or boost-format string. */
+ qt-format or qt-plural-format or boost-format string. */
&& !(i == format_c
&& (possible_format_p (mp->is_format[format_qt])
+ || possible_format_p (mp->is_format[format_qt_plural])
|| possible_format_p (mp->is_format[format_kde])
|| possible_format_p (mp->is_format[format_boost]))))
{
/* Mark msgctxt as done. */
cp->argnumc = 0;
}
- else if (argnum == cp->argnum1)
- {
- cp->msgid = string;
- cp->msgid_context = context;
- cp->msgid_pos.file_name = file_name;
- cp->msgid_pos.line_number = line_number;
- cp->msgid_comment = add_reference (comment);
- stored_string = true;
- /* Mark msgid as done. */
- cp->argnum1 = 0;
- }
- else if (argnum == cp->argnum2)
+ else
{
- cp->msgid_plural = string;
- cp->msgid_plural_context = context;
- cp->msgid_plural_pos.file_name = file_name;
- cp->msgid_plural_pos.line_number = line_number;
- stored_string = true;
- /* Mark msgid_plural as done. */
- cp->argnum2 = 0;
+ if (argnum == cp->argnum1)
+ {
+ cp->msgid = string;
+ cp->msgid_context = context;
+ cp->msgid_pos.file_name = file_name;
+ cp->msgid_pos.line_number = line_number;
+ cp->msgid_comment = add_reference (comment);
+ stored_string = true;
+ /* Mark msgid as done. */
+ cp->argnum1 = 0;
+ }
+ if (argnum == cp->argnum2)
+ {
+ cp->msgid_plural = string;
+ cp->msgid_plural_context = context;
+ cp->msgid_plural_pos.file_name = file_name;
+ cp->msgid_plural_pos.line_number = line_number;
+ stored_string = true;
+ /* Mark msgid_plural as done. */
+ cp->argnum2 = 0;
+ }
}
}
/* Note: There is a memory leak here: When string was stored but is later
}
}
- mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid,
- best_cp->msgid_context,
- &best_cp->msgid_pos,
- best_cp->msgid_comment);
- if (best_cp->msgid_plural != NULL)
- remember_a_message_plural (mp, best_cp->msgid_plural,
- best_cp->msgid_plural_context,
- &best_cp->msgid_plural_pos,
- NULL);
+ {
+ flag_context_ty msgid_context = best_cp->msgid_context;
+ flag_context_ty msgid_plural_context = best_cp->msgid_plural_context;
+
+ /* Special support for the 3-argument tr operator in Qt:
+ When --qt and --keyword=tr:1,1,2c,3t are specified, add to the
+ context the information that the argument is expeected to be a
+ qt-plural-format. */
+ if (recognize_format_qt
+ && current_formatstring_parser3 == &formatstring_qt_plural
+ && best_cp->msgid_plural == best_cp->msgid)
+ {
+ msgid_context.is_format3 = yes_according_to_context;
+ msgid_plural_context.is_format3 = yes_according_to_context;
+ }
+
+ mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid,
+ msgid_context,
+ &best_cp->msgid_pos,
+ best_cp->msgid_comment);
+ if (best_cp->msgid_plural != NULL)
+ remember_a_message_plural (mp, best_cp->msgid_plural,
+ msgid_plural_context,
+ &best_cp->msgid_plural_pos,
+ NULL);
+ }
+
if (best_cp->xcomments.nitems > 0)
{
/* Add best_cp->xcomments to mp->comment_dot, unless already
result.flag_table = tp->flag_table;
result.formatstring_parser1 = tp->formatstring_parser1;
result.formatstring_parser2 = tp->formatstring_parser2;
+ result.formatstring_parser3 = NULL;
/* Handle --qt. It's preferrable to handle this facility here rather
than through an option --language=C++/Qt because the latter would
{
result.flag_table = &flag_table_cxx_qt;
result.formatstring_parser2 = &formatstring_qt;
+ result.formatstring_parser3 = &formatstring_qt_plural;
}
/* Likewise for --kde. */
if (recognize_format_kde && strcmp (tp->name, "C++") == 0)
/* xgettext common functions.
- Copyright (C) 2001-2003, 2005-2006, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003, 2005-2006, 2008-2009 Free Software Foundation, Inc.
Written by Peter Miller <millerp@canb.auug.org.au>
and Bruno Haible <haible@clisp.cons.org>, 2001.
/* Regarding the secondary formatstring type. */
/*enum is_format*/ unsigned int is_format2 : 3;
/*bool*/ unsigned int pass_format2 : 1;
+ /* Regarding the tertiary formatstring type. */
+ /*enum is_format*/ unsigned int is_format3 : 3;
+ /*bool*/ unsigned int pass_format3 : 1;
};
/* Null context. */
extern flag_context_ty null_context;