xgettext: Allow inhibiting the warnings about URLs and email addresses.

Reported at <https://savannah.gnu.org/bugs/?67672>.

* gettext-tools/src/message.h (enum syntax_check_type): Add sc_url, sc_email.
(NSYNTAXCHECKS): Increase by 2.
* gettext-tools/src/message.c (syntax_check_name): Update.
* gettext-tools/src/xgettext.h (default_syntax_check): Add comment.
* gettext-tools/src/xgettext.c (default_syntax_check): Add initializer.
(main): Recognize --no-check option.
(usage): Document --no-check option.
* gettext-tools/src/xg-message.c (decide_syntax_check): Assume that
default_syntax_check[i] != undecided.
* gettext-tools/src/xg-check.c (syntax_check_function): Remove the second
argument.
(string_has_ascii_ellipsis): New function, extracted from
syntax_check_ellipsis_unicode.
(message_has_ascii_ellipsis): New function.
(syntax_check_ellipsis_unicode): Remove the second argument. Simplify. Emit only
a single error for both msgid and msgid_plural.
(string_has_space_ellipsis): New function, extracted from
syntax_check_space_ellipsis.
(message_has_space_ellipsis): New function.
(syntax_check_space_ellipsis): Remove the second argument. Simplify. Emit only
a single error for both msgid and msgid_plural.
(syntax_check_quote_unicode): Remove the second argument.
(syntax_check_bullet_unicode_string): New function, extracted from
syntax_check_bullet_unicode.
(syntax_check_bullet_unicode): Remove the second argument. Simplify.
(string_has_url): Don't recognize 'mailto:' URLs.
(syntax_check_url, syntax_check_email): New functions, extracted from
url_check_message.
(url_check_message): Remove function.
(sc_funcs): Add syntax_check_url, syntax_check_email.
(syntax_check_message): Simplify.
(xgettext_check_message_list): Don't invoke url_check_message.
* gettext-tools/tests/xgettext-14: Update after xg-check.c changesd.
* gettext-tools/tests/xgettext-20: Add more testcases.
* gettext-tools/doc/xgettext.texi: Document the --no-check option.
* gettext-tools/doc/gettext.texi: Bump copyright year.
* NEWS: Mention the change.
This commit is contained in:
Bruno Haible 2026-01-10 20:16:46 +01:00
parent 213b865692
commit f1459a89e2
11 changed files with 423 additions and 248 deletions

10
NEWS
View File

@ -43,6 +43,16 @@ Version 1.0 - December 2025
message.
- The documentation has a new chapter "Pretranslation".
# Improvements for maintainers:
* xgettext:
- The refactoring suggestion when a translatable string contains an URL
or email address can now be inhibited through a command-line option
'--no-check=url' or '--no-check=email', or through a comment in the
source code of the form
/* xgettext: no-url-check */
or
/* xgettext: no-email-check */
# Programming languages support:
* OCaml:
- xgettext now supports OCaml.

View File

@ -94,7 +94,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-2025 Free Software Foundation, Inc.
Copyright (C) 1995-1998, 2001-2026 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
@ -129,7 +129,7 @@ A copy of the license is included in @ref{GNU GPL}.
@page
@vskip 0pt plus 1filll
@c @insertcopying
Copyright (C) 1995-1998, 2001-2025 Free Software Foundation, Inc.
Copyright (C) 1995-1998, 2001-2026 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

View File

