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.
This commit is contained in:
Bruno Haible
2020-04-27 00:19:07 +02:00
parent c4da9b03f0
commit 6c39c38b09
14 changed files with 1150 additions and 12 deletions
+7
View File
@@ -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
+4
View File
@@ -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
+60 -2
View File
@@ -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
+10 -4
View File
@@ -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
+3
View File
@@ -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.
+2 -1
View File
@@ -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 \
+249
View File
@@ -0,0 +1,249 @@
/* xgettext Ruby backend.
Copyright (C) 2020 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 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 <https://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* Specification. */
#include "x-ruby.h"
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}
+50
View File
@@ -0,0 +1,50 @@
/* xgettext Ruby backend.
Copyright (C) 2020 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 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 <https://www.gnu.org/licenses/>. */
#include <stdio.h>
#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
+26 -4
View File
@@ -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;
" <its:translateRule selector=\"/*\" translate=\"no\"/>" \
"</its: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
+4 -1
View File
@@ -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 <millerp@canb.auug.org.au>
and Bruno Haible <haible@clisp.cons.org>, 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.
+2
View File
@@ -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 \
+304
View File
@@ -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%<foo>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%<foo>p"
# Valid: one character argument (unnumbered)
"abc%c"
# Valid: one character argument (numbered)
"abc%1$c"
# Valid: one character argument (named)
"abc%<foo>c"
# Valid: one integer argument (unnumbered)
"abc%d"
# Valid: one integer argument (numbered)
"abc%1$d"
# Valid: one integer argument (named)
"abc%<foo>d"
# Valid: one integer argument (unnumbered)
"abc%i"
# Valid: one integer argument (numbered)
"abc%1$i"
# Valid: one integer argument (named)
"abc%<foo>i"
# Valid: one integer argument (unnumbered)
"abc%u"
# Valid: one integer argument (numbered)
"abc%1$u"
# Valid: one integer argument (named)
"abc%<foo>u"
# Valid: one integer argument (unnumbered)
"abc%o"
# Valid: one integer argument (numbered)
"abc%1$o"
# Valid: one integer argument (named)
"abc%<foo>o"
# Valid: one integer argument (unnumbered)
"abc%x"
# Valid: one integer argument (numbered)
"abc%1$x"
# Valid: one integer argument (named)
"abc%<foo>x"
# Valid: one integer argument (unnumbered)
"abc%X"
# Valid: one integer argument (numbered)
"abc%1$X"
# Valid: one integer argument (named)
"abc%<foo>X"
# Valid: one integer argument (unnumbered)
"abc%b"
# Valid: one integer argument (numbered)
"abc%1$b"
# Valid: one integer argument (named)
"abc%<foo>b"
# Valid: one integer argument (unnumbered)
"abc%B"
# Valid: one integer argument (numbered)
"abc%1$B"
# Valid: one integer argument (named)
"abc%<foo>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%<foo>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%<foo>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%<foo>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%<foo>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%<foo>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%<foo>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%<foo>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%<foo>0#g"
# Valid: one argument with flags (named)
"abc%0#<foo>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%<foo>2g"
# Valid: one argument with width (named)
"abc%2<foo>g"
# 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%<foo>.4g"
# Valid: one argument with precision (named)
"abc%.4<foo>g"
# 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%<foo>14.4g"
# Valid: one argument with width and precision (named)
"abc%14<foo>.4g"
# Valid: one argument with width and precision (named)
"abc%14.4<foo>g"
# 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%<value"
# Invalid: unterminated name
"abc%{value"
# Invalid: unterminated directive
"abc%<value>"
# 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%*<foo>g"
# Invalid: mixing unnumbered and named in the same directive
"abc%.*<foo>g"
# Invalid: mixing unnumbered and named in different directives
"abc%d%<foo>g"
# Invalid: mixing unnumbered and named in different directives
"abc%<foo>d%g"
# Invalid: mixing numbered and named in the same directive
"abc%*1$<foo>g"
# Invalid: mixing numbered and named in the same directive
"abc%.*1$<foo>g"
# Invalid: mixing numbered and named in different directives
"abc%2$d%<foo>g"
# Invalid: mixing numbered and named in different directives
"abc%<foo>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%<value>d"
# Valid: a named argument
"abc%{value}"
# Valid: an empty name
"abc%<>d"
# Valid: an empty name
"abc%{}"
# Valid: ignored named argument
"abc%<dummy>%"
# Valid: three arguments, two with equal names
"abc%<addr>4x,%<char>c,%<addr>u"
# Invalid: argument with conflicting types
"abc%<addr>4x,%<char>c,%<addr>s"
# Valid: no conflict
"abc%<addr>s,%{addr}"
EOF
: ${XGETTEXT=xgettext}
n=0
while read comment; do
read string
n=`expr $n + 1`
cat <<EOF > 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
+361
View File
@@ -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%<foo>2sdef"
msgstr "xyz%<foo>3s"
# Valid: same arguments, with different widths (named)
msgid "abc%<date>5s%<time>4s"
msgstr "xyz%<date>4s%<time>5s"
# Invalid: too few arguments (argument list)
msgid "abc%sdef%u"
msgstr "xyz%s"
# Invalid: too few arguments (named)
msgid "abc%<foo>sdef%<bar>u"
msgstr "xyz%<foo>s"
# Invalid: too many arguments (argument list)
msgid "abc%udef"
msgstr "xyz%uvw%c"
# Invalid: too many arguments (named)
msgid "abc%<foo>udef"
msgstr "xyz%<foo>uvw%<bar>c"
# Valid: permutation (argument list)
msgid "abc%3$d%1$c%2$sdef"
msgstr "xyz%2$s%1$c%3$d"
# Valid: permutation (named)
msgid "abc%<3>d%<1>c%<2>sdef"
msgstr "xyz%<2>s%<1>c%<3>d"
# Invalid: missing argument (named)
msgid "abc%<2>sdef%<1>u"
msgstr "xyz%<1>u"
# Invalid: missing argument (named)
msgid "abc%<1>sdef%<2>u"
msgstr "xyz%<2>u"
# Invalid: added argument (named)
msgid "abc%<foo>udef"
msgstr "xyz%<foo>uvw%<char>c"
# Invalid: added argument (named)
msgid "abc%<foo>udef"
msgstr "xyz%<zoo>cvw%<foo>u"
# Invalid: unnamed vs. named arguments
msgid "abc%sdef"
msgstr "xyz%<value>s"
# Invalid: named vs. unnamed arguments
msgid "abc%{value}def"
msgstr "xyz%s"
# Valid: unnumbered vs. numbered arguments
msgid "abc%sdef%d"
msgstr "xyz%2$duvw%1$s"
# Valid: numbered vs. unnumbered arguments
msgid "abc%1$sdef%2$d"
msgstr "xyz%suvw%d"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%i"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%u"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%o"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%x"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%X"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%b"
# Valid: type compatibility (argument list)
msgid "abc%d"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%i"
msgstr "xyz%u"
# Valid: type compatibility (argument list)
msgid "abc%i"
msgstr "xyz%o"
# Valid: type compatibility (argument list)
msgid "abc%i"
msgstr "xyz%x"
# Valid: type compatibility (argument list)
msgid "abc%i"
msgstr "xyz%X"
# Valid: type compatibility (argument list)
msgid "abc%i"
msgstr "xyz%b"
# Valid: type compatibility (argument list)
msgid "abc%i"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%u"
msgstr "xyz%o"
# Valid: type compatibility (argument list)
msgid "abc%u"
msgstr "xyz%x"
# Valid: type compatibility (argument list)
msgid "abc%u"
msgstr "xyz%X"
# Valid: type compatibility (argument list)
msgid "abc%u"
msgstr "xyz%b"
# Valid: type compatibility (argument list)
msgid "abc%u"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%o"
msgstr "xyz%x"
# Valid: type compatibility (argument list)
msgid "abc%o"
msgstr "xyz%X"
# Valid: type compatibility (argument list)
msgid "abc%o"
msgstr "xyz%b"
# Valid: type compatibility (argument list)
msgid "abc%o"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%x"
msgstr "xyz%X"
# Valid: type compatibility (argument list)
msgid "abc%x"
msgstr "xyz%b"
# Valid: type compatibility (argument list)
msgid "abc%x"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%X"
msgstr "xyz%b"
# Valid: type compatibility (argument list)
msgid "abc%X"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%b"
msgstr "xyz%B"
# Valid: type compatibility (argument list)
msgid "abc%f"
msgstr "xyz%g"
# Valid: type compatibility (argument list)
msgid "abc%f"
msgstr "xyz%G"
# Valid: type compatibility (argument list)
msgid "abc%f"
msgstr "xyz%e"
# Valid: type compatibility (argument list)
msgid "abc%f"
msgstr "xyz%E"
# Valid: type compatibility (argument list)
msgid "abc%f"
msgstr "xyz%a"
# Valid: type compatibility (argument list)
msgid "abc%f"
msgstr "xyz%A"
# Valid: type compatibility (argument list)
msgid "abc%g"
msgstr "xyz%G"
# Valid: type compatibility (argument list)
msgid "abc%g"
msgstr "xyz%e"
# Valid: type compatibility (argument list)
msgid "abc%g"
msgstr "xyz%E"
# Valid: type compatibility (argument list)
msgid "abc%g"
msgstr "xyz%a"
# Valid: type compatibility (argument list)
msgid "abc%g"
msgstr "xyz%A"
# Valid: type compatibility (argument list)
msgid "abc%G"
msgstr "xyz%e"
# Valid: type compatibility (argument list)
msgid "abc%G"
msgstr "xyz%E"
# Valid: type compatibility (argument list)
msgid "abc%G"
msgstr "xyz%a"
# Valid: type compatibility (argument list)
msgid "abc%G"
msgstr "xyz%A"
# Valid: type compatibility (argument list)
msgid "abc%e"
msgstr "xyz%E"
# Valid: type compatibility (argument list)
msgid "abc%e"
msgstr "xyz%a"
# Valid: type compatibility (argument list)
msgid "abc%e"
msgstr "xyz%A"
# Valid: type compatibility (argument list)
msgid "abc%E"
msgstr "xyz%a"
# Valid: type compatibility (argument list)
msgid "abc%E"
msgstr "xyz%A"
# Valid: type compatibility (argument list)
msgid "abc%a"
msgstr "xyz%A"
# Valid: type compatibility (named)
msgid "abc%<foo>d"
msgstr "xyz%<foo>x"
# Valid: type compatibility (named)
msgid "abc%<foo>s"
msgstr "xyz%{foo}"
# Valid: type compatibility (named)
msgid "abc%{foo}"
msgstr "xyz%<foo>s"
# Invalid: type incompatibility (argument list)
msgid "abc%c"
msgstr "xyz%s"
# Invalid: type incompatibility (argument list)
msgid "abc%c"
msgstr "xyz%.0s"
# Invalid: type incompatibility (argument list)
msgid "abc%c"
msgstr "xyz%p"
# Invalid: type incompatibility (argument list)
msgid "abc%c"
msgstr "xyz%i"
# Invalid: type incompatibility (argument list)
msgid "abc%c"
msgstr "xyz%e"
# Invalid: type incompatibility (argument list)
msgid "abc%s"
msgstr "xyz%p"
# Invalid: type incompatibility (argument list)
msgid "abc%s"
msgstr "xyz%i"
# Invalid: type incompatibility (argument list)
msgid "abc%.0s"
msgstr "xyz%i"
# Invalid: type incompatibility (argument list)
msgid "abc%s"
msgstr "xyz%e"
# Invalid: type incompatibility (argument list)
msgid "abc%.0s"
msgstr "xyz%e"
# Invalid: type incompatibility (argument list)
msgid "abc%p"
msgstr "xyz%i"
# Invalid: type incompatibility (argument list)
msgid "abc%p"
msgstr "xyz%e"
# Invalid: type incompatibility (argument list)
msgid "abc%i"
msgstr "xyz%e"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%<foo>s"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%{foo}"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%<foo>.0s"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%.0{foo}"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%<foo>p"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%<foo>i"
# Invalid: type incompatibility (named)
msgid "abc%<foo>c"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility (named)
msgid "abc%<foo>s"
msgstr "xyz%<foo>p"
# Invalid: type incompatibility (named)
msgid "abc%{foo}"
msgstr "xyz%<foo>p"
# Invalid: type incompatibility (named)
msgid "abc%<foo>s"
msgstr "xyz%<foo>i"
# Invalid: type incompatibility (named)
msgid "abc%{foo}"
msgstr "xyz%<foo>i"
# Invalid: type incompatibility (named)
msgid "abc%<foo>.0s"
msgstr "xyz%<foo>i"
# Invalid: type incompatibility (named)
msgid "abc%.0{foo}"
msgstr "xyz%<foo>i"
# Invalid: type incompatibility (named)
msgid "abc%<foo>s"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility (named)
msgid "abc%{foo}"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility (named)
msgid "abc%<foo>.0s"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility (named)
msgid "abc%.0{foo}"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility (named)
msgid "abc%<foo>p"
msgstr "xyz%<foo>i"
# Invalid: type incompatibility (named)
msgid "abc%<foo>p"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility (named)
msgid "abc%<foo>i"
msgstr "xyz%<foo>e"
# Invalid: type incompatibility for width (argument list)
msgid "abc%g%*g"
msgstr "xyz%*g%g"
EOF
: ${MSGFMT=msgfmt}
n=0
while read comment; do
read msgid_line
read msgstr_line
n=`expr $n + 1`
cat <<EOF > f-r-2-$n.po
#, ruby-format
${msgid_line}
${msgstr_line}
EOF
fail=
if echo "$comment" | grep 'Valid:' > /dev/null; then
if ${MSGFMT} --check-format -o f-r-2-$n.mo f-r-2-$n.po; then
:
else
fail=yes
fi
else
${MSGFMT} --check-format -o f-r-2-$n.mo f-r-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-r-2-$n.po 1>&2
Exit 1
fi
rm -f f-r-2-$n.po f-r-2-$n.mo
done < f-r-2.data
Exit 0
+68
View File
@@ -0,0 +1,68 @@
#!/bin/sh
. "${srcdir=.}/init.sh"; path_prepend_ . ../src
# Test of Ruby support.
(rxgettext --version) >/dev/null 2>/dev/null \
|| { echo "Skipping test: rxgettext not found"; Exit 77; }
cat <<\EOF > xg-ru-1.rb
_("abc")
# Some comment.
_("abc%%def")
_("abc%{foo}def")
# xgettext: no-ruby-format
_("abc%{bar}def")
_("some %d people")
EOF
: ${XGETTEXT=xgettext}
${XGETTEXT} --add-comments --no-location -o xg-ru-1.tmp xg-ru-1.rb 2>xg-ru-1.err
test $? = 0 || { cat xg-ru-1.err; Exit 1; }
func_filter_POT_Creation_Date xg-ru-1.tmp xg-ru-1.pot
cat <<\EOF > xg-ru-1.ok
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "abc"
msgstr ""
#. Some comment.
#, ruby-format
msgid "abc%%def"
msgstr ""
#, ruby-format
msgid "abc%{foo}def"
msgstr ""
#, no-ruby-format
msgid "abc%{bar}def"
msgstr ""
#, ruby-format
msgid "some %d people"
msgstr ""
EOF
: ${DIFF=diff}
${DIFF} xg-ru-1.ok xg-ru-1.pot
result=$?
exit $result