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.
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.
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
@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
@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
@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
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}
/* 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>
/* 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"
};
/* 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>
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? */
/* 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
/* 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. ----- */
-static int
-syntax_check_ellipsis_unicode (const message_ty *mp, const char *msgid)
+/* Determine whether a string (msgid or msgid_plural) contains an ASCII
+ ellipsis. */
+static bool
+string_has_ascii_ellipsis (const char *string)
{
- 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);
+ 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);
+ /* 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++;
- }
+ if (cp >= str && memcmp (cp, "...", 3) == 0)
+ return true;
- str = end + 1;
- }
- }
+ str = end + 1;
+ }
+ return false;
+}
- return seen_errors;
+/* 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)
+{
+ if (message_has_ascii_ellipsis (mp))
+ {
+ po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
+ _("ASCII ellipsis ('...') instead of Unicode"));
+ return 1;
+ }
-/* Implementation of the sc_space_ellipsis syntax check. */
+ return 0;
+}
-static int
-syntax_check_space_ellipsis (const message_ty *mp, const char *msgid)
+
+/* ----- 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)
{
- 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);
+ 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;
- }
+ 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 (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 (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++;
- }
- }
+ if (uc != 0xfffd && uc_is_space (uc))
+ return true;
+ }
- str = end + 1;
- }
- }
+ str = end + 1;
+ }
+ return false;
+}
- return seen_errors;
+/* 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)
+{
+ 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;
+ }
+
+ return 0;
+}
-/* Implementation of the sc_quote_unicode syntax check. */
+
+/* ----- Implementation of the sc_quote_unicode syntax check. ----- */
struct callback_arg
{
}
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
{
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;
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
(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://"
|| (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. */
|| (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;
}
if (!is_header (mp))
{
seen_errors += syntax_check_message (mp) + format_check_message (mp);
- url_check_message (mp);
}
}
/* 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
{
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];
}
/* 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
/* 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;
{ "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 },
{ "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 },
{ "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 },
sort_by_msgid = true;
break;
- case CHAR_MAX + 25: /* --strict */
+ case CHAR_MAX + 26: /* --strict */
message_print_style_uniforum ();
break;
}
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)
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;
(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"));
/* 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.
/* 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];
: ${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
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
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