@ -1,5 +1,5 @@
@c This file is part of the GNU gettext manual.
@c Copyright (C) 1995-2025 Free Software Foundation, Inc.
@c Copyright (C) 1995-2026 Free Software Foundation, Inc.
@c See the file gettext.texi for copying conditions.
@pindex xgettext
@ -188,8 +188,8 @@ the line with the string there is merely a blank line.
@item --check[=@var{CHECK}]
@opindex --check@r{, @code{xgettext} option}
@cindex supported syntax checks, @code{xgettext}
Perform a syntax check on msgid and msgid_plural. The supported checks
are:
Perform a syntax check on msgid and msgid_plural.
The supported checks that are disabled by default and that can be enabled are:
@table @samp
@item ellipsis-unicode
@ -223,11 +223,37 @@ where @var{name} is the name of a valid syntax check. If a flag is
prefixed by @code{no-}, the meaning is negated.
Some tests apply the checks to each sentence within the msgid, rather
than the whole string. xgettext detects the end of sentence by
than the whole string. @code{xgettext} detects the end of sentence by
performing a pattern match, which usually looks for a period followed by
a certain number of spaces. The number is specified with the
@code{--sentence-end} option.
@item --no-check[=@var{CHECK}]
@opindex --no-check@r{, @code{xgettext} option}
Don't perform a syntax check on msgid and msgid_plural
that is enabled by default.
The supported checks that are enabled by default are:
@table @samp
@item url
Prohibit a URL inside a string.
@item email
Prohibit an email address inside a string.
@end table
The option has an effect on all input files.
To disable a check for a certain string,
you can mark it with an @code{xgettext:} special comment in the source file.
For example, if you want to suppress the check on a particular string,
add the following comment:
@example
/* xgettext: no-email-check */
gettext ("Specify your@@email-address.com here.");
@end example
@item --sentence-end[=@var{TYPE}]
@opindex --sentence-end@r{, @code{xgettext} option}
@cindex sentence end markers, @code{xgettext}

View File

@ -1,5 +1,5 @@
/* GNU gettext - internationalization aids
Copyright (C) 1995-2025 Free Software Foundation, Inc.
Copyright (C) 1995-2026 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
@ -175,7 +175,9 @@ const char *const syntax_check_name[NSYNTAXCHECKS] =
/* sc_ellipsis_unicode */ "ellipsis-unicode",
/* sc_space_ellipsis */ "space-ellipsis",
/* sc_quote_unicode */ "quote-unicode",
/* sc_bullet_unicode */ "bullet-unicode"
/* sc_bullet_unicode */ "bullet-unicode",
/* sc_url */ "url",
/* sc_email */ "email"
};

View File

@ -1,5 +1,5 @@
/* GNU gettext - internationalization aids
Copyright (C) 1995-2025 Free Software Foundation, Inc.
Copyright (C) 1995-2026 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
@ -134,9 +134,11 @@ enum syntax_check_type
sc_ellipsis_unicode,
sc_space_ellipsis,
sc_quote_unicode,
sc_bullet_unicode
sc_bullet_unicode,
sc_url,
sc_email
};
#define NSYNTAXCHECKS 4
#define NSYNTAXCHECKS 6
extern LIBGETTEXTSRC_DLL_VARIABLE const char *const syntax_check_name[NSYNTAXCHECKS];
/* Is current msgid subject to a syntax check? */

View File

