From: Bruno Haible Date: Sun, 26 Apr 2020 22:19:07 +0000 (+0200) Subject: xgettext: Add Ruby support. X-Git-Tag: v0.21~78 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6c39c38b094db79b59ee38b1a0320cb3713f5fc9;p=thirdparty%2Fgettext.git xgettext: Add Ruby support. * gettext-tools/src/x-ruby.h: New file. * gettext-tools/src/x-ruby.c: New file. * gettext-tools/src/xgettext.h (verbose): New declaration. * gettext-tools/src/xgettext.c: Include x-ruby.h. (verbose): New declaration. (flag_table_ruby): New variable. (long_options): Add '--verbose'. (main): Update for Ruby. Handle '-v'/'--verbose' option. (usage): Document the '-L Ruby' and '-v' options. (xgettext_record_flag, language_to_extractor, extension_to_language): Update for Ruby. * gettext-tools/src/Makefile.am (noinst_HEADERS): Add x-ruby.h. (xgettext_SOURCES): Add x-ruby.c. * gettext-tools/src/FILES: Mention x-ruby.h, x-ruby.c. * gettext-tools/tests/xgettext-ruby-1: New file. * gettext-tools/tests/format-ruby-1: New file. * gettext-tools/tests/format-ruby-2: New file. * gettext-tools/tests/Makefile.am (TESTS): Add them. * gettext-tools/doc/gettext.texi (Ruby): New section. * gettext-tools/doc/xgettext.texi: Document the '-L Ruby' and '-v' options. * HACKING: Document the recommended Ruby packages. * NEWS: Mention the Ruby support. --- diff --git a/HACKING b/HACKING index f953b68a4..edc0b5862 100644 --- a/HACKING +++ b/HACKING @@ -142,6 +142,13 @@ are skipped. To this effect, you need to install also: + Homepage: http://www.php.net/ + Ubuntu package: php + * Ruby + + Homepage: https://www.ruby-lang.org/en/ + + Ubuntu package: ruby + * The ruby-gettext package + + Homepage: https://ruby-gettext.github.io/ + + Ubuntu package: ruby-gettext + * lua + Homepage: https://www.lua.org/ + Ubuntu package: lua5.2 or lua5.1 diff --git a/NEWS b/NEWS index 443a7c1a6..5e5156dbb 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ Version 0.21 - April 2020 o xgettext now recognizes text blocks as string literals. - JavaScript: xgettext parses JSX expressions more reliably. + - Ruby: + o xgettext now supports Ruby. + o 'msgfmt -c' now verifies the syntax of translations of Ruby format + strings. * Runtime behaviour: - On native Windows platforms, the directory that contains the message diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 669a55023..5f4a88616 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -79,7 +79,7 @@ This file provides documentation for GNU @code{gettext} utilities. It also serves as a reference for the free Translation Project. @copying -Copyright (C) 1995-1998, 2001-2019 Free Software Foundation, Inc. +Copyright (C) 1995-1998, 2001-2020 Free Software Foundation, Inc. This manual is free documentation. It is dually licensed under the GNU FDL and the GNU GPL. This means that you can redistribute this @@ -114,7 +114,7 @@ A copy of the license is included in @ref{GNU GPL}. @page @vskip 0pt plus 1filll @c @insertcopying -Copyright (C) 1995-1998, 2001-2019 Free Software Foundation, Inc. +Copyright (C) 1995-1998, 2001-2020 Free Software Foundation, Inc. This manual is free documentation. It is dually licensed under the GNU FDL and the GNU GPL. This means that you can redistribute this @@ -436,6 +436,7 @@ Individual Programming Languages * Tcl:: Tcl - Tk's scripting language * Perl:: Perl * PHP:: PHP Hypertext Preprocessor +* Ruby:: Ruby * Pike:: Pike * GCC-source:: GNU Compiler Collection sources * Lua:: Lua @@ -9548,6 +9549,7 @@ that language, and to combine the resulting files using @code{msgcat}. * Tcl:: Tcl - Tk's scripting language * Perl:: Perl * PHP:: PHP Hypertext Preprocessor +* Ruby:: Ruby * Pike:: Pike * GCC-source:: GNU Compiler Collection sources * Lua:: Lua @@ -12045,6 +12047,62 @@ On platforms without gettext, the functions are not available. An example is available in the @file{examples} directory: @code{hello-php}. +@node Ruby +@subsection Ruby +@cindex Ruby + +@table @asis +@item RPMs +ruby, ruby-gettext + +@item Ubuntu packages +ruby, ruby-gettext + +@item File extension +@code{rb} + +@item String syntax +@code{"abc"}, @code{'abc'}, @code{%q/abc/} etc., +@code{%q(abc)}, @code{%q[abc]}, @code{%q@{abc@}} + +@item gettext shorthand +@code{_("abc")} + +@item gettext/ngettext functions +@code{gettext}, @code{ngettext} + +@item textdomain +--- + +@item bindtextdomain +@code{bindtextdomain} function + +@item setlocale +--- + +@item Prerequisite +@code{require 'gettext'} +@code{include GetText} + +@item Use or emulate GNU gettext +emulate + +@item Extractor +@code{xgettext} + +@item Formatting with positions +@code{sprintf("%2$d %1$d", x, y)} +@*@code{"%@{new@} replaces %@{old@}" % @{:old => oldvalue, :new => newvalue@}} + +@item Portability +fully portable + +@item po-mode marking +--- +@end table + +@c An example is available in the @file{examples} directory: @code{hello-ruby}. + @node Pike @subsection Pike @cindex Pike diff --git a/gettext-tools/doc/xgettext.texi b/gettext-tools/doc/xgettext.texi index f12af6aca..b2c6374fa 100644 --- a/gettext-tools/doc/xgettext.texi +++ b/gettext-tools/doc/xgettext.texi @@ -1,5 +1,5 @@ @c This file is part of the GNU gettext manual. -@c Copyright (C) 1995-2019 Free Software Foundation, Inc. +@c Copyright (C) 1995-2020 Free Software Foundation, Inc. @c See the file gettext.texi for copying conditions. @pindex xgettext @@ -76,9 +76,9 @@ Specifies the language of the input files. The supported languages are @code{C}, @code{C++}, @code{ObjectiveC}, @code{PO}, @code{Shell}, @code{Python}, @code{Lisp}, @code{EmacsLisp}, @code{librep}, @code{Scheme}, @code{Smalltalk}, @code{Java}, @code{JavaProperties}, @code{C#}, @code{awk}, -@code{YCP}, @code{Tcl}, @code{Perl}, @code{PHP}, @code{GCC-source}, -@code{NXStringTable}, @code{RST}, @code{RSJ}, @code{Glade}, @code{Lua}, -@code{JavaScript}, @code{Vala}, @code{GSettings}, @code{Desktop}. +@code{YCP}, @code{Tcl}, @code{Perl}, @code{PHP}, @code{Ruby}, +@code{GCC-source}, @code{NXStringTable}, @code{RST}, @code{RSJ}, @code{Glade}, +@code{Lua}, @code{JavaScript}, @code{Vala}, @code{GSettings}, @code{Desktop}. @item -C @itemx --c++ @@ -642,4 +642,10 @@ Display this help and exit. @opindex --version@r{, @code{xgettext} option} Output version information and exit. +@item -v +@itemx --verbose +@opindex -v@r{, @code{xgettext} option} +@opindex --verbose@r{, @code{xgettext} option} +Increase verbosity level. + @end table diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES index 54b4b4c82..5f7d21d9a 100644 --- a/gettext-tools/src/FILES +++ b/gettext-tools/src/FILES @@ -362,6 +362,9 @@ msgl-check.c | x-php.h | x-php.c | String extractor for PHP. +| x-ruby.h +| x-ruby.c +| String extractor for Ruby. | x-rst.h | x-rst.c | String extractor from .rst files, for Object Pascal. diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index 3349b9acb..875eb4eb0 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -59,7 +59,7 @@ rc-str-list.h xg-pos.h xg-encoding.h xg-mixed-string.h xg-arglist-context.h \ xg-arglist-callshape.h xg-arglist-parser.h xg-message.h \ x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \ x-scheme.h x-smalltalk.h x-java.h x-properties.h x-csharp.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 x-lua.h \ +x-tcl.h x-perl.h x-php.h x-ruby.h x-stringtable.h x-rst.h x-glade.h x-lua.h \ x-javascript.h x-vala.h x-gsettings.h x-desktop.h x-appdata.h EXTRA_DIST += FILES project-id @@ -206,6 +206,7 @@ xgettext_SOURCES += \ x-tcl.c \ x-perl.c \ x-php.c \ + x-ruby.c \ x-rst.c \ x-lua.c \ x-javascript.c \ diff --git a/gettext-tools/src/x-ruby.c b/gettext-tools/src/x-ruby.c new file mode 100644 index 000000000..e3714390a --- /dev/null +++ b/gettext-tools/src/x-ruby.c @@ -0,0 +1,249 @@ +/* xgettext Ruby backend. + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Bruno Haible , 2020. + + 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 "config.h" +#endif + +/* Specification. */ +#include "x-ruby.h" + +#include +#include +#include +#include +#include + +#include "message.h" +#include "sh-quote.h" +#include "spawn-pipe.h" +#include "wait-process.h" +#include "xvasprintf.h" +#include "x-po.h" +#include "xgettext.h" +#include "xg-message.h" +#include "c-strstr.h" +#include "read-catalog-abstract.h" +#include "error.h" +#include "gettext.h" + +/* A convenience macro. I don't like writing gettext() every time. */ +#define _(str) gettext (str) + +/* The Ruby syntax is defined in + https://ruby-doc.org/core-2.7.1/doc/syntax_rdoc.html + https://ruby-doc.org/core-2.7.1/doc/syntax/comments_rdoc.html + https://ruby-doc.org/core-2.7.1/doc/syntax/literals_rdoc.html + We don't parse Ruby directly, but instead rely on the 'rxgettext' program + from https://github.com/ruby-gettext/gettext . */ + + +/* ====================== Keyword set customization. ====================== */ + +/* This function currently has no effect. */ +void +x_ruby_extract_all (void) +{ +} + +/* This function currently has no effect. */ +void +x_ruby_keyword (const char *keyword) +{ +} + +/* This function currently has no effect. */ +void +init_flag_table_ruby (void) +{ +} + + +/* ========================= Extracting strings. ========================== */ + +void +extract_ruby (const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp) +{ + const char *progname = "rxgettext"; + char *dummy_filename; + msgdomain_list_ty *mdlp2; + int pass; + + dummy_filename = xasprintf (_("(output from '%s')"), progname); + + /* Invoke rgettext twice: + 1. to get the messages, without ruby-format flags. + 2. to get the 'xgettext:' comments that guide us while adding + [no-]ruby-format flags. */ + mdlp2 = msgdomain_list_alloc (true); + for (pass = 0; pass < 2; pass++) + { + char *argv[4]; + unsigned int i; + pid_t child; + int fd[1]; + FILE *fp; + int exitstatus; + + /* Prepare arguments. */ + argv[0] = (char *) progname; + i = 1; + + if (pass > 0) + argv[i++] = (char *) "--add-comments=xgettext:"; + else + { + if (add_all_comments) + argv[i++] = (char *) "--add-comments"; + else if (comment_tag != NULL) + argv[i++] = xasprintf ("--add-comments=%s", comment_tag); + } + + argv[i++] = (char *) real_filename; + + argv[i] = NULL; + + if (verbose) + { + char *command = shell_quote_argv (argv); + error (0, 0, "%s", command); + free (command); + } + + child = create_pipe_in (progname, progname, argv, + DEV_NULL, false, true, true, fd); + + fp = fdopen (fd[0], "r"); + if (fp == NULL) + error (EXIT_FAILURE, errno, _("fdopen() failed")); + + /* Read the resulting PO file. */ + extract_po (fp, dummy_filename, dummy_filename, flag_table, + pass == 0 ? mdlp : mdlp2); + + fclose (fp); + + /* Remove zombie process from process list, and retrieve exit status. */ + exitstatus = + wait_subprocess (child, progname, false, false, true, true, NULL); + if (exitstatus != 0) + error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"), + progname, exitstatus); + } + + /* Add [no-]ruby-format flags and process 'xgettext:' comments. + This processing is similar to the one done in remember_a_message(). */ + if (mdlp->nitems == 1 && mdlp2->nitems == 1) + { + message_list_ty *mlp = mdlp->item[0]->messages; + message_list_ty *mlp2 = mdlp2->item[0]->messages; + size_t j; + + for (j = 0; j < mlp->nitems; j++) + { + message_ty *mp = mlp->item[j]; + + if (!is_header (mp)) + { + /* Find 'xgettext:' comments and apply them to mp. */ + message_ty *mp2 = + message_list_search (mlp2, mp->msgctxt, mp->msgid); + + if (mp2 != NULL && mp2->comment_dot != NULL) + { + string_list_ty *mp2_comment_dot = mp2->comment_dot; + size_t k; + + for (k = 0; k < mp2_comment_dot->nitems; k++) + { + const char *s = mp2_comment_dot->item[k]; + + /* To reduce the possibility of unwanted matches we do a + two step match: the line must contain 'xgettext:' and + one of the possible format description strings. */ + const char *t = c_strstr (s, "xgettext:"); + if (t != NULL) + { + bool tmp_fuzzy; + enum is_format tmp_format[NFORMATS]; + struct argument_range tmp_range; + enum is_wrap tmp_wrap; + enum is_syntax_check tmp_syntax_check[NSYNTAXCHECKS]; + bool interesting; + size_t i; + + t += strlen ("xgettext:"); + + po_parse_comment_special (t, &tmp_fuzzy, tmp_format, + &tmp_range, &tmp_wrap, + tmp_syntax_check); + + interesting = false; + for (i = 0; i < NFORMATS; i++) + if (tmp_format[i] != undecided) + { + mp->is_format[i] = tmp_format[i]; + interesting = true; + } + if (has_range_p (tmp_range)) + { + intersect_range (mp, &tmp_range); + interesting = true; + } + if (tmp_wrap != undecided) + { + mp->do_wrap = tmp_wrap; + interesting = true; + } + for (i = 0; i < NSYNTAXCHECKS; i++) + if (tmp_syntax_check[i] != undecided) + { + mp->do_syntax_check[i] = tmp_syntax_check[i]; + interesting = true; + } + + /* If the "xgettext:" marker was followed by an + interesting keyword, and we updated our + is_format/do_wrap variables, eliminate the comment + from the #. comments. */ + if (interesting) + if (mp->comment_dot != NULL) + { + const char *removed = + string_list_remove (mp->comment_dot, s); + + if (removed != NULL) + free ((char *) removed); + } + } + } + } + + /* Now evaluate the consequences of the 'xgettext:' comments, */ + decide_is_format (mp); + decide_do_wrap (mp); + decide_syntax_check (mp); + } + } + } + + msgdomain_list_free (mdlp2); + + free (dummy_filename); +} diff --git a/gettext-tools/src/x-ruby.h b/gettext-tools/src/x-ruby.h new file mode 100644 index 000000000..6f29febd1 --- /dev/null +++ b/gettext-tools/src/x-ruby.h @@ -0,0 +1,50 @@ +/* xgettext Ruby backend. + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Bruno Haible , 2020. + + 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 . */ + + +#include + +#include "message.h" +#include "xg-arglist-context.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define EXTENSIONS_RUBY \ + { "rb", "Ruby" }, \ + +#define SCANNERS_RUBY \ + { "Ruby", NULL, extract_ruby, \ + &flag_table_ruby, &formatstring_ruby, NULL }, \ + +extern void extract_ruby (const char *real_filename, + const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp); + +extern void x_ruby_keyword (const char *keyword); +extern void x_ruby_extract_all (void); + +extern void init_flag_table_ruby (void); + + +#ifdef __cplusplus +} +#endif diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 3c85c1f03..72b6e2b08 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -106,6 +106,7 @@ #include "x-tcl.h" #include "x-perl.h" #include "x-php.h" +#include "x-ruby.h" #include "x-stringtable.h" #include "x-rst.h" #include "x-glade.h" @@ -166,6 +167,9 @@ static catalog_output_format_ty output_syntax = &output_format_po; /* If nonzero omit header with information about this run. */ int xgettext_omit_header; +/* Be more verbose. */ +int verbose = 0; + /* Table of flag_context_list_ty tables. */ static flag_context_list_table_ty flag_table_c; static flag_context_list_table_ty flag_table_cxx_qt; @@ -186,6 +190,7 @@ static flag_context_list_table_ty flag_table_ycp; 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; +static flag_context_list_table_ty flag_table_ruby; static flag_context_list_table_ty flag_table_lua; static flag_context_list_table_ty flag_table_javascript; static flag_context_list_table_ty flag_table_vala; @@ -210,7 +215,7 @@ static locating_rule_list_ty *its_locating_rules; " " \ "" -/* If nonzero add comments used by itstool. */ +/* If nonzero add comments used by itstool. */ static bool add_itstool_comments = false; /* Long options. */ @@ -263,6 +268,7 @@ static const struct option long_options[] = { "stringtable-output", no_argument, NULL, CHAR_MAX + 7 }, { "style", required_argument, NULL, CHAR_MAX + 15 }, { "trigraphs", no_argument, NULL, 'T' }, + { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "width", required_argument, NULL, 'w' }, { NULL, 0, NULL, 0 } @@ -373,12 +379,13 @@ main (int argc, char *argv[]) init_flag_table_tcl (); init_flag_table_perl (); init_flag_table_php (); + init_flag_table_ruby (); init_flag_table_lua (); init_flag_table_javascript (); init_flag_table_vala (); while ((optchar = getopt_long (argc, argv, - "ac::Cd:D:eEf:Fhijk::l:L:m::M::no:p:sTVw:W:x:", + "ac::Cd:D:eEf:Fhijk::l:L:m::M::no:p:sTvVw:W:x:", long_options, NULL)) != EOF) switch (optchar) { @@ -399,6 +406,7 @@ main (int argc, char *argv[]) x_tcl_extract_all (); x_perl_extract_all (); x_php_extract_all (); + x_ruby_extract_all (); x_lua_extract_all (); x_javascript_extract_all (); x_vala_extract_all (); @@ -478,6 +486,7 @@ main (int argc, char *argv[]) x_tcl_keyword (optarg); x_perl_keyword (optarg); x_php_keyword (optarg); + x_ruby_keyword (optarg); x_lua_keyword (optarg); x_javascript_keyword (optarg); x_vala_keyword (optarg); @@ -541,6 +550,10 @@ main (int argc, char *argv[]) x_c_trigraphs (); break; + case 'v': + verbose++; + break; + case 'V': do_version = true; break; @@ -1073,8 +1086,8 @@ Choice of input file language:\n")); (C, C++, ObjectiveC, PO, Shell, Python, Lisp,\n\ EmacsLisp, librep, Scheme, Smalltalk, Java,\n\ JavaProperties, C#, awk, YCP, Tcl, Perl, PHP,\n\ - GCC-source, NXStringTable, RST, RSJ, Glade,\n\ - Lua, JavaScript, Vala, Desktop)\n")); + Ruby, GCC-source, NXStringTable, RST, RSJ,\n\ + Glade, Lua, JavaScript, Vala, Desktop)\n")); printf (_("\ -C, --c++ shorthand for --language=C++\n")); printf (_("\ @@ -1218,6 +1231,8 @@ Informative output:\n")); -h, --help display this help and exit\n")); printf (_("\ -V, --version output version information and exit\n")); + printf (_("\ + -v, --verbose increase verbosity level\n")); printf ("\n"); /* TRANSLATORS: The first placeholder is the web address of the Savannah project of this package. The second placeholder is the bug-reporting @@ -1555,6 +1570,11 @@ xgettext_record_flag (const char *optionstring) name_start, name_end, argnum, value, pass); break; + case format_ruby: + flag_context_list_table_insert (&flag_table_ruby, 0, + name_start, name_end, + argnum, value, pass); + break; case format_gcc_internal: flag_context_list_table_insert (&flag_table_gcc_internal, 0, name_start, name_end, @@ -2126,6 +2146,7 @@ language_to_extractor (const char *name) SCANNERS_TCL SCANNERS_PERL SCANNERS_PHP + SCANNERS_RUBY SCANNERS_STRINGTABLE SCANNERS_RST SCANNERS_GLADE @@ -2217,6 +2238,7 @@ extension_to_language (const char *extension) EXTENSIONS_TCL EXTENSIONS_PERL EXTENSIONS_PHP + EXTENSIONS_RUBY EXTENSIONS_STRINGTABLE EXTENSIONS_RST EXTENSIONS_GLADE diff --git a/gettext-tools/src/xgettext.h b/gettext-tools/src/xgettext.h index c6db36148..c6c98da55 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-2009, 2011, 2013-2014, 2018 Free Software Foundation, Inc. + Copyright (C) 2001-2003, 2005-2006, 2008-2009, 2011, 2013-2014, 2018, 2020 Free Software Foundation, Inc. Written by Peter Miller and Bruno Haible , 2001. @@ -50,6 +50,9 @@ extern const char *msgstr_suffix; If false, keep the header entry present in the input. */ extern int xgettext_omit_header; +/* Be more verbose. */ +extern int verbose; + extern enum is_syntax_check default_syntax_check[NSYNTAXCHECKS]; /* Language dependent format string parser. diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 055b92c74..14272f4b8 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -108,6 +108,7 @@ TESTS = gettext-1 gettext-2 \ xgettext-rst-1 xgettext-rst-2 \ xgettext-python-1 xgettext-python-2 xgettext-python-3 \ xgettext-python-4 \ + xgettext-ruby-1 \ xgettext-scheme-1 xgettext-scheme-2 xgettext-scheme-3 \ xgettext-scheme-4 \ xgettext-sh-1 xgettext-sh-2 xgettext-sh-3 xgettext-sh-4 xgettext-sh-5 \ @@ -146,6 +147,7 @@ TESTS = gettext-1 gettext-2 \ format-perl-mixed-1 format-perl-mixed-2 \ format-qt-1 format-qt-2 \ format-qt-plural-1 format-qt-plural-2 \ + format-ruby-1 format-ruby-2 \ format-scheme-1 format-scheme-2 \ format-sh-1 format-sh-2 \ format-tcl-1 format-tcl-2 \ diff --git a/gettext-tools/tests/format-ruby-1 b/gettext-tools/tests/format-ruby-1 new file mode 100755 index 000000000..2294b3365 --- /dev/null +++ b/gettext-tools/tests/format-ruby-1 @@ -0,0 +1,304 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test recognition of Ruby format strings. + +(rxgettext --version) >/dev/null 2>/dev/null \ + || { echo "Skipping test: rxgettext not found"; Exit 77; } + +cat <<\EOF > f-r-1.data +# Valid: no argument +"abc%%" +# Valid: one string argument (unnumbered) +"abc%s" +# Valid: one string argument (numbered) +"abc%1$s" +# Valid: one string argument (named) +"abc%s" +# Valid: one string argument (named) +"abc%{foo}" +# Valid: one escaped string argument (unnumbered) +"abc%p" +# Valid: one escaped string argument (numbered) +"abc%1$p" +# Valid: one escaped string argument (named) +"abc%p" +# Valid: one character argument (unnumbered) +"abc%c" +# Valid: one character argument (numbered) +"abc%1$c" +# Valid: one character argument (named) +"abc%c" +# Valid: one integer argument (unnumbered) +"abc%d" +# Valid: one integer argument (numbered) +"abc%1$d" +# Valid: one integer argument (named) +"abc%d" +# Valid: one integer argument (unnumbered) +"abc%i" +# Valid: one integer argument (numbered) +"abc%1$i" +# Valid: one integer argument (named) +"abc%i" +# Valid: one integer argument (unnumbered) +"abc%u" +# Valid: one integer argument (numbered) +"abc%1$u" +# Valid: one integer argument (named) +"abc%u" +# Valid: one integer argument (unnumbered) +"abc%o" +# Valid: one integer argument (numbered) +"abc%1$o" +# Valid: one integer argument (named) +"abc%o" +# Valid: one integer argument (unnumbered) +"abc%x" +# Valid: one integer argument (numbered) +"abc%1$x" +# Valid: one integer argument (named) +"abc%x" +# Valid: one integer argument (unnumbered) +"abc%X" +# Valid: one integer argument (numbered) +"abc%1$X" +# Valid: one integer argument (named) +"abc%X" +# Valid: one integer argument (unnumbered) +"abc%b" +# Valid: one integer argument (numbered) +"abc%1$b" +# Valid: one integer argument (named) +"abc%b" +# Valid: one integer argument (unnumbered) +"abc%B" +# Valid: one integer argument (numbered) +"abc%1$B" +# Valid: one integer argument (named) +"abc%B" +# Valid: one floating-point argument (unnumbered) +"abc%f" +# Valid: one floating-point argument (numbered) +"abc%1$f" +# Valid: one floating-point argument (named) +"abc%f" +# Valid: one floating-point argument (unnumbered) +"abc%g" +# Valid: one floating-point argument (numbered) +"abc%1$g" +# Valid: one floating-point argument (named) +"abc%g" +# Valid: one floating-point argument (unnumbered) +"abc%G" +# Valid: one floating-point argument (numbered) +"abc%1$G" +# Valid: one floating-point argument (named) +"abc%G" +# Valid: one floating-point argument (unnumbered) +"abc%e" +# Valid: one floating-point argument (numbered) +"abc%1$e" +# Valid: one floating-point argument (named) +"abc%e" +# Valid: one floating-point argument (unnumbered) +"abc%E" +# Valid: one floating-point argument (numbered) +"abc%1$E" +# Valid: one floating-point argument (named) +"abc%E" +# Valid: one floating-point argument (unnumbered) +"abc%a" +# Valid: one floating-point argument (numbered) +"abc%1$a" +# Valid: one floating-point argument (named) +"abc%a" +# Valid: one floating-point argument (unnumbered) +"abc%A" +# Valid: one floating-point argument (numbered) +"abc%1$A" +# Valid: one floating-point argument (named) +"abc%A" +# Valid: one argument with flags (unnumbered) +"abc%0#g" +# Valid: one argument with flags (numbered) +"abc%1$0#g" +# Valid: one argument with flags (numbered) +"abc%0#1$g" +# Valid: one argument with flags (named) +"abc%0#g" +# Valid: one argument with flags (named) +"abc%0#g" +# Valid: one argument with width (unnumbered) +"abc%2g" +# Valid: one argument with width (numbered) +"abc%1$2g" +# Valid: one argument with width (named) +"abc%2g" +# Valid: one argument with width (named) +"abc%2g" +# Valid: one argument with width (unnumbered) +"abc%*g" +# Valid: one argument with width (numbered) +"abc%2$*1$g" +# Valid: one argument with precision (unnumbered) +"abc%.4g" +# Valid: one argument with precision (numbered) +"abc%1$.4g" +# Valid: one argument with precision (named) +"abc%.4g" +# Valid: one argument with precision (named) +"abc%.4g" +# Valid: one argument with precision (unnumbered) +"abc%.*g" +# Valid: one argument with precision (numbered) +"abc%2$.*1$g" +# Valid: one argument with width and precision (unnumbered) +"abc%14.4g" +# Valid: one argument with width and precision (numbered) +"abc%1$14.4g" +# Valid: one argument with width and precision (named) +"abc%14.4g" +# Valid: one argument with width and precision (named) +"abc%14.4g" +# Valid: one argument with width and precision (named) +"abc%14.4g" +# Valid: one argument with width and precision (unnumbered) +"abc%14.*g" +# Valid: one argument with width and precision (numbered) +"abc%2$14.*1$g" +# Valid: one argument with width and precision (unnumbered) +"abc%*.4g" +# Valid: one argument with width and precision (numbered) +"abc%2$*1$.4g" +# Valid: one argument with width and precision (unnumbered) +"abc%*.*g" +# Valid: one argument with width and precision (numbered) +"abc%3$*1$.*2$g" +# Invalid: unterminated directive +"abc%" +# Invalid: unterminated name +"abc%" +# Invalid: unknown format specifier (unnumbered) +"abc%y" +# Invalid: mixing unnumbered and numbered in the same directive +"abc%2$*g" +# Invalid: mixing unnumbered and numbered in the same directive +"abc%*1$g" +# Invalid: mixing unnumbered and numbered in the same directive +"abc%2$.*g" +# Invalid: mixing unnumbered and numbered in the same directive +"abc%.*1$g" +# Invalid: mixing unnumbered and numbered in different directives +"abc%d%2$g" +# Invalid: mixing unnumbered and numbered in different directives +"abc%1$d%g" +# Invalid: mixing unnumbered and named in the same directive +"abc%*g" +# Invalid: mixing unnumbered and named in the same directive +"abc%.*g" +# Invalid: mixing unnumbered and named in different directives +"abc%d%g" +# Invalid: mixing unnumbered and named in different directives +"abc%d%g" +# Invalid: mixing numbered and named in the same directive +"abc%*1$g" +# Invalid: mixing numbered and named in the same directive +"abc%.*1$g" +# Invalid: mixing numbered and named in different directives +"abc%2$d%g" +# Invalid: mixing numbered and named in different directives +"abc%d%2$g" +# Invalid: flags after width (unnumbered) +"abc%*0g" +# Invalid: flags after width (numbered) +"abc%2$*1$0g" +# Invalid: flags after precision (unnumbered) +"abc%.*0g" +# Invalid: flags after precision (numbered) +"abc%2$.*1$0g" +# Invalid: width after precision (unnumbered) +"abc%.*14g" +# Invalid: width after precision (unnumbered) +"abc%.4*g" +# Invalid: width after precision (unnumbered) +"abc%.**g" +# Invalid: width after precision (numbered) +"abc%2$.*1$14g" +# Invalid: width after precision (numbered) +"abc%2$.4*1$g" +# Invalid: width after precision (numbered) +"abc%3$.*1$*2$g" +# Invalid: twice width (unnumbered) +"abc%2*g" +# Invalid: twice width (unnumbered) +"abc%*2g" +# Invalid: twice width (numbered) +"abc%2$2*1$g" +# Invalid: twice width (numbered) +"abc%2$*1$2g" +# Invalid: twice precision (unnumbered) +"abc%.4.2g" +# Invalid: twice precision (numbered) +"abc%1$.4.2g" +# Valid: three arguments +"abc%d%u%u" +# Valid: an unused argument +"abc%2$d%3$u" +# Valid: a named argument +"abc%d" +# Valid: a named argument +"abc%{value}" +# Valid: an empty name +"abc%<>d" +# Valid: an empty name +"abc%{}" +# Valid: ignored named argument +"abc%%" +# Valid: three arguments, two with equal names +"abc%4x,%c,%u" +# Invalid: argument with conflicting types +"abc%4x,%c,%s" +# Valid: no conflict +"abc%s,%{addr}" +EOF + +: ${XGETTEXT=xgettext} +n=0 +while read comment; do + read string + n=`expr $n + 1` + cat < f-r-1-$n.in +gettext(${string}); +EOF + ${XGETTEXT} -L Ruby -o f-r-1-$n.po f-r-1-$n.in || Exit 1 + test -f f-r-1-$n.po || Exit 1 + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if grep ruby-format f-r-1-$n.po > /dev/null; then + : + else + fail=yes + fi + else + if grep ruby-format f-r-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-r-1-$n.in 1>&2 + echo "Got:" 1>&2 + cat f-r-1-$n.po 1>&2 + Exit 1 + fi + rm -f f-r-1-$n.in f-r-1-$n.po +done < f-r-1.data + +Exit 0 diff --git a/gettext-tools/tests/format-ruby-2 b/gettext-tools/tests/format-ruby-2 new file mode 100755 index 000000000..cce98e984 --- /dev/null +++ b/gettext-tools/tests/format-ruby-2 @@ -0,0 +1,361 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test checking of Ruby format strings. + +cat <<\EOF > f-r-2.data +# Valid: %% doesn't count +msgid "abc%%def" +msgstr "xyz" +# Invalid: invalid msgstr +msgid "abc%%def" +msgstr "xyz%" +# Valid: same arguments, with different widths (argument list) +msgid "abc%2sdef" +msgstr "xyz%3s" +# Valid: same arguments, with different widths (argument list) +msgid "abc%2sdef" +msgstr "xyz%1$3s" +# Valid: same arguments, with different widths (named) +msgid "abc%2sdef" +msgstr "xyz%3s" +# Valid: same arguments, with different widths (named) +msgid "abc%5s%