Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/click/_termui_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,13 @@ def format_eta(self) -> str:
hours = t % 24
t //= 24
if t > 0:
return f"{t}d {hours:02}:{minutes:02}:{seconds:02}"
return "{d}{day_label} {h:02}:{m:02}:{s:02}".format(
d=t,
day_label=_("d"),
Comment on lines +177 to +179

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return "{d}{day_label} {h:02}:{m:02}:{s:02}".format(
d=t,
day_label=_("d"),
return "{d}{day_label} {h:02}:{m:02}:{s:02}".format(
d=t,
# TRANSLATORS: The 'd' stands for 'day'.
day_label=_("d"),

h=hours,
m=minutes,
s=seconds,
)
else:
return f"{hours:02}:{minutes:02}:{seconds:02}"
return ""
Expand Down
5 changes: 3 additions & 2 deletions src/click/_winconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ctypes.wintypes import HANDLE
from ctypes.wintypes import LPCWSTR
from ctypes.wintypes import LPWSTR
from gettext import gettext as _

from ._compat import _NonClosingTextIOWrapper

Expand Down Expand Up @@ -152,7 +153,7 @@ def readinto(self, b: Buffer) -> int:
# wait for KeyboardInterrupt
time.sleep(0.1)
if not rv:
raise OSError(f"Windows error: {GetLastError()}")
raise OSError(_("Windows error: {error}").format(error=GetLastError())) # noqa: UP032

if buffer[0] == EOF:
return 0
Expand All @@ -169,7 +170,7 @@ def _get_error_message(errno: int) -> str:
return "ERROR_SUCCESS"
elif errno == ERROR_NOT_ENOUGH_MEMORY:
return "ERROR_NOT_ENOUGH_MEMORY"
return f"Windows error {errno}"
return _("Windows error {errno}").format(errno=errno) # noqa: UP032

def write(self, b: Buffer) -> int:
bytes_to_be_written = len(b)
Expand Down
91 changes: 59 additions & 32 deletions src/click/core.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Ask Ruff to accept the format method on strings, and not let pyupgrade
# always force f-strings. The latter are unfortunately not supported yet
# by Babel, a localisation library.
#
# Note: Using `# noqa: UP032` on lines has not worked, so a file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of these noqa marks are needed. Ruff is not trying to autoupgrade anything if I remove the comments, inline or file-level.

# setting.
# ruff: noqa: UP032

from __future__ import annotations

import collections.abc as cabc
Expand Down Expand Up @@ -76,15 +84,15 @@ def _check_nested_chain(
return

if register:
message = (
f"It is not possible to add the group {cmd_name!r} to another"
f" group {base_command.name!r} that is in chain mode."
)
message = _(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're only translating user-facing messages at this time, not developer-facing. Please remove all such translation markings.

"It is not possible to add the group {cmd_name!r} to another"
" group {base_cmd_name!r} that is in chain mode."
).format(cmd_name=cmd_name, base_cmd_name=base_command.name) # noqa: UP032
else:
message = (
f"Found the group {cmd_name!r} as subcommand to another group "
f" {base_command.name!r} that is in chain mode. This is not supported."
)
message = _(
"Found the group {cmd_name!r} as subcommand to another group "
" {base_cmd_name!r} that is in chain mode. This is not supported."
).format(cmd_name=cmd_name, base_cmd_name=base_command.name) # noqa: UP032

raise RuntimeError(message)

Expand Down Expand Up @@ -986,8 +994,10 @@ def get_params(self, ctx: Context) -> list[Parameter]:
for duplicate_opt in duplicate_opts:
warnings.warn(
(
f"The parameter {duplicate_opt} is used more than once. "
"Remove its duplicate as parameters should be unique."
_(
"The parameter {param} is used more than once. "
"Remove its duplicate as parameters should be unique."
).format(param=duplicate_opt)
),
stacklevel=3,
)
Expand Down Expand Up @@ -1077,9 +1087,9 @@ def get_short_help_str(self, limit: int = 45) -> str:

if self.deprecated:
deprecated_message = (
f"(DEPRECATED: {self.deprecated})"
_("(DEPRECATED: {target})".format(target=self.deprecated))
if isinstance(self.deprecated, str)
else "(DEPRECATED)"
else _("(DEPRECATED)")
)
text = _("{text} {deprecated_message}").format(
text=text, deprecated_message=deprecated_message
Expand Down Expand Up @@ -1114,9 +1124,9 @@ def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None:

if self.deprecated:
deprecated_message = (
f"(DEPRECATED: {self.deprecated})"
_("(DEPRECATED: {target})".format(target=self.deprecated))
if isinstance(self.deprecated, str)
else "(DEPRECATED)"
else _("(DEPRECATED)")
)
text = _("{text} {deprecated_message}").format(
Copy link
Member

@davidism davidism Aug 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't have ever been marked for translation. It should be removed, since you've added the translation to the proper location above.

text=text, deprecated_message=deprecated_message
Expand Down Expand Up @@ -2123,8 +2133,10 @@ def __init__(
if __debug__:
if self.type.is_composite and nargs != self.type.arity:
raise ValueError(
f"'nargs' must be {self.type.arity} (or None) for"
f" type {self.type!r}, but it was {nargs}."
_(
"'nargs' must be {arity} (or None) for"
" type {type!r}, but it was {nargs}."
).format(arity=self.type.arity, type=self.type, nargs=nargs)
)

# Skip no default or callable default.
Expand Down Expand Up @@ -2158,14 +2170,21 @@ def __init__(
if nargs > 1 and len(check_default) != nargs:
subject = "item length" if multiple else "length"
raise ValueError(
f"'default' {subject} must match nargs={nargs}."
_("'default' {subject} must match nargs={nargs}.").format(
subject=subject, nargs=nargs
)
)

if required and deprecated:
raise ValueError(
f"The {self.param_type_name} '{self.human_readable_name}' "
"is deprecated and still required. A deprecated "
f"{self.param_type_name} cannot be required."
_(
"The {type_name} '{readable_name}' "
"is deprecated and still required. A deprecated "
"{type_name} cannot be required."
).format(
type_name=self.param_type_name,
readable_name=self.human_readable_name,
)
)

def to_info_dict(self) -> dict[str, t.Any]:
Expand Down Expand Up @@ -2571,9 +2590,9 @@ def __init__(

if deprecated:
deprecated_message = (
f"(DEPRECATED: {deprecated})"
_("(DEPRECATED: {target})".format(target=deprecated))
if isinstance(deprecated, str)
else "(DEPRECATED)"
else _("(DEPRECATED)")
)
help = help + deprecated_message if help is not None else deprecated_message

Expand Down Expand Up @@ -2676,7 +2695,7 @@ def to_info_dict(self) -> dict[str, t.Any]:
def get_error_hint(self, ctx: Context) -> str:
result = super().get_error_hint(ctx)
if self.show_envvar:
result += f" (env var: '{self.envvar}')"
result += _(" (env var: '{var}')").format(var=self.envvar) # noqa: UP032
return result

def _parse_decls(
Expand All @@ -2690,7 +2709,7 @@ def _parse_decls(
for decl in decls:
if decl.isidentifier():
if name is not None:
raise TypeError(f"Name '{name}' defined twice")
raise TypeError(_("Name '{name}' defined twice").format(name=name)) # noqa: UP032
name = decl
else:
split_char = ";" if decl[:1] == "/" else "/"
Expand All @@ -2705,8 +2724,10 @@ def _parse_decls(
secondary_opts.append(second.lstrip())
if first == second:
raise ValueError(
f"Boolean option {decl!r} cannot use the"
" same flag for true/false."
_(
"Boolean option {decl!r} cannot use the"
" same flag for true/false."
).format(decl=decl)
)
else:
possible_names.append(_split_opt(decl))
Expand All @@ -2722,14 +2743,18 @@ def _parse_decls(
if not expose_value:
return None, opts, secondary_opts
raise TypeError(
f"Could not determine name for option with declarations {decls!r}"
_(
"Could not determine name for option with declarations {decls!r}"
).format(decls=decls)
)

if not opts and not secondary_opts:
raise TypeError(
f"No options defined but a name was passed ({name})."
" Did you mean to declare an argument instead? Did"
f" you mean to pass '--{name}'?"
_(
"No options defined but a name was passed ({name})."
" Did you mean to declare an argument instead? Did"
" you mean to pass '--{name}'?"
).format(name=name)
)

return name, opts, secondary_opts
Expand Down Expand Up @@ -3095,8 +3120,10 @@ def _parse_decls(
name = name.replace("-", "_").lower()
else:
raise TypeError(
"Arguments take exactly one parameter declaration, got"
f" {len(decls)}: {decls}."
_(
"Arguments take exactly one parameter declaration, got"
" {length}: {decls}."
).format(length=len(decls), decls=decls)
)
return name, [arg], []

Expand Down
37 changes: 26 additions & 11 deletions src/click/decorators.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Ask Ruff to accept the format method on strings, and not let pyupgrade
# always force f-strings. The latter are unfortunately not supported yet
# by Babel, a localisation library.
#
# Note: Using `# noqa: UP032` on lines has not worked, so a file
# setting.
# ruff: noqa: UP032

from __future__ import annotations

import inspect
Expand Down Expand Up @@ -85,10 +93,12 @@ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:

if obj is None:
raise RuntimeError(
"Managed to invoke callback without a context"
f" object of type {object_type.__name__!r}"
" existing."
)
_(
"Managed to invoke callback without a context"
" object of type {type!r}"
" existing."
).format(type=object_type.__name__) # noqa: UP032
) # noqa: UP032

return ctx.invoke(f, obj, *args, **kwargs)

Expand Down Expand Up @@ -121,12 +131,14 @@ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
return update_wrapper(new_func, f)

if doc_description is None:
doc_description = f"the {key!r} key from :attr:`click.Context.meta`"
doc_description = _("the {key!r} key from :attr:`click.Context.meta`").format(
key=key
) # noqa: UP032

decorator.__doc__ = (
f"Decorator that passes {doc_description} as the first argument"
decorator.__doc__ = _(
"Decorator that passes {description} as the first argument"
" to the decorated function."
)
).format(description=doc_description) # noqa: UP032
return decorator


Expand Down Expand Up @@ -498,13 +510,16 @@ def callback(ctx: Context, param: Parameter, value: bool) -> None:
version = importlib.metadata.version(package_name)
except importlib.metadata.PackageNotFoundError:
raise RuntimeError(
f"{package_name!r} is not installed. Try passing"
" 'package_name' instead."
_(
"{name!r} is not installed. Try passing 'package_name' instead."
).format(name=package_name) # noqa: UP032
) from None

if version is None:
raise RuntimeError(
f"Could not determine the version for {package_name!r} automatically."
_("Could not determine the version for {name!r} automatically.").format(
name=package_name
) # noqa: UP032
)

echo(
Expand Down
2 changes: 1 addition & 1 deletion src/click/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> N
``"Usage: "``.
"""
if prefix is None:
prefix = f"{_('Usage:')} "
prefix = "{usage} ".format(usage=_("Usage:"))

usage_prefix = f"{prefix:>{self.current_indent}}{prog} "
text_width = self.width - self.current_indent
Expand Down
28 changes: 22 additions & 6 deletions src/click/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
Copyright 2002-2006 Python Software Foundation. All rights reserved.
"""

# Ask Ruff to accept the format method on strings, and not let pyupgrade
# always force f-strings. The latter are unfortunately not supported yet
# by Babel, a localisation library.
#
# Note: Using `# noqa: UP032` on lines has not worked, so a file
# setting.
# ruff: noqa: UP032

# This code uses parts of optparse written by Gregory P. Ward and
# maintained by the Python Software Foundation.
# Copyright 2001-2006 Gregory P. Ward
Expand Down Expand Up @@ -142,7 +150,11 @@ def __init__(
for opt in opts:
prefix, value = _split_opt(opt)
if not prefix:
raise ValueError(f"Invalid start character for option ({opt})")
raise ValueError(
_("Invalid start character for option ({option})").format(
option=opt
)
) # noqa: UP032
self.prefixes.add(prefix[0])
if len(prefix) == 1 and len(value) == 1:
self._short_opts.append(opt)
Expand Down Expand Up @@ -175,7 +187,7 @@ def process(self, value: t.Any, state: _ParsingState) -> None:
elif self.action == "count":
state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore
else:
raise ValueError(f"unknown action '{self.action}'")
raise ValueError(_("unknown action '{action}'").format(action=self.action)) # noqa: UP032
state.order.append(self.obj)


Expand Down Expand Up @@ -511,8 +523,10 @@ def __getattr__(name: str) -> object:
"ParsingState",
}:
warnings.warn(
f"'parser.{name}' is deprecated and will be removed in Click 9.0."
" The old parser is available in 'optparse'.",
_(
"'parser.{name}' is deprecated and will be removed in Click 9.0."
" The old parser is available in 'optparse'."
).format(name=name), # noqa: UP032
DeprecationWarning,
stacklevel=2,
)
Expand All @@ -522,8 +536,10 @@ def __getattr__(name: str) -> object:
from .shell_completion import split_arg_string

warnings.warn(
"Importing 'parser.split_arg_string' is deprecated, it will only be"
" available in 'shell_completion' in Click 9.0.",
_(
"Importing 'parser.split_arg_string' is deprecated, it will only be"
" available in 'shell_completion' in Click 9.0."
),
DeprecationWarning,
stacklevel=2,
)
Expand Down
4 changes: 2 additions & 2 deletions src/click/termui.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,13 +608,13 @@ def style(
try:
bits.append(f"\033[{_interpret_color(fg)}m")
except KeyError:
raise TypeError(f"Unknown color {fg!r}") from None
raise TypeError(_("Unknown color {colour!r}").format(colour=fg)) from None # noqa: UP032

if bg:
try:
bits.append(f"\033[{_interpret_color(bg, 10)}m")
except KeyError:
raise TypeError(f"Unknown color {bg!r}") from None
raise TypeError(_("Unknown color {colour!r}").format(colour=bg)) from None # noqa: UP032

if bold is not None:
bits.append(f"\033[{1 if bold else 22}m")
Expand Down
Loading