@ -1,5 +1,5 @@
/* Checking of messages in POT files: so-called "syntax checks".
Copyright (C) 2015-2025 Free Software Foundation, Inc.
Copyright (C) 2015-2026 Free Software Foundation, Inc.
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
@ -45,117 +45,148 @@
/* Function that implements a single syntax check.
MP is a message.
MSGID is either MP->msgid or MP->msgid_plural.
Returns the number of errors that were seen and reported. */
typedef int (* syntax_check_function) (const message_ty *mp, const char *msgid);
typedef int (* syntax_check_function) (const message_ty *mp);
/* Implementation of the sc_ellipsis_unicode syntax check. */
/* ----- Implementation of the sc_ellipsis_unicode syntax check. ----- */
/* Determine whether a string (msgid or msgid_plural) contains an ASCII
ellipsis. */
static bool
string_has_ascii_ellipsis (const char *string)
{
const char *str = string;
const char *str_limit = str + strlen (string);
while (str < str_limit)
{
ucs4_t ending_char;
const char *end = sentence_end (str, &ending_char);
/* sentence_end doesn't treat '...' specially. */
const char *cp = end - (ending_char == '.' ? 2 : 3);
if (cp >= str && memcmp (cp, "...", 3) == 0)
return true;
str = end + 1;
}
return false;
}
/* Determine whether a message contains an ASCII ellipsis. */
static bool
message_has_ascii_ellipsis (const message_ty *mp)
{
return string_has_ascii_ellipsis (mp->msgid)
|| (mp->msgid_plural != NULL
&& string_has_ascii_ellipsis (mp->msgid_plural));
}
static int
syntax_check_ellipsis_unicode (const message_ty *mp, const char *msgid)
syntax_check_ellipsis_unicode (const message_ty *mp)
{
int seen_errors = 0;
{
const char *str = msgid;
const char *str_limit = str + strlen (msgid);
while (str < str_limit)
{
ucs4_t ending_char;
const char *end = sentence_end (str, &ending_char);
if (message_has_ascii_ellipsis (mp))
{
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
_("ASCII ellipsis ('...') instead of Unicode"));
return 1;
}
/* sentence_end doesn't treat '...' specially. */
const char *cp = end - (ending_char == '.' ? 2 : 3);
if (cp >= str && memcmp (cp, "...", 3) == 0)
{
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
_("ASCII ellipsis ('...') instead of Unicode"));
seen_errors++;
}
str = end + 1;
}
}
return seen_errors;
return 0;
}
/* Implementation of the sc_space_ellipsis syntax check. */
/* ----- Implementation of the sc_space_ellipsis syntax check. ----- */
/* Determine whether a string (msgid or msgid_plural) contains a space before
an ellipsis. */
static bool
string_has_space_ellipsis (const char *string)
{
const char *str = string;
const char *str_limit = str + strlen (string);
while (str < str_limit)
{
ucs4_t ending_char;
const char *end = sentence_end (str, &ending_char);
const char *ellipsis = NULL;
if (ending_char == 0x2026)
ellipsis = end;
else if (ending_char == '.')
{
/* sentence_end doesn't treat '...' specially. */
const char *cp = end - 2;
if (cp >= str && memcmp (cp, "...", 3) == 0)
ellipsis = cp;
}
else
{
/* Look for a '...'. */
const char *cp = end - 3;
if (cp >= str && memcmp (cp, "...", 3) == 0)
ellipsis = cp;
else
{
/* Look for a U+2026. */
ucs4_t uc = 0xfffd;
for (cp = end - 1; cp >= str; cp--)
{
u8_mbtouc (&uc, (const unsigned char *) cp, end - cp);
if (uc != 0xfffd)
break;
}
if (uc == 0x2026)
ellipsis = cp;
}
}
if (ellipsis)
{
/* Look at the character before ellipsis. */
ucs4_t uc = 0xfffd;
for (const char *cp = ellipsis - 1; cp >= str; cp--)
{
u8_mbtouc (&uc, (const unsigned char *) cp, ellipsis - cp);
if (uc != 0xfffd)
break;
}
if (uc != 0xfffd && uc_is_space (uc))
return true;
}
str = end + 1;
}
return false;
}
/* Determine whether a message contains a space before an ellipsis. */
static bool
message_has_space_ellipsis (const message_ty *mp)
{
return string_has_space_ellipsis (mp->msgid)
|| (mp->msgid_plural != NULL
&& string_has_space_ellipsis (mp->msgid_plural));
}
static int
syntax_check_space_ellipsis (const message_ty *mp, const char *msgid)
syntax_check_space_ellipsis (const message_ty *mp)
{
int seen_errors = 0;
{
const char *str = msgid;
const char *str_limit = str + strlen (msgid);
while (str < str_limit)
{
ucs4_t ending_char;
const char *end = sentence_end (str, &ending_char);
if (message_has_space_ellipsis (mp))
{
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
_("space before ellipsis found in user visible string"));
return 1;
}
const char *ellipsis = NULL;
if (ending_char == 0x2026)
ellipsis = end;
else if (ending_char == '.')
{
/* sentence_end doesn't treat '...' specially. */
const char *cp = end - 2;
if (cp >= str && memcmp (cp, "...", 3) == 0)
ellipsis = cp;
}
else
{
/* Look for a '...'. */
const char *cp = end - 3;
if (cp >= str && memcmp (cp, "...", 3) == 0)
ellipsis = cp;
else
{
/* Look for a U+2026. */
ucs4_t uc = 0xfffd;
for (cp = end - 1; cp >= str; cp--)
{
u8_mbtouc (&uc, (const unsigned char *) cp, end - cp);
if (uc != 0xfffd)
break;
}
if (uc == 0x2026)
ellipsis = cp;
}
}
if (ellipsis)
{
/* Look at the character before ellipsis. */
ucs4_t uc = 0xfffd;
for (const char *cp = ellipsis - 1; cp >= str; cp--)
{
u8_mbtouc (&uc, (const unsigned char *) cp, ellipsis - cp);
if (uc != 0xfffd)
break;
}
if (uc != 0xfffd && uc_is_space (uc))
{
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
_("space before ellipsis found in user visible strings"));
seen_errors++;
}
}
str = end + 1;
}
}
return seen_errors;
return 0;
}
/* Implementation of the sc_quote_unicode syntax check. */
/* ----- Implementation of the sc_quote_unicode syntax check. ----- */
struct callback_arg
{
@ -189,20 +220,23 @@ syntax_check_quote_unicode_callback (char quote, const char *quoted,
}
static int
syntax_check_quote_unicode (const message_ty *mp, const char *msgid)
syntax_check_quote_unicode (const message_ty *mp)
{
struct callback_arg arg;
arg.mp = mp;
arg.seen_errors = 0;
scan_quoted (msgid, strlen (msgid),
scan_quoted (mp->msgid, strlen (mp->msgid),
syntax_check_quote_unicode_callback, &arg);
if (mp->msgid_plural != NULL)
scan_quoted (mp->msgid_plural, strlen (mp->msgid_plural),
syntax_check_quote_unicode_callback, &arg);
return arg.seen_errors;
}
/* Implementation of the sc_bullet_unicode syntax check. */
/* ----- Implementation of the sc_bullet_unicode syntax check. ----- */
struct bullet_ty
{
@ -220,7 +254,7 @@ struct bullet_stack_ty
static struct bullet_stack_ty bullet_stack;
static int
syntax_check_bullet_unicode (const message_ty *mp, const char *msgid)
syntax_check_bullet_unicode_string (const message_ty *mp, const char *msgid)
{
bool seen_error = false;
@ -306,110 +340,18 @@ syntax_check_bullet_unicode (const message_ty *mp, const char *msgid)
return 0;
}
/* List of all syntax checks. */
static const syntax_check_function sc_funcs[NSYNTAXCHECKS] =
{
syntax_check_ellipsis_unicode,
syntax_check_space_ellipsis,
syntax_check_quote_unicode,
syntax_check_bullet_unicode
};
/* Perform all syntax checks on a non-obsolete message.
Return the number of errors that were seen. */
static int
syntax_check_message (const message_ty *mp)
syntax_check_bullet_unicode (const message_ty *mp)
{
int seen_errors = 0;
for (int i = 0; i < NSYNTAXCHECKS; i++)
{
if (mp->do_syntax_check[i] == yes)
{
seen_errors += sc_funcs[i] (mp, mp->msgid);
if (mp->msgid_plural)
seen_errors += sc_funcs[i] (mp, mp->msgid_plural);
}
}
return seen_errors;
return syntax_check_bullet_unicode_string (mp, mp->msgid)
+ (mp->msgid_plural != NULL
? syntax_check_bullet_unicode_string (mp, mp->msgid_plural)
: 0);
}
/* Signal an error when checking format strings. */
struct formatstring_error_logger_locals
{
const lex_pos_ty *pos;
};
static void
formatstring_error_logger (void *data, const char *format, ...)
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
__attribute__ ((__format__ (__printf__, 2, 3)))
#endif
;
static void
formatstring_error_logger (void *data, const char *format, ...)
{
struct formatstring_error_logger_locals *l =
(struct formatstring_error_logger_locals *) data;
va_list args;
va_start (args, format);
if_verror (IF_SEVERITY_ERROR,
l->pos->file_name, l->pos->line_number, (size_t)(-1), false,
format, args);
va_end (args);
}
/* Perform all format checks on a non-obsolete message.
Return the number of errors that were seen. */
static int
format_check_message (const message_ty *mp)
{
int seen_errors = 0;
if (mp->msgid_plural != NULL)
{
/* Look for format string incompatibilities between msgid and
msgid_plural. */
for (size_t i = 0; i < NFORMATS; i++)
if (possible_format_p (mp->is_format[i]))
{
struct formatstring_parser *parser = formatstring_parsers[i];
char *invalid_reason1 = NULL;
void *descr1 =
parser->parse (mp->msgid, false, NULL, &invalid_reason1);
char *invalid_reason2 = NULL;
void *descr2 =
parser->parse (mp->msgid_plural, false, NULL, &invalid_reason2);
if (descr1 != NULL && descr2 != NULL)
{
struct formatstring_error_logger_locals locals;
locals.pos = &mp->pos;
if (parser->check (descr2, descr1, false,
formatstring_error_logger, &locals,
"msgid_plural", "msgid"))
seen_errors++;
}
if (descr2 != NULL)
parser->free (descr2);
else
free (invalid_reason2);
if (descr1 != NULL)
parser->free (descr1);
else
free (invalid_reason1);
}
}
return seen_errors;
}
/* ----- Implementation of the sc_url syntax check. ----- */
/* This check is enabled by default. It produces a warning, not an error. */
/* Determine whether a string (msgid or msgid_plural) contains a URL. */
static bool
@ -419,7 +361,12 @@ string_has_url (const char *string)
(not "file:"). */
static const char *patterns[] =
{
/* We can afford to be silent about 'mailto:' here, because it is
almost always followed by an email address, that we report though
the sc_email check. */
#if 0
"mailto:",
#endif
"http://", "https://",
"ftp://",
"irc://", "ircs://"
@ -473,6 +420,20 @@ message_has_url (const message_ty *mp)
|| (mp->msgid_plural != NULL && string_has_url (mp->msgid_plural));
}
static int
syntax_check_url (const message_ty *mp)
{
if (message_has_url (mp))
if_error (IF_SEVERITY_WARNING,
mp->pos.file_name, mp->pos.line_number, (size_t)(-1), false,
_("Message contains an embedded URL. Better move it out of the translatable string, see %s"),
"https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html");
return 0;
}
/* ----- Implementation of the sc_email syntax check. ----- */
/* This check is enabled by default. It produces a warning, not an error. */
/* Determine whether a string (msgid or msgid_plural) contains an
email address. */
@ -551,21 +512,116 @@ message_has_email (const message_ty *mp)
|| (mp->msgid_plural != NULL && string_has_email (mp->msgid_plural));
}
/* Perform the URL check on a non-obsolete message. */
static void
url_check_message (const message_ty *mp)
static int
syntax_check_email (const message_ty *mp)
{
if (message_has_url (mp))
if_error (IF_SEVERITY_WARNING,
mp->pos.file_name, mp->pos.line_number, (size_t)(-1), false,
_("Message contains an embedded URL. Better move it out of the translatable string, see %s"),
"https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html");
else if (message_has_email (mp))
if (message_has_email (mp))
if_error (IF_SEVERITY_WARNING,
mp->pos.file_name, mp->pos.line_number, (size_t)(-1), false,
_("Message contains an embedded email address. Better move it out of the translatable string, see %s"),
"https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html");
return 0;
}
/* ---------------------- List of all syntax checks. ---------------------- */
static const syntax_check_function sc_funcs[NSYNTAXCHECKS] =
{
syntax_check_ellipsis_unicode,
syntax_check_space_ellipsis,
syntax_check_quote_unicode,
syntax_check_bullet_unicode,
syntax_check_url,
syntax_check_email
};
/* Perform all syntax checks on a non-obsolete message.
Return the number of errors that were seen. */
static int
syntax_check_message (const message_ty *mp)
{
int seen_errors = 0;
for (int i = 0; i < NSYNTAXCHECKS; i++)
if (mp->do_syntax_check[i] == yes)
seen_errors += sc_funcs[i] (mp);
return seen_errors;
}
/* Signal an error when checking format strings. */
struct formatstring_error_logger_locals
{
const lex_pos_ty *pos;
};
static void
formatstring_error_logger (void *data, const char *format, ...)
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
__attribute__ ((__format__ (__printf__, 2, 3)))
#endif
;
static void
formatstring_error_logger (void *data, const char *format, ...)
{
struct formatstring_error_logger_locals *l =
(struct formatstring_error_logger_locals *) data;
va_list args;
va_start (args, format);
if_verror (IF_SEVERITY_ERROR,
l->pos->file_name, l->pos->line_number, (size_t)(-1), false,
format, args);
va_end (args);
}
/* Perform all format checks on a non-obsolete message.
Return the number of errors that were seen. */
static int
format_check_message (const message_ty *mp)
{
int seen_errors = 0;
if (mp->msgid_plural != NULL)
{
/* Look for format string incompatibilities between msgid and
msgid_plural. */
for (size_t i = 0; i < NFORMATS; i++)
if (possible_format_p (mp->is_format[i]))
{
struct formatstring_parser *parser = formatstring_parsers[i];
char *invalid_reason1 = NULL;
void *descr1 =
parser->parse (mp->msgid, false, NULL, &invalid_reason1);
char *invalid_reason2 = NULL;
void *descr2 =
parser->parse (mp->msgid_plural, false, NULL, &invalid_reason2);
if (descr1 != NULL && descr2 != NULL)
{
struct formatstring_error_logger_locals locals;
locals.pos = &mp->pos;
if (parser->check (descr2, descr1, false,
formatstring_error_logger, &locals,
"msgid_plural", "msgid"))
seen_errors++;
}
if (descr2 != NULL)
parser->free (descr2);
else
free (invalid_reason2);
if (descr1 != NULL)
parser->free (descr1);
else
free (invalid_reason1);
}
}
return seen_errors;
}
@ -583,7 +639,6 @@ xgettext_check_message_list (message_list_ty *mlp)
if (!is_header (mp))
{
seen_errors += syntax_check_message (mp) + format_check_message (mp);
url_check_message (mp);
}
}

View File

@ -1,5 +1,5 @@
/* Extracting a message. Accumulating the message list.
Copyright (C) 2001-2025 Free Software Foundation, Inc.
Copyright (C) 2001-2026 Free Software Foundation, Inc.
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
@ -231,7 +231,7 @@ decide_syntax_check (message_ty *mp)
{
for (size_t i = 0; i < NSYNTAXCHECKS; i++)
if (mp->do_syntax_check[i] == undecided)
mp->do_syntax_check[i] = default_syntax_check[i] == yes ? yes : no;
mp->do_syntax_check[i] = default_syntax_check[i];
}

View File

@ -1,5 +1,5 @@
/* Extracts strings from C source file to Uniforum style .po file.
Copyright (C) 1995-2025 Free Software Foundation, Inc.
Copyright (C) 1995-2026 Free Software Foundation, Inc.
Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
This program is free software: you can redistribute it and/or modify
@ -239,8 +239,16 @@ static bool recognize_format_kde;
/* If true, recognize Boost format strings. */
static bool recognize_format_boost;
/* Syntax checks enabled by default. */
enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
/* Syntax checks enabled through a command-line option or by default. */
enum is_syntax_check default_syntax_check[NSYNTAXCHECKS] =
{
/* sc_ellipsis_unicode */ no,
/* sc_space_ellipsis */ no,
/* sc_quote_unicode */ no,
/* sc_bullet_unicode */ no,
/* sc_url */ yes,
/* sc_email */ yes
};
static locating_rule_list_ty *its_locating_rules;
@ -391,11 +399,11 @@ main (int argc, char *argv[])
{ "force-po", 0, no_argument, &force_po, 1 },
{ "foreign-user", CHAR_MAX + 2, no_argument },
{ "from-code", CHAR_MAX + 3, required_argument },
{ "generated", CHAR_MAX + 24, required_argument },
{ "generated", CHAR_MAX + 25, required_argument },
{ "help", 'h', no_argument },
{ "indent", 'i', no_argument },
{ "its", CHAR_MAX + 20, required_argument },
{ "itstool", CHAR_MAX + 19, no_argument },
{ "its", CHAR_MAX + 21, required_argument },
{ "itstool", CHAR_MAX + 20, no_argument },
{ "join-existing", 'j', no_argument },
{ "kde", CHAR_MAX + 10, no_argument },
{ "keyword", 'k', optional_argument },
@ -403,8 +411,9 @@ main (int argc, char *argv[])
{ "msgid-bugs-address", CHAR_MAX + 5, required_argument },
{ "msgstr-prefix", 'm', optional_argument },
{ "msgstr-suffix", 'M', optional_argument },
{ "no-check", CHAR_MAX + 18, required_argument },
{ "no-escape", 'e', no_argument },
{ "no-git", CHAR_MAX + 23, no_argument },
{ "no-git", CHAR_MAX + 24, no_argument },
{ "no-location", CHAR_MAX + 16, no_argument },
{ "no-wrap", CHAR_MAX + 4, no_argument },
{ "omit-header", 0, no_argument, &xgettext_omit_header, 1 },
@ -414,15 +423,15 @@ main (int argc, char *argv[])
{ "package-version", CHAR_MAX + 13, required_argument },
{ "properties-output", CHAR_MAX + 6, no_argument },
{ "qt", CHAR_MAX + 9, no_argument },
{ "reference", CHAR_MAX + 22, required_argument },
{ "sentence-end", CHAR_MAX + 18, required_argument },
{ "reference", CHAR_MAX + 23, required_argument },
{ "sentence-end", CHAR_MAX + 19, required_argument },
{ "sort-by-file", 'F', no_argument },
{ "sort-output", 's', no_argument },
{ "strict", CHAR_MAX + 25, no_argument },
{ "strict", CHAR_MAX + 26, no_argument },
{ "string-limit", 'l', required_argument },
{ "stringtable-output", CHAR_MAX + 7, no_argument },
{ "style", CHAR_MAX + 15, required_argument },
{ "tag", CHAR_MAX + 21, required_argument },
{ "tag", CHAR_MAX + 22, required_argument },
{ "trigraphs", 'T', no_argument },
{ "verbose", 'v', no_argument },
{ "version", 'V', no_argument },
@ -604,7 +613,7 @@ main (int argc, char *argv[])
sort_by_msgid = true;
break;
case CHAR_MAX + 25: /* --strict */
case CHAR_MAX + 26: /* --strict */
message_print_style_uniforum ();
break;
@ -722,7 +731,23 @@ main (int argc, char *argv[])
}
break;
case CHAR_MAX + 18: /* --sentence-end */
case CHAR_MAX + 18: /* --no-check */
{
size_t i;
for (i = 0; i < NSYNTAXCHECKS; i++)
{
if (strcmp (optarg, syntax_check_name[i]) == 0)
{
default_syntax_check[i] = no;
break;
}
}
if (i == NSYNTAXCHECKS)
error (EXIT_FAILURE, 0, _("syntax check '%s' unknown"), optarg);
}
break;
case CHAR_MAX + 19: /* --sentence-end */
if (strcmp (optarg, "single-space") == 0)
sentence_end_required_spaces = 1;
else if (strcmp (optarg, "double-space") == 0)
@ -731,27 +756,27 @@ main (int argc, char *argv[])
error (EXIT_FAILURE, 0, _("sentence end type '%s' unknown"), optarg);
break;
case CHAR_MAX + 19: /* --itstool */
case CHAR_MAX + 20: /* --itstool */
add_itstool_comments = true;
break;
case CHAR_MAX + 20: /* --its */
case CHAR_MAX + 21: /* --its */
explicit_its_filename = optarg;
break;
case CHAR_MAX + 21: /* --tag */
case CHAR_MAX + 22: /* --tag */
x_javascript_tag (optarg);
break;
case CHAR_MAX + 22: /* --reference */
case CHAR_MAX + 23: /* --reference */
string_list_append (&files_for_vc_mtime, optarg);
break;
case CHAR_MAX + 23: /* --no-git */
case CHAR_MAX + 24: /* --no-git */
xgettext_no_git = true;
break;
case CHAR_MAX + 24: /* --generated */
case CHAR_MAX + 25: /* --generated */
gl_set_add (generated_files, optarg);
break;
@ -1212,6 +1237,9 @@ Operation mode:\n"));
(ellipsis-unicode, space-ellipsis,\n\
quote-unicode, bullet-unicode)\n"));
printf (_("\
--no-check=NAME don't perform syntax check on messages\n\
(url, email)\n"));
printf (_("\
--sentence-end=TYPE type describing the end of sentence\n\
(single-space, which is the default, \n\
or double-space)\n"));

