From: Bruno Haible Date: Wed, 28 Jan 2009 02:13:03 +0000 (+0000) Subject: Recognize Qt4 plural forms format strings. X-Git-Tag: v0.18~244 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6aa7b7ed;p=thirdparty%2Fgettext.git Recognize Qt4 plural forms format strings. --- diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index d738760fc..f339d0c44 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,32 @@ +2009-01-27 Bruno Haible + + * 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 * plural-table.c (plural_table): Put Turkish under nplurals=2. diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES index 764488966..f786e06f0 100644 --- a/gettext-tools/src/FILES +++ b/gettext-tools/src/FILES @@ -228,6 +228,7 @@ 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-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. diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index 3409496aa..834a84d81 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -113,7 +113,8 @@ FORMAT_SOURCE += format-invalid.h \ 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 = \ diff --git a/gettext-tools/src/format-qt-plural.c b/gettext-tools/src/format-qt-plural.c new file mode 100644 index 000000000..8b225fdda --- /dev/null +++ b/gettext-tools/src/format-qt-plural.c @@ -0,0 +1,194 @@ +/* Qt plural format strings. + Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc. + Written by Bruno Haible , 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#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 + +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 */ diff --git a/gettext-tools/src/format.c b/gettext-tools/src/format.c index 592a398dd..ce50e971b 100644 --- a/gettext-tools/src/format.c +++ b/gettext-tools/src/format.c @@ -1,5 +1,5 @@ /* Format strings. - Copyright (C) 2001-2008 Free Software Foundation, Inc. + Copyright (C) 2001-2009 Free Software Foundation, Inc. Written by Bruno Haible , 2001. This program is free software: you can redistribute it and/or modify @@ -54,6 +54,7 @@ struct formatstring_parser *formatstring_parsers[NFORMATS] = /* 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 }; diff --git a/gettext-tools/src/format.h b/gettext-tools/src/format.h index 8de202ae0..412ecf6ab 100644 --- a/gettext-tools/src/format.h +++ b/gettext-tools/src/format.h @@ -1,5 +1,5 @@ /* Format strings. - Copyright (C) 2001-2008 Free Software Foundation, Inc. + Copyright (C) 2001-2009 Free Software Foundation, Inc. Written by Bruno Haible , 2001. This program is free software: you can redistribute it and/or modify @@ -115,6 +115,7 @@ 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; +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; diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c index 121240eac..fdc2f9bd3 100644 --- a/gettext-tools/src/message.c +++ b/gettext-tools/src/message.c @@ -1,5 +1,5 @@ /* 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 @@ -54,6 +54,7 @@ const char *const format_language[NFORMATS] = /* format_php */ "php", /* format_gcc_internal */ "gcc-internal", /* format_qt */ "qt", + /* format_qt_plursl */ "qt-plural", /* format_kde */ "kde", /* format_boost */ "boost" }; @@ -80,6 +81,7 @@ const char *const format_language_pretty[NFORMATS] = /* format_php */ "PHP", /* format_gcc_internal */ "GCC internal", /* format_qt */ "Qt", + /* format_qt_plural */ "Qt plural", /* format_kde */ "KDE", /* format_boost */ "Boost" }; diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h index 6142055b2..16088dd26 100644 --- a/gettext-tools/src/message.h +++ b/gettext-tools/src/message.h @@ -1,5 +1,5 @@ /* 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 @@ -63,10 +63,11 @@ enum format_type 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]; diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 8a0f6151f..6466a4804 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -252,6 +252,7 @@ struct extractor_ty flag_context_list_table_ty *flag_table; struct formatstring_parser *formatstring_parser1; struct formatstring_parser *formatstring_parser2; + struct formatstring_parser *formatstring_parser3; }; @@ -1249,6 +1250,11 @@ inherited_context (flag_context_ty outer_context, 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; } @@ -1374,6 +1380,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table, 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 (); } @@ -1403,6 +1413,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table, 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 (); } @@ -1423,6 +1437,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table, 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 (); } @@ -1450,6 +1468,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table, 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 (); } @@ -1666,6 +1688,11 @@ xgettext_record_flag (const char *optionstring) 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, @@ -1842,6 +1869,7 @@ error while opening \"%s\" for reading"), new_name); 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 @@ -1861,6 +1889,7 @@ extract_from_file (const char *file_name, extractor_ty extractor, 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); @@ -1966,7 +1995,9 @@ set_format_flags_from_context (enum is_format is_format[NFORMATS], { 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) @@ -1977,6 +2008,9 @@ set_format_flags_from_context (enum is_format is_format[NFORMATS], 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])) { @@ -2237,14 +2271,17 @@ meta information, not the empty string.\n"))); { 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])))) { @@ -2360,7 +2397,8 @@ remember_a_message_plural (message_ty *mp, char *string, 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 @@ -2368,9 +2406,10 @@ remember_a_message_plural (message_ty *mp, char *string, && !(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])))) { @@ -2537,26 +2576,29 @@ arglist_parser_remember (struct arglist_parser *ap, /* 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 @@ -2802,15 +2844,33 @@ arglist_parser_done (struct arglist_parser *ap, int argnum) } } - 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 @@ -3067,6 +3127,7 @@ language_to_extractor (const char *name) 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 @@ -3075,6 +3136,7 @@ language_to_extractor (const char *name) { 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) diff --git a/gettext-tools/src/xgettext.h b/gettext-tools/src/xgettext.h index 0d84c4563..a54b81c86 100644 --- a/gettext-tools/src/xgettext.h +++ b/gettext-tools/src/xgettext.h @@ -1,5 +1,5 @@ /* 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 and Bruno Haible , 2001. @@ -89,6 +89,9 @@ struct flag_context_ty /* 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;