View File

@ -1,5 +1,5 @@
/* xgettext common functions.
Copyright (C) 2001-2003, 2005-2006, 2008-2009, 2011, 2013-2014, 2018, 2020, 2023 Free Software Foundation, Inc.
Copyright (C) 2001-2026 Free Software Foundation, Inc.
Written by Peter Miller <millerp@canb.auug.org.au>
and Bruno Haible <haible@clisp.cons.org>, 2001.
@ -53,6 +53,7 @@ extern int xgettext_omit_header;
/* Be more verbose. */
extern int verbose;
/* Syntax checks enabled through a command-line option or by default. */
extern enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];

View File

@ -20,7 +20,7 @@ EOF
: ${XGETTEXT=xgettext}
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --check=ellipsis-unicode -d xg-ellipsis-u.tmp xg-ellipsis-u.c 2>xg-ellipsis-u.err
test `grep -c 'ASCII ellipsis' xg-ellipsis-u.err` = 4 || Exit 1
test `grep -c 'ASCII ellipsis' xg-ellipsis-u.err` = 3 || Exit 1
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --check=ellipsis-unicode --sentence-end=double-space -d xg-ellipsis-ud.tmp xg-ellipsis-u.c 2>xg-ellipsis-ud.err
@ -40,7 +40,7 @@ EOF
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --check=space-ellipsis -d xg-space-e.tmp xg-space-e.c 2>xg-space-e.err
test `grep -c 'space before ellipsis' xg-space-e.err` = 3 || Exit 1
test `grep -c 'space before ellipsis' xg-space-e.err` = 2 || Exit 1
# --check=quote-unicode
cat <<\EOF > xg-quote-u.c

View File

@ -9,16 +9,67 @@ cat <<\EOF > xg-test20.c
gettext ("Report bugs to <mailto:foobar@example.com>");
gettext ("Report bugs to: bug-foobar@gnu.org");
gettext ("Report bugs in the bug tracker at <https://savannah.gnu.org/projects/foobar>");
/* xgettext: no-email-check */
gettext ("M2: Report bugs to <mailto:foobar@example.com>");
/* xgettext: no-email-check */
gettext ("M2: Report bugs to: bug-foobar@gnu.org");
/* xgettext: no-url-check */
gettext ("M2: Report bugs in the bug tracker at <https://savannah.gnu.org/projects/foobar>");
/* xgettext: no-url-check */
gettext ("M3: Report bugs to: bug-foobar@gnu.org");
EOF
: ${XGETTEXT=xgettext}
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments -d xg-test20.tmp xg-test20.c 2>xg-test20.err \
|| Exit 1
cat xg-test20.err; echo
if grep "xg-test20.c:1:.*No-embedded-URLs.html" xg-test20.err; then
if grep "xg-test20.c:1:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
Exit 1
fi
grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20.err || Exit 1
grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20.err || Exit 1
grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20.err || Exit 1
grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
if grep "xg-test20.c:6:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
Exit 1
fi
if grep "xg-test20.c:8:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
Exit 1
fi
if grep "xg-test20.c:10:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
Exit 1
fi
grep "xg-test20.c:12:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
# Likewise, with --no-check=url option:
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --no-check=url -d xg-test20.tmp xg-test20.c 2>xg-test20a.err \
|| Exit 1
cat xg-test20a.err; echo
grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20a.err >/dev/null || Exit 1
grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20a.err >/dev/null || Exit 1
if grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20a.err >/dev/null; then
Exit 1
fi
grep "xg-test20.c:12:.*No-embedded-URLs.html" xg-test20a.err >/dev/null || Exit 1
# Likewise, with --no-check=email option:
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --no-check=email -d xg-test20.tmp xg-test20.c 2>xg-test20b.err \
|| Exit 1
cat xg-test20b.err; echo
if grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20b.err >/dev/null; then
Exit 1
fi
if grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20b.err >/dev/null; then
Exit 1
fi
grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20b.err >/dev/null || Exit 1
if grep "xg-test20.c:12:.*No-embedded-URLs.html" xg-test20b.err >/dev/null; then
Exit 1
fi