From 9a2614630e8d51e04f901f6e94de4d32341a393d Mon Sep 17 00:00:00 2001 From: Zuckerig Date: Mon, 16 Feb 2026 16:51:03 +0500 Subject: [PATCH 1/3] Localization --- main.py | 164 ++++++++++++++++++------------------ pyproject.toml | 1 + requirements.txt | 1 + rf4s/app/app.py | 132 +++++++++++++++-------------- rf4s/controller/player.py | 3 +- rf4s/i18n/__init__.py | 23 ++++++ rf4s/i18n/en.yml | 169 ++++++++++++++++++++++++++++++++++++++ rf4s/i18n/ru.yml | 169 ++++++++++++++++++++++++++++++++++++++ rf4s/i18n/zh-TW.yml | 169 ++++++++++++++++++++++++++++++++++++++ rf4s/result/result.py | 74 +++++++++++------ rf4s/utils.py | 7 +- 11 files changed, 728 insertions(+), 184 deletions(-) create mode 100644 rf4s/i18n/__init__.py create mode 100644 rf4s/i18n/en.yml create mode 100644 rf4s/i18n/ru.yml create mode 100644 rf4s/i18n/zh-TW.yml diff --git a/main.py b/main.py index 331a548..82669a8 100644 --- a/main.py +++ b/main.py @@ -8,12 +8,18 @@ """ import argparse +import io import logging import logging.config import shlex import sys from pathlib import Path +# Force UTF-8 for stdout/stderr on Windows to support all i18n languages (zh-TW, ru, etc.) +if sys.platform == "win32": + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace") + import rich.logging # noqa: F401 import rich_argparse from rich import box, print @@ -21,6 +27,7 @@ from yacs.config import CfgNode as CN from rf4s import config, utils +from rf4s.i18n import setup as setup_i18n, t from rf4s.app import ( BotApp, CalculateApp, @@ -43,48 +50,44 @@ # https://patorjk.com/software/taag/#p=testall&f=3D-ASCII&t=RF4S%0A, ANSI Shadow FEATURES = ( - {"name": "Fishing Bot", "command": "bot"}, - {"name": "Craft Items", "command": "craft"}, - {"name": "Move Forward", "command": "move"}, - {"name": "Harvest Baits", "command": "harvest"}, - {"name": "Auto Friction Brake", "command": "frictionbrake"}, - {"name": "Calculate Tackle's Stats", "command": "calculate"}, + {"name_key": "feature_bot", "command": "bot"}, + {"name_key": "feature_craft", "command": "craft"}, + {"name_key": "feature_move", "command": "move"}, + {"name_key": "feature_harvest", "command": "harvest"}, + {"name_key": "feature_frictionbrake", "command": "frictionbrake"}, + {"name_key": "feature_calculate", "command": "calculate"}, ) BOT_BOOLEAN_ARGUMENTS = ( - ("t", "tag", "keep only tagged fishes"), - ("c", "coffee", "drink coffee if stamina is low during fish fight"), - ("a", "alcohol", "drink alcohol before keeping the fish"), - ("r", "refill", "consume tea and carrot if hunger or comfort is low"), - ("H", "harvest", "harvest baits before casting the rod"), - ("L", "lure", "change current lure with a random favorite one, mode: spin"), - ("m", "mouse", "move mouse randomly before casting the rod"), - ("P", "pause", "pause the script before casting the rod occasionally"), - ("RC", "random-cast", "do a redundant rod cast randomly"), - ("SC", "skip-cast", "skip the first rod cast"), - ("l", "lift", "lift the tackle constantly during a fish fight"), - ("e", "electro", "enable electric mode for Electro Raptor series reel"), - ("FB", "friction-brake", "adjust friction brake automatically"), - ("GR", "gear-ratio", "switch the gear ratio or mode after the retrieval timed out"), - ("b", "bite", "save a screenshot in screenshots/ when a fish bite"), - ("s", "screenshot", "save a screenshot in screenshots/ after you caught a fish"), - ("d", "data", "save fishing data in /logs"), - ("E", "email", "send email noticication after the script stop"), - ("M", "miaotixing", "send miaotixing notification after the script stop"), - ("D", "discord", "send Discord notification after the script stop"), - ("TG", "telegram", "send Telegram notification after the script stop"), - ("S", "shutdown", "shutdown computer after the script stop"), - ("SO", "signout", "sign out instead of closing the game"), - ("BL", "broken-lure", "replace broken lures with favorite ones"), - ("SR", "spod-rod", "recast spod rod"), - ("DM", "dry-mix", "enable dry mix refill, mode: bottom"), - ("GB", "groundbait", "enable groundbait refill, mode: bottom"), - ("PVA", "pva", "enable pva refill, mode: bottom"), - ( - "NA", - "no-animation", - "disable waiting for trophy and gift animations, gift\nchange 'Catch screen style' to 'Simple' in game settings to use this flag", - ), + ("t", "tag", "help_tag"), + ("c", "coffee", "help_coffee"), + ("a", "alcohol", "help_alcohol"), + ("r", "refill", "help_refill"), + ("H", "harvest", "help_harvest_arg"), + ("L", "lure", "help_lure"), + ("m", "mouse", "help_mouse"), + ("P", "pause", "help_pause"), + ("RC", "random-cast", "help_random_cast"), + ("SC", "skip-cast", "help_skip_cast"), + ("l", "lift", "help_lift"), + ("e", "electro", "help_electro"), + ("FB", "friction-brake", "help_friction_brake"), + ("GR", "gear-ratio", "help_gear_ratio"), + ("b", "bite", "help_bite"), + ("s", "screenshot", "help_screenshot"), + ("d", "data", "help_data"), + ("E", "email", "help_email"), + ("M", "miaotixing", "help_miaotixing"), + ("D", "discord", "help_discord"), + ("TG", "telegram", "help_telegram"), + ("S", "shutdown", "help_shutdown"), + ("SO", "signout", "help_signout"), + ("BL", "broken-lure", "help_broken_lure"), + ("SR", "spod-rod", "help_spod_rod"), + ("DM", "dry-mix", "help_dry_mix"), + ("GB", "groundbait", "help_groundbait"), + ("PVA", "pva", "help_pva"), + ("NA", "no-animation", "help_no_animation"), ) EPILOG = """ @@ -158,18 +161,18 @@ def setup_parser(cfg: CN) -> tuple[argparse.ArgumentParser, tuple]: :rtype: ArgumentParser """ parent_parser = argparse.ArgumentParser(add_help=False) - parent_parser.add_argument("opts", nargs="*", help="overwrite configuration") + parent_parser.add_argument("opts", nargs="*", help=t("help_opts")) main_parser = argparse.ArgumentParser(epilog=EPILOG, formatter_class=Formatter) main_parser.add_argument( "-V", "--version", action="version", version=f"RF4S {VERSION}" ) - feature_parsers = main_parser.add_subparsers(title="features", dest="feature") + feature_parsers = main_parser.add_subparsers(title=t("features"), dest="feature") bot_parser = feature_parsers.add_parser( "bot", - help="start fishing bot", + help=t("help_bot"), parents=[parent_parser], formatter_class=Formatter, ) @@ -180,7 +183,7 @@ def setup_parser(cfg: CN) -> tuple[argparse.ArgumentParser, tuple]: for argument in BOT_BOOLEAN_ARGUMENTS: flag1 = f"-{argument[0]}" flag2 = f"--{argument[1]}" - help_message = argument[2] + help_message = t(argument[2]) bot_parser.add_argument(flag1, flag2, action="store_true", help=help_message) profile_strategy = bot_parser.add_mutually_exclusive_group() @@ -193,7 +196,7 @@ def pid(_pid: str) -> int: "--pid", type=pid, choices=range(len(cfg.PROFILE)), - help="specify the id of the profile to use", + help=t("help_pid"), metavar=f"{{0-{len(cfg.PROFILE) - 1}}}", ) @@ -206,7 +209,7 @@ def pname(_pname: str) -> str: "-N", "--pname", type=pname, - help="specify the name of the profile to use", + help=t("help_pname"), metavar="{profile name}", ) @@ -220,7 +223,7 @@ def num_fish(_num_fish: str) -> int: # const=0, # Flag is used but no argument given type=num_fish, choices=range(cfg.BOT.KEEPNET.CAPACITY), - help="specify the number of fishes in your keepnet, (default: %(default)s)", + help=t("help_fishes_in_keepnet"), metavar=f"{{0-{cfg.BOT.KEEPNET.CAPACITY - 1}}}", ) bot_parser.add_argument( @@ -231,10 +234,7 @@ def num_fish(_num_fish: str) -> int: default=None, type=str, choices=["forward", "left", "right"], - help=( - "enable trolling mode and specify the direction\n" - "(default: %(default)s, no argument: %(const)s)" - ), + help=t("help_trolling"), ) bot_parser.add_argument( "-R", @@ -244,10 +244,7 @@ def num_fish(_num_fish: str) -> int: default=None, type=int, choices=[0, 5], - help=( - "enable rainbow line mode and specify the meter to lift the rod\n" - "(default: %(default)s, no argument: %(const)s)" - ), + help=t("help_rainbow"), ) bot_parser.add_argument( @@ -258,14 +255,11 @@ def num_fish(_num_fish: str) -> int: default=0, type=int, choices=[0, 1, 2, 3, 5], - help=( - "enable boat ticket renewal and specify the duration\n" - "(default: %(default)s, no argument: %(const)s)" - ), + help=t("help_boat_ticket"), ) craft_parser = feature_parsers.add_parser( - "craft", help="craft items", parents=[parent_parser], formatter_class=Formatter + "craft", help=t("help_craft"), parents=[parent_parser], formatter_class=Formatter ) craft_parser.add_argument( "-V", "--version", action="version", version=f"RF4S-craft {VERSION}" @@ -274,26 +268,26 @@ def num_fish(_num_fish: str) -> int: "-d", "--discard", action="store_true", - help="discard all the crafted items (for groundbaits)", + help=t("help_discard"), ) craft_parser.add_argument( "-i", "--ignore", action="store_true", - help="ignore unselected material slots", + help=t("help_ignore"), ) craft_parser.add_argument( "-n", "--craft-limit", type=int, default=-1, - help="specify the number of items to craft, (default: %(default)s)", + help=t("help_craft_limit"), metavar="{number of items}", ) move_parser = feature_parsers.add_parser( "move", - help="toggle moving forward", + help=t("help_move"), parents=[parent_parser], formatter_class=Formatter, ) @@ -304,12 +298,12 @@ def num_fish(_num_fish: str) -> int: "-s", "--shift", action="store_true", - help="Hold down the Shift key while moving", + help=t("help_shift"), ) harvest_parser = feature_parsers.add_parser( "harvest", - help="harvest baits", + help=t("help_harvest"), parents=[parent_parser], formatter_class=Formatter, ) @@ -320,12 +314,12 @@ def num_fish(_num_fish: str) -> int: "-r", "--refill", action="store_true", - help="refill hunger and comfort by consuming tea and carrot", + help=t("help_refill_harvest"), ) friction_brake_parser = feature_parsers.add_parser( "frictionbrake", - help="automate friction brake", + help=t("help_frictionbrake"), aliases=["fb"], parents=[parent_parser], formatter_class=Formatter, @@ -336,7 +330,7 @@ def num_fish(_num_fish: str) -> int: calculate_paser = feature_parsers.add_parser( "calculate", - help="calculate tackle's stats", + help=t("help_calculate"), aliases=["cal"], parents=[parent_parser], formatter_class=Formatter, @@ -361,15 +355,15 @@ def display_features() -> None: Shows a formatted table with feature IDs and names. """ table = Table( - "Features", - title="Select a feature to start 🚀", + t("features"), + title=t("select_feature"), show_header=False, box=box.HEAVY, min_width=36, ) for i, feature in enumerate(FEATURES): - table.add_row(f"{i:>2}. {feature['name']}") + table.add_row(f"{i:>2}. {t(feature['name_key'])}") print(table) @@ -379,30 +373,28 @@ def get_fid(parser: argparse.ArgumentParser) -> int: Continuously prompts until a valid feature ID is entered or the user chooses to quit. """ - utils.print_usage_box("Enter feature id to use, h to see help message, q to quit.") + utils.print_usage_box(t("enter_fid")) while True: user_input = input(">>> ") if user_input.isdigit() and 0 <= int(user_input) < len(FEATURES): break if user_input == "q": - print("Bye.") + print(t("bye")) sys.exit() if user_input == "h": parser.print_help() continue - utils.print_error("Invalid input, please try again.") + utils.print_error(t("invalid_input")) return int(user_input) def get_launch_options(parser: argparse.ArgumentParser) -> str: - utils.print_usage_box( - "Enter launch options, Enter to skip, h to see help message, q to quit." - ) + utils.print_usage_box(t("enter_launch_options")) while True: user_input = input(">>> ") if user_input == "q": - print("Bye.") + print(t("bye")) sys.exit() if user_input == "h": parser.print_help() @@ -412,30 +404,28 @@ def get_launch_options(parser: argparse.ArgumentParser) -> str: def get_language(): - utils.print_usage_box("What's your game language? [(1) en (2) ru (3) q (quit)]") + utils.print_usage_box(t("game_language_prompt")) while True: user_input = input(">>> ") if user_input.isdigit() and user_input in ("1", "2"): break if user_input == "q": - print("Bye.") + print(t("bye")) sys.exit() - utils.print_error("Invalid input, please try again.") + utils.print_error(t("invalid_input")) return '"en"' if user_input == "1" else '"ru"' def get_click_lock(): - utils.print_usage_box( - "Is Windows Mouse ClickLock enabled? [(1) yes (2) no (3) q (quit)]" - ) + utils.print_usage_box(t("click_lock_prompt")) while True: user_input = input(">>> ") if user_input.isdigit() and user_input in ("1", "2"): break if user_input == "q": - print("Bye.") + print(t("bye")) sys.exit() - utils.print_error("Invalid input, please try again.") + utils.print_error(t("invalid_input")) return "true" if user_input == "1" else "false" @@ -468,7 +458,9 @@ def setup_cfg(): def main() -> None: + setup_i18n("en") cfg = setup_cfg() + setup_i18n(cfg.LANGUAGE) parser, subparsers = setup_parser(cfg) args = parser.parse_args() # First parse to get {command} {flags} utils.print_logo_box(LOGO) # Print logo here so the help message will not show it diff --git a/pyproject.toml b/pyproject.toml index 8133143..30a2103 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "pynput==1.7.6", "pyscreeze==0.1.29", "python-dotenv==1.0.1", + "python-i18n[YAML]>=0.3.9", "pywin32==306", "pyyaml==6.0.2", "rich==13.9.4", diff --git a/requirements.txt b/requirements.txt index d23ab7b..8273675 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,6 +45,7 @@ pyscreenshot==3.1 pyscreeze==0.1.29 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 +python-i18n[YAML]>=0.3.9 pytweening==1.2.0 pywin32==306 pyyaml==6.0.2 diff --git a/rf4s/app/app.py b/rf4s/app/app.py index 4f51c65..0ed2eea 100644 --- a/rf4s/app/app.py +++ b/rf4s/app/app.py @@ -34,6 +34,7 @@ from rf4s import config, exceptions, utils from rf4s.app.core import logger +from rf4s.i18n import t from rf4s.component.friction_brake import FrictionBrake from rf4s.config import load_cfg from rf4s.controller.detection import Detection @@ -82,7 +83,7 @@ def display_result(self) -> None: if not result_dict: return - result = Table(title="Running Result", box=box.HEAVY, show_header=False) + result = Table(title=t("running_result"), box=box.HEAVY, show_header=False) for name, value in self.result.as_dict().items(): result.add_row(name, str(value)) print(result) @@ -136,9 +137,9 @@ def validate_cfg(self): def display_info(self): settings = Table( - title="Settings", show_header=False, box=box.HEAVY, min_width=36 + title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row("LAUNCH OPTIONS (FINAL)", " ".join(sys.argv[1:])) + settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) for k, v in self.cfg.PROFILE.items(): if k != "DESCRIPTION": settings.add_row(k, str(v)) @@ -146,7 +147,7 @@ def display_info(self): if self.cfg.PROFILE.DESCRIPTION: utils.print_description_box(self.cfg.PROFILE.DESCRIPTION) utils.print_usage_box( - f"Press {self.cfg.KEY.PAUSE} to pause, {self.cfg.KEY.QUIT} to quit." + t("press_pause_quit", pause=self.cfg.KEY.PAUSE, quit=self.cfg.KEY.QUIT) ) def validate_smtp(self) -> None: @@ -253,7 +254,7 @@ def display_profiles(self) -> None: Shows a formatted table with profile IDs and names. """ profiles = Table( - title="Select a profile to start ⚙️", + title=t("select_profile"), box=box.HEAVY, show_header=False, min_width=36, @@ -268,16 +269,16 @@ def get_pid(self) -> None: Continuously prompts until a valid profile ID is entered or the user chooses to quit. """ - utils.print_usage_box("Enter profile id to use, q to quit.") + utils.print_usage_box(t("enter_pid")) while True: user_input = input(">>> ") if user_input.isdigit() and 0 <= int(user_input) < len(self.cfg.PROFILE): break if user_input == "q": - print("Bye.") + print(t("bye")) sys.exit() - utils.print_error("Invalid profile id, please try again.") + utils.print_error(t("invalid_pid")) self.args.pid = int(user_input) @@ -448,11 +449,9 @@ def start(self) -> None: break utils.print_usage_box( - f"Press {self.cfg.KEY.PAUSE} to reload config and restart." - ) - utils.print_hint_box( - "Any modifications made to LAUNCH_OPTIONS will be ignored." + t("press_restart", pause=self.cfg.KEY.PAUSE) ) + utils.print_hint_box(t("hint_launch_ignored")) with self.player.hold_keys(mouse=False, shift=False, reset=True): pause_listener = keyboard.Listener(on_release=self._pause_wait) pause_listener.start() @@ -489,9 +488,9 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() settings = Table( - title="Settings", show_header=False, box=box.HEAVY, min_width=36 + title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row("LAUNCH OPTIONS (FINAL)", " ".join(sys.argv[1:])) + settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) print(settings) self.result = CraftResult() @@ -551,7 +550,7 @@ def start(self) -> None: listener.start() try: - utils.print_usage_box(f"Press {self.cfg.KEY.QUIT} to quit.") + utils.print_usage_box(t("press_quit", quit=self.cfg.KEY.QUIT)) logger.warning("This might get you banned, use at your own risk") logger.warning("Use Razor or Logitech macros instead") random.seed(datetime.now().timestamp()) @@ -606,8 +605,7 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() utils.print_usage_box( - f"Press {self.cfg.KEY.MOVE_PAUSE} to pause, " - f"{self.cfg.KEY.MOVE_QUIT} to quit.", + t("press_move", pause=self.cfg.KEY.MOVE_PAUSE, quit=self.cfg.KEY.MOVE_QUIT) ) self.result = Result() @@ -666,16 +664,16 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() settings = Table( - title="Settings", show_header=False, box=box.HEAVY, min_width=36 + title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row("LAUNCH OPTIONS (FINAL)", " ".join(sys.argv[1:])) - settings.add_row("Power saving", str(self.cfg.HARVEST.POWER_SAVING)) - settings.add_row("Check delay", str(self.cfg.HARVEST.CHECK_DELAY)) - settings.add_row("Energy threshold", str(self.cfg.STAT.ENERGY_THRESHOLD)) - settings.add_row("Hunger threshold", str(self.cfg.STAT.HUNGER_THRESHOLD)) - settings.add_row("Comfort threshold", str(self.cfg.STAT.COMFORT_THRESHOLD)) + settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) + settings.add_row(t("power_saving"), str(self.cfg.HARVEST.POWER_SAVING)) + settings.add_row(t("check_delay"), str(self.cfg.HARVEST.CHECK_DELAY)) + settings.add_row(t("energy_threshold"), str(self.cfg.STAT.ENERGY_THRESHOLD)) + settings.add_row(t("hunger_threshold"), str(self.cfg.STAT.HUNGER_THRESHOLD)) + settings.add_row(t("comfort_threshold"), str(self.cfg.STAT.COMFORT_THRESHOLD)) print(settings) - utils.print_usage_box(f"Press {self.cfg.KEY.QUIT} to quit.") + utils.print_usage_box(t("press_quit", quit=self.cfg.KEY.QUIT)) self.result = HarvestResult() self.timer = Timer(self.cfg) @@ -772,8 +770,9 @@ def start(self) -> None: @dataclass class Part: - name: str - prompt: str + key: str + name_key: str + prompt_key: str color: str base: float = 0.0 load_capacity: Optional[float] = None @@ -781,6 +780,14 @@ class Part: real_load_capacity: Optional[float] = None pre_real_load_capacity: Optional[float] = None + @property + def name(self) -> str: + return t(self.name_key) + + @property + def prompt(self) -> str: + return t(self.prompt_key) + def calculate_real_load_capacity(self) -> None: self.real_load_capacity = ( self.load_capacity * (1 - self.base) * (1 - self.wear / 100) @@ -802,15 +809,15 @@ def __init__(self, cfg, args, parser): _ = cfg, args, parser self.result = None self.parts = [ - Part(name="Rod", prompt="Load capacity (kg)", color="orange1", base=0.3), - Part(name="Reel mechanism", prompt="Mech (kg)", color="plum1", base=0.3), - Part(name="Reel friction brake", prompt="Drag (kg)", color="gold1"), - Part(name="Fishing line", prompt="Load capacity (kg)", color="salmon1"), - Part(name="Leader", prompt="Load capacity (kg)", color="pale_green1"), - Part(name="Hook", prompt="Load capacity (kg)", color="sky_blue1"), + Part(key="rod", name_key="part_rod", prompt_key="prompt_load_capacity", color="orange1", base=0.3), + Part(key="reel_mechanism", name_key="part_reel_mechanism", prompt_key="prompt_mech", color="plum1", base=0.3), + Part(key="reel_friction_brake", name_key="part_reel_fb", prompt_key="prompt_drag", color="gold1"), + Part(key="fishing_line", name_key="part_fishing_line", prompt_key="prompt_load_capacity", color="salmon1"), + Part(key="leader", name_key="part_leader", prompt_key="prompt_load_capacity", color="pale_green1"), + Part(key="hook", name_key="part_hook", prompt_key="prompt_load_capacity", color="sky_blue1"), ] self.friction_brake = next( - part for part in self.parts if part.name == "Reel friction brake" + part for part in self.parts if part.key == "reel_friction_brake" ) def calculate_tackle_stats(self): @@ -821,9 +828,9 @@ def calculate_tackle_stats(self): if part.pre_real_load_capacity is not None: raise exceptions.PreviousError else: - utils.print_error(f"{part.name}'s value not found.") + utils.print_error(t("value_not_found", name=part.name)) part.load_capacity = self.get_validated_input(part, part.prompt) - part.wear = self.get_validated_input(part, "Wear (%)") + part.wear = self.get_validated_input(part, t("prompt_wear"), is_wear=True) except exceptions.SkipError: if part.real_load_capacity is not None: part.real_load_capacity = None @@ -837,7 +844,7 @@ def calculate_tackle_stats(self): part.calculate_real_load_capacity() self.result.add_row(part.name, f"{part.real_load_capacity:.2f} kg") - def get_validated_input(self, part: Part, prompt: str) -> float: + def get_validated_input(self, part: Part, prompt: str, is_wear: bool = False) -> float: while True: user_input = Prompt.ask( f"[{part.color}][{part.name}][/{part.color}] {prompt}" @@ -847,12 +854,12 @@ def get_validated_input(self, part: Part, prompt: str) -> float: raise exceptions.RestartError case CalculateCommand.PREVIOUS.value: if part.pre_real_load_capacity is None: - utils.print_error(f"{part.name}'s value not found.") + utils.print_error(t("value_not_found", name=part.name)) continue raise exceptions.PreviousError case CalculateCommand.PREVIOUS_REMAINING.value: if part.pre_real_load_capacity is None: - utils.print_error(f"{part.name}'s value not found.") + utils.print_error(t("value_not_found", name=part.name)) continue raise exceptions.PreviousRemainingError case CalculateCommand.SKIP.value: @@ -864,24 +871,24 @@ def get_validated_input(self, part: Part, prompt: str) -> float: try: number = float(user_input) - if prompt.startswith("Wear"): + if is_wear: if not (0 <= number <= 100): - utils.print_error("Wear must be between 0 and 100.") + utils.print_error(t("wear_range")) continue elif number < 0: - utils.print_error("Value must be non-negative.") + utils.print_error(t("non_negative")) continue return number except ValueError: - utils.print_error("Invalid input. Please try again.") + utils.print_error(t("invalid_input_retry")) def reset_stats(self) -> None: for part in self.parts: part.pre_real_load_capacity = part.real_load_capacity part.real_load_capacity = None self.result = Table( - "Results", - title="Tackle's Stats", + t("results"), + title=t("tackle_stats"), show_header=False, box=box.HEAVY, min_width=36, @@ -892,7 +899,7 @@ def update_result(self) -> None: if not valid_parts: return weakest_part = min(valid_parts, key=lambda x: x.real_load_capacity) - self.result.add_row("Weakest part", weakest_part.name) + self.result.add_row(t("weakest_part"), weakest_part.name) if self.friction_brake.real_load_capacity is None: return @@ -907,7 +914,7 @@ def update_result(self) -> None: ), ) self.result.add_row( - "Recommend friction brake", + t("recommend_fb"), f"{int(recommend_friction_brake):2d}", ) except ZeroDivisionError: @@ -918,16 +925,8 @@ def start(self): Prompts the user for input, calculates the result, and displays them in a table. """ - utils.print_usage_box( - "Commands:\n" - "r: Restart\n" - "s: Skip a part\n" - "S: Skip the remaining parts\n" - "p: Use previous value for a part\n" - "P: Use previous value for the remaing parts\n" - "q: Quit" - ) - utils.print_hint_box("Press V and click the gear icon to view the parts.") + utils.print_usage_box(t("commands_help")) + utils.print_hint_box(t("hint_view_parts")) while True: self.reset_stats() @@ -938,7 +937,7 @@ def start(self): except exceptions.RestartError: continue except exceptions.QuitError: - print("Bye.") + print(t("bye")) break if self.result.rows: self.update_result() @@ -971,19 +970,18 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() settings = Table( - title="Settings", show_header=False, box=box.HEAVY, min_width=36 + title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row("LAUNCH OPTIONS (FINAL)", " ".join(sys.argv[1:])) - settings.add_row("Initial friction brake", str(self.cfg.FRICTION_BRAKE.INITIAL)) - settings.add_row("Max friction brake", str(self.cfg.FRICTION_BRAKE.MAX)) - settings.add_row("Start delay", str(self.cfg.FRICTION_BRAKE.START_DELAY)) - settings.add_row("Increase delay", str(self.cfg.FRICTION_BRAKE.INCREASE_DELAY)) - settings.add_row("Sensitivity", self.cfg.FRICTION_BRAKE.SENSITIVITY) + settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) + settings.add_row(t("initial_fb"), str(self.cfg.FRICTION_BRAKE.INITIAL)) + settings.add_row(t("max_fb"), str(self.cfg.FRICTION_BRAKE.MAX)) + settings.add_row(t("start_delay"), str(self.cfg.FRICTION_BRAKE.START_DELAY)) + settings.add_row(t("increase_delay"), str(self.cfg.FRICTION_BRAKE.INCREASE_DELAY)) + settings.add_row(t("sensitivity"), self.cfg.FRICTION_BRAKE.SENSITIVITY) print(settings) utils.print_usage_box( - f"Press {self.cfg.KEY.FRICTION_BRAKE_RESET} to reset friction brake, " - f"{self.cfg.KEY.FRICTION_BRAKE_QUIT} to quit." + t("press_fb", reset=self.cfg.KEY.FRICTION_BRAKE_RESET, quit=self.cfg.KEY.FRICTION_BRAKE_QUIT) ) self.window = Window() diff --git a/rf4s/controller/player.py b/rf4s/controller/player.py index 118da98..05830e8 100644 --- a/rf4s/controller/player.py +++ b/rf4s/controller/player.py @@ -30,6 +30,7 @@ from rf4s.controller.detection import Detection, TagColor from rf4s.controller.notification import send_result, send_screenshot from rf4s.controller.timer import Timer +from rf4s.i18n import t from rf4s.result.result import BotResult from rf4s.utils import add_jitter @@ -990,7 +991,7 @@ def get_result_table(self, result) -> Table: :return: formatted running result table :rtype: Table """ - table = Table(title="Running Result", box=box.HEAVY, show_header=False) + table = Table(title=t("running_result"), box=box.HEAVY, show_header=False) for k, v in result.items(): table.add_row(k, str(v)) diff --git a/rf4s/i18n/__init__.py b/rf4s/i18n/__init__.py new file mode 100644 index 0000000..c29e77a --- /dev/null +++ b/rf4s/i18n/__init__.py @@ -0,0 +1,23 @@ +import sys +from pathlib import Path + +import i18n + +i18n.set("filename_format", "{locale}.{format}") +i18n.set("file_format", "yml") +i18n.set("fallback", "en") +i18n.set("enable_memoization", True) + +if "__compiled__" in globals(): + _i18n_dir = Path(sys.executable).parent / "rf4s" / "i18n" +else: + _i18n_dir = Path(__file__).parent + +i18n.load_path.append(str(_i18n_dir)) + + +def setup(language: str = "en") -> None: + i18n.set("locale", language) + + +t = i18n.t diff --git a/rf4s/i18n/en.yml b/rf4s/i18n/en.yml new file mode 100644 index 0000000..063a1b7 --- /dev/null +++ b/rf4s/i18n/en.yml @@ -0,0 +1,169 @@ +en: + # Common + bye: "Bye." + invalid_input: "Invalid input, please try again." + press_any_key: "Press any key to quit." + using: "You're now using: %{msg}" + hint: "Hint: %{msg}" + + # main.py - feature selection + select_feature: "Select a feature to start 🚀" + features: "Features" + enter_fid: "Enter feature id to use, h to see help message, q to quit." + enter_launch_options: "Enter launch options, Enter to skip, h to see help message, q to quit." + game_language_prompt: "What's your game language? [(1) en (2) ru (3) q (quit)]" + click_lock_prompt: "Is Windows Mouse ClickLock enabled? [(1) yes (2) no (3) q (quit)]" + + # Feature names + feature_bot: "Fishing Bot" + feature_craft: "Craft Items" + feature_move: "Move Forward" + feature_harvest: "Harvest Baits" + feature_frictionbrake: "Auto Friction Brake" + feature_calculate: "Calculate Tackle's Stats" + + # Argparse help (subcommands) + help_bot: "start fishing bot" + help_craft: "craft items" + help_move: "toggle moving forward" + help_harvest: "harvest baits" + help_frictionbrake: "automate friction brake" + help_calculate: "calculate tackle's stats" + + # Argparse help (bot boolean arguments) + help_tag: "keep only tagged fishes" + help_coffee: "drink coffee if stamina is low during fish fight" + help_alcohol: "drink alcohol before keeping the fish" + help_refill: "consume tea and carrot if hunger or comfort is low" + help_harvest_arg: "harvest baits before casting the rod" + help_lure: "change current lure with a random favorite one, mode: spin" + help_mouse: "move mouse randomly before casting the rod" + help_pause: "pause the script before casting the rod occasionally" + help_random_cast: "do a redundant rod cast randomly" + help_skip_cast: "skip the first rod cast" + help_lift: "lift the tackle constantly during a fish fight" + help_electro: "enable electric mode for Electro Raptor series reel" + help_friction_brake: "adjust friction brake automatically" + help_gear_ratio: "switch the gear ratio or mode after the retrieval timed out" + help_bite: "save a screenshot in screenshots/ when a fish bite" + help_screenshot: "save a screenshot in screenshots/ after you caught a fish" + help_data: "save fishing data in /logs" + help_email: "send email noticication after the script stop" + help_miaotixing: "send miaotixing notification after the script stop" + help_discord: "send Discord notification after the script stop" + help_telegram: "send Telegram notification after the script stop" + help_shutdown: "shutdown computer after the script stop" + help_signout: "sign out instead of closing the game" + help_broken_lure: "replace broken lures with favorite ones" + help_spod_rod: "recast spod rod" + help_dry_mix: "enable dry mix refill, mode: bottom" + help_groundbait: "enable groundbait refill, mode: bottom" + help_pva: "enable pva refill, mode: bottom" + help_no_animation: "disable waiting for trophy and gift animations, gift\nchange 'Catch screen style' to 'Simple' in game settings to use this flag" + + # Argparse help (other arguments) + help_opts: "overwrite configuration" + help_pid: "specify the id of the profile to use" + help_pname: "specify the name of the profile to use" + help_fishes_in_keepnet: "specify the number of fishes in your keepnet, (default: %(default)s)" + help_trolling: "enable trolling mode and specify the direction\n(default: %(default)s, no argument: %(const)s)" + help_rainbow: "enable rainbow line mode and specify the meter to lift the rod\n(default: %(default)s, no argument: %(const)s)" + help_boat_ticket: "enable boat ticket renewal and specify the duration\n(default: %(default)s, no argument: %(const)s)" + help_discard: "discard all the crafted items (for groundbaits)" + help_ignore: "ignore unselected material slots" + help_craft_limit: "specify the number of items to craft, (default: %(default)s)" + help_shift: "Hold down the Shift key while moving" + help_refill_harvest: "refill hunger and comfort by consuming tea and carrot" + + # app.py - common UI + settings: "Settings" + launch_options_final: "LAUNCH OPTIONS (FINAL)" + running_result: "Running Result" + select_profile: "Select a profile to start ⚙️" + enter_pid: "Enter profile id to use, q to quit." + invalid_pid: "Invalid profile id, please try again." + press_pause_quit: "Press %{pause} to pause, %{quit} to quit." + press_quit: "Press %{quit} to quit." + press_move: "Press %{pause} to pause, %{quit} to quit." + press_restart: "Press %{pause} to reload config and restart." + hint_launch_ignored: "Any modifications made to LAUNCH_OPTIONS will be ignored." + press_fb: "Press %{reset} to reset friction brake, %{quit} to quit." + + # Harvest settings + power_saving: "Power saving" + check_delay: "Check delay" + energy_threshold: "Energy threshold" + hunger_threshold: "Hunger threshold" + comfort_threshold: "Comfort threshold" + + # Friction brake settings + initial_fb: "Initial friction brake" + max_fb: "Max friction brake" + start_delay: "Start delay" + increase_delay: "Increase delay" + sensitivity: "Sensitivity" + + # CalculateApp + tackle_stats: "Tackle's Stats" + results: "Results" + weakest_part: "Weakest part" + recommend_fb: "Recommend friction brake" + commands_help: "Commands:\nr: Restart\ns: Skip a part\nS: Skip the remaining parts\np: Use previous value for a part\nP: Use previous value for the remaining parts\nq: Quit" + hint_view_parts: "Press V and click the gear icon to view the parts." + value_not_found: "%{name}'s value not found." + wear_range: "Wear must be between 0 and 100." + non_negative: "Value must be non-negative." + invalid_input_retry: "Invalid input. Please try again." + + # Tackle part names + part_rod: "Rod" + part_reel_mechanism: "Reel mechanism" + part_reel_fb: "Reel friction brake" + part_fishing_line: "Fishing line" + part_leader: "Leader" + part_hook: "Hook" + prompt_load_capacity: "Load capacity (kg)" + prompt_mech: "Mech (kg)" + prompt_drag: "Drag (kg)" + prompt_wear: "Wear (%)" + + # result.py - result labels + result_stop_reason: "Stop reason" + result_start_time: "Start time" + result_end_time: "End time" + result_running_time: "Running time" + result_bite_rate: "Bite rate" + result_total_fish: "Total fish" + result_kept_fish: "Kept fish" + result_kept_ratio: "Kept ratio" + result_green_tag: "Green tag fish" + result_yellow_tag: "Yellow tag fish" + result_blue_tag: "Blue tag fish" + result_purple_tag: "Purple tag fish" + result_pink_tag: "Pink tag fish" + result_card: "Card" + result_gift: "Gift" + result_tea: "Tea consumed" + result_carrot: "Carrot consumed" + result_alcohol: "Alcohol consumed" + result_coffee: "Coffee consumed" + result_bait: "Bait harvested" + result_ticket: "Ticket used" + result_success_crafts: "Successful crafts" + result_fail_crafts: "Failed crafts" + result_materials: "Materials used" + + # Stop reasons + stop_keepnet_full: "Keepnet is full" + stop_tackle_broken: "Tackle is broken" + stop_line_at_end: "Fishing line is at its end" + stop_coffee_limit: "Coffee limit reached" + stop_no_bait: "Run out of bait" + stop_no_rods: "All rods are unavailable" + stop_lure_broken: "Lure is broken" + stop_line_snagged: "Line is snagged" + stop_ticket_expired: "Boat ticket expired" + stop_no_ticket: "New boat ticket not found" + stop_no_favorite: "Favorite item not found" + stop_disconnected: "Game disconnected" + stop_terminated: "Terminated by user" diff --git a/rf4s/i18n/ru.yml b/rf4s/i18n/ru.yml new file mode 100644 index 0000000..4240559 --- /dev/null +++ b/rf4s/i18n/ru.yml @@ -0,0 +1,169 @@ +ru: + # Общие + bye: "Пока." + invalid_input: "Неверный ввод, попробуйте ещё раз." + press_any_key: "Нажмите любую клавишу для выхода." + using: "Вы используете: %{msg}" + hint: "Подсказка: %{msg}" + + # main.py - выбор функций + select_feature: "Выберите функцию для запуска 🚀" + features: "Функции" + enter_fid: "Введите id функции, h — справка, q — выход." + enter_launch_options: "Введите параметры запуска, Enter — пропустить, h — справка, q — выход." + game_language_prompt: "Какой язык в игре? [(1) en (2) ru (3) q (выход)]" + click_lock_prompt: "Включена ли залипание кнопки мыши Windows? [(1) да (2) нет (3) q (выход)]" + + # Названия функций + feature_bot: "Рыболовный бот" + feature_craft: "Крафт предметов" + feature_move: "Движение вперёд" + feature_harvest: "Сбор наживки" + feature_frictionbrake: "Авто фрикцион" + feature_calculate: "Расчёт характеристик снасти" + + # Argparse help (подкоманды) + help_bot: "запустить рыболовного бота" + help_craft: "крафтить предметы" + help_move: "переключить движение вперёд" + help_harvest: "собирать наживку" + help_frictionbrake: "автоматизировать фрикцион" + help_calculate: "рассчитать характеристики снасти" + + # Argparse help (булевые аргументы бота) + help_tag: "оставлять только помеченных рыб" + help_coffee: "пить кофе при низкой выносливости во время вываживания" + help_alcohol: "пить алкоголь перед сохранением рыбы" + help_refill: "употреблять чай и морковь при низком голоде или комфорте" + help_harvest_arg: "собирать наживку перед забросом" + help_lure: "менять приманку на случайную избранную, режим: спиннинг" + help_mouse: "случайно двигать мышь перед забросом" + help_pause: "иногда ставить скрипт на паузу перед забросом" + help_random_cast: "случайно делать лишний заброс" + help_skip_cast: "пропустить первый заброс" + help_lift: "постоянно поднимать снасть во время вываживания" + help_electro: "включить электрический режим для катушки Electro Raptor" + help_friction_brake: "автоматически регулировать фрикцион" + help_gear_ratio: "переключить передаточное число после таймаута подмотки" + help_bite: "сохранять скриншот в screenshots/ при поклёвке" + help_screenshot: "сохранять скриншот в screenshots/ после поимки рыбы" + help_data: "сохранять данные рыбалки в /logs" + help_email: "отправить email-уведомление после остановки скрипта" + help_miaotixing: "отправить уведомление miaotixing после остановки скрипта" + help_discord: "отправить уведомление Discord после остановки скрипта" + help_telegram: "отправить уведомление Telegram после остановки скрипта" + help_shutdown: "выключить компьютер после остановки скрипта" + help_signout: "выйти из аккаунта вместо закрытия игры" + help_broken_lure: "заменять сломанные приманки на избранные" + help_spod_rod: "перезабрасывать спод-род" + help_dry_mix: "включить пополнение сухой смеси, режим: донка" + help_groundbait: "включить пополнение прикормки, режим: донка" + help_pva: "включить пополнение PVA, режим: донка" + help_no_animation: "отключить ожидание анимации трофея и подарка\nизмените 'Стиль экрана поимки' на 'Простой' в настройках игры" + + # Argparse help (другие аргументы) + help_opts: "перезаписать конфигурацию" + help_pid: "указать id профиля" + help_pname: "указать имя профиля" + help_fishes_in_keepnet: "указать количество рыб в садке, (по умолчанию: %(default)s)" + help_trolling: "включить режим троллинга и указать направление\n(по умолчанию: %(default)s, без аргумента: %(const)s)" + help_rainbow: "включить режим радужной лески и указать метры подъёма\n(по умолчанию: %(default)s, без аргумента: %(const)s)" + help_boat_ticket: "включить продление билета на лодку и указать длительность\n(по умолчанию: %(default)s, без аргумента: %(const)s)" + help_discard: "выбрасывать все скрафченные предметы (для прикормки)" + help_ignore: "игнорировать невыбранные слоты материалов" + help_craft_limit: "указать количество предметов для крафта, (по умолчанию: %(default)s)" + help_shift: "удерживать Shift при движении" + help_refill_harvest: "восполнять голод и комфорт чаем и морковью" + + # app.py - общие элементы UI + settings: "Настройки" + launch_options_final: "ПАРАМЕТРЫ ЗАПУСКА (ИТОГ)" + running_result: "Результат работы" + select_profile: "Выберите профиль для запуска ⚙️" + enter_pid: "Введите id профиля, q — выход." + invalid_pid: "Неверный id профиля, попробуйте ещё раз." + press_pause_quit: "Нажмите %{pause} для паузы, %{quit} для выхода." + press_quit: "Нажмите %{quit} для выхода." + press_move: "Нажмите %{pause} для паузы, %{quit} для выхода." + press_restart: "Нажмите %{pause} для перезагрузки конфига и перезапуска." + hint_launch_ignored: "Любые изменения LAUNCH_OPTIONS будут проигнорированы." + press_fb: "Нажмите %{reset} для сброса фрикциона, %{quit} для выхода." + + # Harvest settings + power_saving: "Энергосбережение" + check_delay: "Задержка проверки" + energy_threshold: "Порог энергии" + hunger_threshold: "Порог голода" + comfort_threshold: "Порог комфорта" + + # Friction brake settings + initial_fb: "Начальный фрикцион" + max_fb: "Макс. фрикцион" + start_delay: "Задержка старта" + increase_delay: "Задержка увеличения" + sensitivity: "Чувствительность" + + # CalculateApp + tackle_stats: "Характеристики снасти" + results: "Результаты" + weakest_part: "Слабое звено" + recommend_fb: "Рекомендуемый фрикцион" + commands_help: "Команды:\nr: Перезапуск\ns: Пропустить часть\nS: Пропустить оставшиеся части\np: Использовать предыдущее значение\nP: Использовать предыдущее значение для оставшихся\nq: Выход" + hint_view_parts: "Нажмите V и кликните на иконку шестерёнки для просмотра частей." + value_not_found: "Значение %{name} не найдено." + wear_range: "Износ должен быть от 0 до 100." + non_negative: "Значение должно быть неотрицательным." + invalid_input_retry: "Неверный ввод. Попробуйте ещё раз." + + # Названия частей снасти + part_rod: "Удилище" + part_reel_mechanism: "Механизм катушки" + part_reel_fb: "Фрикцион катушки" + part_fishing_line: "Леска" + part_leader: "Поводок" + part_hook: "Крючок" + prompt_load_capacity: "Нагрузка (кг)" + prompt_mech: "Мех (кг)" + prompt_drag: "Тяга (кг)" + prompt_wear: "Износ (%)" + + # result.py - метки результатов + result_stop_reason: "Причина остановки" + result_start_time: "Время начала" + result_end_time: "Время окончания" + result_running_time: "Время работы" + result_bite_rate: "Частота поклёвок" + result_total_fish: "Всего рыб" + result_kept_fish: "Оставлено рыб" + result_kept_ratio: "Доля оставленных" + result_green_tag: "Зелёная метка" + result_yellow_tag: "Жёлтая метка" + result_blue_tag: "Синяя метка" + result_purple_tag: "Фиолетовая метка" + result_pink_tag: "Розовая метка" + result_card: "Карточка" + result_gift: "Подарок" + result_tea: "Чай выпито" + result_carrot: "Морковь съедено" + result_alcohol: "Алкоголь выпито" + result_coffee: "Кофе выпито" + result_bait: "Наживки собрано" + result_ticket: "Билетов использовано" + result_success_crafts: "Успешных крафтов" + result_fail_crafts: "Неудачных крафтов" + result_materials: "Материалов использовано" + + # Причины остановки + stop_keepnet_full: "Садок полон" + stop_tackle_broken: "Снасть сломана" + stop_line_at_end: "Леска закончилась" + stop_coffee_limit: "Лимит кофе достигнут" + stop_no_bait: "Наживка закончилась" + stop_no_rods: "Все удилища недоступны" + stop_lure_broken: "Приманка сломана" + stop_line_snagged: "Леска зацепилась" + stop_ticket_expired: "Билет на лодку истёк" + stop_no_ticket: "Новый билет на лодку не найден" + stop_no_favorite: "Избранный предмет не найден" + stop_disconnected: "Игра отключена" + stop_terminated: "Остановлено пользователем" diff --git a/rf4s/i18n/zh-TW.yml b/rf4s/i18n/zh-TW.yml new file mode 100644 index 0000000..c073887 --- /dev/null +++ b/rf4s/i18n/zh-TW.yml @@ -0,0 +1,169 @@ +zh-TW: + # 通用 + bye: "再見。" + invalid_input: "輸入無效,請再試一次。" + press_any_key: "按任意鍵退出。" + using: "您正在使用:%{msg}" + hint: "提示:%{msg}" + + # main.py - 功能選擇 + select_feature: "選擇要啟動的功能 🚀" + features: "功能" + enter_fid: "輸入功能 id,h 查看說明,q 退出。" + enter_launch_options: "輸入啟動選項,Enter 跳過,h 查看說明,q 退出。" + game_language_prompt: "您的遊戲語言是什麼?[(1) en (2) ru (3) q (退出)]" + click_lock_prompt: "是否啟用了 Windows 滑鼠鎖定點擊?[(1) 是 (2) 否 (3) q (退出)]" + + # 功能名稱 + feature_bot: "釣魚機器人" + feature_craft: "製作物品" + feature_move: "向前移動" + feature_harvest: "收集餌料" + feature_frictionbrake: "自動煞車" + feature_calculate: "計算釣具數據" + + # Argparse help(子命令) + help_bot: "啟動釣魚機器人" + help_craft: "製作物品" + help_move: "切換向前移動" + help_harvest: "收集餌料" + help_frictionbrake: "自動化煞車" + help_calculate: "計算釣具數據" + + # Argparse help(機器人布林參數) + help_tag: "只保留有標記的魚" + help_coffee: "戰鬥中體力低時喝咖啡" + help_alcohol: "保留魚之前喝酒" + help_refill: "飢餓或舒適度低時消耗茶和胡蘿蔔" + help_harvest_arg: "拋竿前收集餌料" + help_lure: "用隨機收藏的餌更換當前餌,模式:路亞" + help_mouse: "拋竿前隨機移動滑鼠" + help_pause: "偶爾在拋竿前暫停腳本" + help_random_cast: "隨機進行多餘的拋竿" + help_skip_cast: "跳過第一次拋竿" + help_lift: "戰鬥中持續提竿" + help_electro: "啟用 Electro Raptor 系列捲線器的電動模式" + help_friction_brake: "自動調整煞車" + help_gear_ratio: "收線超時後切換齒輪比或模式" + help_bite: "魚咬鉤時在 screenshots/ 保存截圖" + help_screenshot: "釣到魚後在 screenshots/ 保存截圖" + help_data: "將釣魚數據保存到 /logs" + help_email: "腳本停止後發送電子郵件通知" + help_miaotixing: "腳本停止後發送喵提醒通知" + help_discord: "腳本停止後發送 Discord 通知" + help_telegram: "腳本停止後發送 Telegram 通知" + help_shutdown: "腳本停止後關閉電腦" + help_signout: "登出而不是關閉遊戲" + help_broken_lure: "用收藏的餌替換損壞的餌" + help_spod_rod: "重新拋擲誘餌竿" + help_dry_mix: "啟用乾粉補充,模式:底釣" + help_groundbait: "啟用窩料補充,模式:底釣" + help_pva: "啟用 PVA 補充,模式:底釣" + help_no_animation: "停用獎盃和禮物動畫等待\n在遊戲設定中將「捕獲畫面樣式」改為「簡單」以使用此選項" + + # Argparse help(其他參數) + help_opts: "覆寫設定" + help_pid: "指定要使用的設定檔 id" + help_pname: "指定要使用的設定檔名稱" + help_fishes_in_keepnet: "指定魚護中的魚數,(預設:%(default)s)" + help_trolling: "啟用拖釣模式並指定方向\n(預設:%(default)s,無參數:%(const)s)" + help_rainbow: "啟用彩虹線模式並指定提竿公尺數\n(預設:%(default)s,無參數:%(const)s)" + help_boat_ticket: "啟用船票續期並指定時長\n(預設:%(default)s,無參數:%(const)s)" + help_discard: "丟棄所有製作的物品(用於窩料)" + help_ignore: "忽略未選擇的材料欄位" + help_craft_limit: "指定要製作的物品數量,(預設:%(default)s)" + help_shift: "移動時按住 Shift 鍵" + help_refill_harvest: "透過消耗茶和胡蘿蔔來補充飢餓和舒適度" + + # app.py - 通用 UI + settings: "設定" + launch_options_final: "啟動選項(最終)" + running_result: "執行結果" + select_profile: "選擇要啟動的設定檔 ⚙️" + enter_pid: "輸入設定檔 id,q 退出。" + invalid_pid: "無效的設定檔 id,請再試一次。" + press_pause_quit: "按 %{pause} 暫停,%{quit} 退出。" + press_quit: "按 %{quit} 退出。" + press_move: "按 %{pause} 暫停,%{quit} 退出。" + press_restart: "按 %{pause} 重新載入設定並重啟。" + hint_launch_ignored: "對 LAUNCH_OPTIONS 的任何修改將被忽略。" + press_fb: "按 %{reset} 重置煞車,%{quit} 退出。" + + # Harvest settings + power_saving: "省電模式" + check_delay: "檢查延遲" + energy_threshold: "能量閾值" + hunger_threshold: "飢餓閾值" + comfort_threshold: "舒適度閾值" + + # Friction brake settings + initial_fb: "初始煞車" + max_fb: "最大煞車" + start_delay: "啟動延遲" + increase_delay: "增加延遲" + sensitivity: "靈敏度" + + # CalculateApp + tackle_stats: "釣具數據" + results: "結果" + weakest_part: "最弱部件" + recommend_fb: "建議煞車值" + commands_help: "指令:\nr:重新開始\ns:跳過一個部件\nS:跳過剩餘部件\np:使用上次的值\nP:對剩餘部件使用上次的值\nq:退出" + hint_view_parts: "按 V 並點擊齒輪圖示查看部件。" + value_not_found: "找不到 %{name} 的值。" + wear_range: "磨損必須在 0 到 100 之間。" + non_negative: "值必須為非負數。" + invalid_input_retry: "輸入無效,請再試一次。" + + # 釣具部件名稱 + part_rod: "釣竿" + part_reel_mechanism: "捲線器機構" + part_reel_fb: "捲線器煞車" + part_fishing_line: "釣線" + part_leader: "子線" + part_hook: "魚鉤" + prompt_load_capacity: "承載力(kg)" + prompt_mech: "機械(kg)" + prompt_drag: "阻力(kg)" + prompt_wear: "磨損(%)" + + # result.py - 結果標籤 + result_stop_reason: "停止原因" + result_start_time: "開始時間" + result_end_time: "結束時間" + result_running_time: "執行時間" + result_bite_rate: "咬鉤率" + result_total_fish: "總魚數" + result_kept_fish: "保留魚數" + result_kept_ratio: "保留比例" + result_green_tag: "綠色標記魚" + result_yellow_tag: "黃色標記魚" + result_blue_tag: "藍色標記魚" + result_purple_tag: "紫色標記魚" + result_pink_tag: "粉色標記魚" + result_card: "卡片" + result_gift: "禮物" + result_tea: "消耗茶" + result_carrot: "消耗胡蘿蔔" + result_alcohol: "消耗酒" + result_coffee: "消耗咖啡" + result_bait: "收穫餌料" + result_ticket: "使用船票" + result_success_crafts: "成功製作" + result_fail_crafts: "失敗製作" + result_materials: "使用材料" + + # 停止原因 + stop_keepnet_full: "魚護已滿" + stop_tackle_broken: "釣具損壞" + stop_line_at_end: "釣線已到盡頭" + stop_coffee_limit: "咖啡上限已達" + stop_no_bait: "餌料用完" + stop_no_rods: "所有釣竿不可用" + stop_lure_broken: "餌已損壞" + stop_line_snagged: "釣線纏住" + stop_ticket_expired: "船票已過期" + stop_no_ticket: "找不到新船票" + stop_no_favorite: "找不到收藏物品" + stop_disconnected: "遊戲已斷線" + stop_terminated: "使用者終止" diff --git a/rf4s/result/result.py b/rf4s/result/result.py index 4882f90..4674ccd 100644 --- a/rf4s/result/result.py +++ b/rf4s/result/result.py @@ -1,6 +1,23 @@ from dataclasses import dataclass from rf4s.controller.timer import Timer +from rf4s.i18n import t + +STOP_REASON_KEYS = { + "Keepnet is full": "stop_keepnet_full", + "Tackle is broken": "stop_tackle_broken", + "Fishing line is at its end": "stop_line_at_end", + "Coffee limit reached": "stop_coffee_limit", + "Run out of bait": "stop_no_bait", + "All rods are unavailable": "stop_no_rods", + "Lure is broken": "stop_lure_broken", + "Line is snagged": "stop_line_snagged", + "Boat ticket expired": "stop_ticket_expired", + "New boat ticket not found": "stop_no_ticket", + "Favorite item not found": "stop_no_favorite", + "Game disconnected": "stop_disconnected", + "Terminated by user": "stop_terminated", +} @dataclass @@ -38,28 +55,31 @@ def as_dict(self, msg: str, timer: Timer) -> dict: kept_ratio = "0%" bite_rate = "0/hr" + stop_key = STOP_REASON_KEYS.get(msg) + stop_reason = t(stop_key) if stop_key else msg + return { - "Stop reason": msg, - "Start time": timer.get_start_datetime(), - "End time": timer.get_cur_datetime(), - "Running time": timer.get_running_time_str(), - "Bite rate": bite_rate, - "Total fish": self.total, - "Kept fish": self.kept, - "Kept ratio": kept_ratio, - "Green tag fish": self.green, - "Yellow tag fish": self.yellow, - "Blue tag fish": self.blue, - "Purple tag fish": self.purple, - "Pink tag fish": self.pink, - "Card": self.card, - "Gift": self.gift, - "Tea consumed": self.tea, - "Carrot consumed": self.carrot, - "Alcohol consumed": self.alcohol, - "Coffee consumed": self.coffee, - "Bait harvested": self.bait, - "Ticket used": self.ticket, + t("result_stop_reason"): stop_reason, + t("result_start_time"): timer.get_start_datetime(), + t("result_end_time"): timer.get_cur_datetime(), + t("result_running_time"): timer.get_running_time_str(), + t("result_bite_rate"): bite_rate, + t("result_total_fish"): self.total, + t("result_kept_fish"): self.kept, + t("result_kept_ratio"): kept_ratio, + t("result_green_tag"): self.green, + t("result_yellow_tag"): self.yellow, + t("result_blue_tag"): self.blue, + t("result_purple_tag"): self.purple, + t("result_pink_tag"): self.pink, + t("result_card"): self.card, + t("result_gift"): self.gift, + t("result_tea"): self.tea, + t("result_carrot"): self.carrot, + t("result_alcohol"): self.alcohol, + t("result_coffee"): self.coffee, + t("result_bait"): self.bait, + t("result_ticket"): self.ticket, } @@ -71,9 +91,9 @@ class CraftResult: def as_dict(self) -> dict: return { - "Successful crafts": self.success, - "Failed crafts": self.fail, - "Materials used": self.material, + t("result_success_crafts"): self.success, + t("result_fail_crafts"): self.fail, + t("result_materials"): self.material, } @@ -85,7 +105,7 @@ class HarvestResult: def as_dict(self) -> dict: return { - "Tea consumed": self.tea, - "Carrot consumed": self.carrot, - "Bait harvested": self.bait, + t("result_tea"): self.tea, + t("result_carrot"): self.carrot, + t("result_bait"): self.bait, } diff --git a/rf4s/utils.py b/rf4s/utils.py index 47edcd6..3b52684 100644 --- a/rf4s/utils.py +++ b/rf4s/utils.py @@ -20,6 +20,7 @@ from rich.panel import Panel from rf4s.controller.console import console +from rf4s.i18n import t LOOP_DELAY = 1 @@ -125,11 +126,11 @@ def print_usage_box(msg: str) -> None: def print_description_box(msg: str) -> None: - print(Panel.fit(f"You're now using: {msg}")) + print(Panel.fit(t("using", msg=msg))) def print_hint_box(msg: str) -> None: - print(Panel.fit(f"Hint: {msg}", style="green")) + print(Panel.fit(t("hint", msg=msg), style="green")) def print_error(msg: str) -> None: @@ -138,7 +139,7 @@ def print_error(msg: str) -> None: def safe_exit(): if is_run_by_clicking(): - print_usage_box("Press any key to quit.") + print_usage_box(t("press_any_key")) # KeyboardInterrupt will mess with stdin, input will crash silently # Use msvcrt.getch() because it doesn't depends on stdin msvcrt.getch() From 0ae2a8759ab4d695e18361938a174999d987d219 Mon Sep 17 00:00:00 2001 From: Zuckerig Date: Tue, 17 Feb 2026 11:03:35 +0500 Subject: [PATCH 2/3] Editing based on comments --- main.py | 140 ++++++++--------- pyproject.toml | 2 +- requirements.txt | 2 +- rf4s/app/app.py | 102 ++++++------- rf4s/controller/player.py | 30 ++-- rf4s/i18n/en.yml | 312 +++++++++++++++++++------------------- rf4s/i18n/ru.yml | 312 +++++++++++++++++++------------------- rf4s/i18n/zh-TW.yml | 312 +++++++++++++++++++------------------- rf4s/result/result.py | 73 ++++----- rf4s/utils.py | 6 +- 10 files changed, 631 insertions(+), 660 deletions(-) diff --git a/main.py b/main.py index 82669a8..ed8aae9 100644 --- a/main.py +++ b/main.py @@ -8,18 +8,12 @@ """ import argparse -import io import logging import logging.config import shlex import sys from pathlib import Path -# Force UTF-8 for stdout/stderr on Windows to support all i18n languages (zh-TW, ru, etc.) -if sys.platform == "win32": - sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") - sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace") - import rich.logging # noqa: F401 import rich_argparse from rich import box, print @@ -50,44 +44,44 @@ # https://patorjk.com/software/taag/#p=testall&f=3D-ASCII&t=RF4S%0A, ANSI Shadow FEATURES = ( - {"name_key": "feature_bot", "command": "bot"}, - {"name_key": "feature_craft", "command": "craft"}, - {"name_key": "feature_move", "command": "move"}, - {"name_key": "feature_harvest", "command": "harvest"}, - {"name_key": "feature_frictionbrake", "command": "frictionbrake"}, - {"name_key": "feature_calculate", "command": "calculate"}, + {"name_key": "feature.bot", "command": "bot"}, + {"name_key": "feature.craft", "command": "craft"}, + {"name_key": "feature.move", "command": "move"}, + {"name_key": "feature.harvest", "command": "harvest"}, + {"name_key": "feature.frictionbrake", "command": "frictionbrake"}, + {"name_key": "feature.calculate", "command": "calculate"}, ) BOT_BOOLEAN_ARGUMENTS = ( - ("t", "tag", "help_tag"), - ("c", "coffee", "help_coffee"), - ("a", "alcohol", "help_alcohol"), - ("r", "refill", "help_refill"), - ("H", "harvest", "help_harvest_arg"), - ("L", "lure", "help_lure"), - ("m", "mouse", "help_mouse"), - ("P", "pause", "help_pause"), - ("RC", "random-cast", "help_random_cast"), - ("SC", "skip-cast", "help_skip_cast"), - ("l", "lift", "help_lift"), - ("e", "electro", "help_electro"), - ("FB", "friction-brake", "help_friction_brake"), - ("GR", "gear-ratio", "help_gear_ratio"), - ("b", "bite", "help_bite"), - ("s", "screenshot", "help_screenshot"), - ("d", "data", "help_data"), - ("E", "email", "help_email"), - ("M", "miaotixing", "help_miaotixing"), - ("D", "discord", "help_discord"), - ("TG", "telegram", "help_telegram"), - ("S", "shutdown", "help_shutdown"), - ("SO", "signout", "help_signout"), - ("BL", "broken-lure", "help_broken_lure"), - ("SR", "spod-rod", "help_spod_rod"), - ("DM", "dry-mix", "help_dry_mix"), - ("GB", "groundbait", "help_groundbait"), - ("PVA", "pva", "help_pva"), - ("NA", "no-animation", "help_no_animation"), + ("t", "tag", "help.tag"), + ("c", "coffee", "help.coffee"), + ("a", "alcohol", "help.alcohol"), + ("r", "refill", "help.refill"), + ("H", "harvest", "help.harvest_arg"), + ("L", "lure", "help.lure"), + ("m", "mouse", "help.mouse"), + ("P", "pause", "help.pause"), + ("RC", "random-cast", "help.random_cast"), + ("SC", "skip-cast", "help.skip_cast"), + ("l", "lift", "help.lift"), + ("e", "electro", "help.electro"), + ("FB", "friction-brake", "help.friction_brake"), + ("GR", "gear-ratio", "help.gear_ratio"), + ("b", "bite", "help.bite"), + ("s", "screenshot", "help.screenshot"), + ("d", "data", "help.data"), + ("E", "email", "help.email"), + ("M", "miaotixing", "help.miaotixing"), + ("D", "discord", "help.discord"), + ("TG", "telegram", "help.telegram"), + ("S", "shutdown", "help.shutdown"), + ("SO", "signout", "help.signout"), + ("BL", "broken-lure", "help.broken_lure"), + ("SR", "spod-rod", "help.spod_rod"), + ("DM", "dry-mix", "help.dry_mix"), + ("GB", "groundbait", "help.groundbait"), + ("PVA", "pva", "help.pva"), + ("NA", "no-animation", "help.no_animation"), ) EPILOG = """ @@ -161,18 +155,18 @@ def setup_parser(cfg: CN) -> tuple[argparse.ArgumentParser, tuple]: :rtype: ArgumentParser """ parent_parser = argparse.ArgumentParser(add_help=False) - parent_parser.add_argument("opts", nargs="*", help=t("help_opts")) + parent_parser.add_argument("opts", nargs="*", help=t("help.opts")) main_parser = argparse.ArgumentParser(epilog=EPILOG, formatter_class=Formatter) main_parser.add_argument( "-V", "--version", action="version", version=f"RF4S {VERSION}" ) - feature_parsers = main_parser.add_subparsers(title=t("features"), dest="feature") + feature_parsers = main_parser.add_subparsers(title=t("main.features"), dest="feature") bot_parser = feature_parsers.add_parser( "bot", - help=t("help_bot"), + help=t("help.bot"), parents=[parent_parser], formatter_class=Formatter, ) @@ -196,7 +190,7 @@ def pid(_pid: str) -> int: "--pid", type=pid, choices=range(len(cfg.PROFILE)), - help=t("help_pid"), + help=t("help.pid"), metavar=f"{{0-{len(cfg.PROFILE) - 1}}}", ) @@ -209,7 +203,7 @@ def pname(_pname: str) -> str: "-N", "--pname", type=pname, - help=t("help_pname"), + help=t("help.pname"), metavar="{profile name}", ) @@ -223,7 +217,7 @@ def num_fish(_num_fish: str) -> int: # const=0, # Flag is used but no argument given type=num_fish, choices=range(cfg.BOT.KEEPNET.CAPACITY), - help=t("help_fishes_in_keepnet"), + help=t("help.fishes_in_keepnet"), metavar=f"{{0-{cfg.BOT.KEEPNET.CAPACITY - 1}}}", ) bot_parser.add_argument( @@ -234,7 +228,7 @@ def num_fish(_num_fish: str) -> int: default=None, type=str, choices=["forward", "left", "right"], - help=t("help_trolling"), + help=t("help.trolling"), ) bot_parser.add_argument( "-R", @@ -244,7 +238,7 @@ def num_fish(_num_fish: str) -> int: default=None, type=int, choices=[0, 5], - help=t("help_rainbow"), + help=t("help.rainbow"), ) bot_parser.add_argument( @@ -255,11 +249,11 @@ def num_fish(_num_fish: str) -> int: default=0, type=int, choices=[0, 1, 2, 3, 5], - help=t("help_boat_ticket"), + help=t("help.boat_ticket"), ) craft_parser = feature_parsers.add_parser( - "craft", help=t("help_craft"), parents=[parent_parser], formatter_class=Formatter + "craft", help=t("help.craft"), parents=[parent_parser], formatter_class=Formatter ) craft_parser.add_argument( "-V", "--version", action="version", version=f"RF4S-craft {VERSION}" @@ -268,26 +262,26 @@ def num_fish(_num_fish: str) -> int: "-d", "--discard", action="store_true", - help=t("help_discard"), + help=t("help.discard"), ) craft_parser.add_argument( "-i", "--ignore", action="store_true", - help=t("help_ignore"), + help=t("help.ignore"), ) craft_parser.add_argument( "-n", "--craft-limit", type=int, default=-1, - help=t("help_craft_limit"), + help=t("help.craft_limit"), metavar="{number of items}", ) move_parser = feature_parsers.add_parser( "move", - help=t("help_move"), + help=t("help.move"), parents=[parent_parser], formatter_class=Formatter, ) @@ -298,12 +292,12 @@ def num_fish(_num_fish: str) -> int: "-s", "--shift", action="store_true", - help=t("help_shift"), + help=t("help.shift"), ) harvest_parser = feature_parsers.add_parser( "harvest", - help=t("help_harvest"), + help=t("help.harvest"), parents=[parent_parser], formatter_class=Formatter, ) @@ -314,12 +308,12 @@ def num_fish(_num_fish: str) -> int: "-r", "--refill", action="store_true", - help=t("help_refill_harvest"), + help=t("help.refill_harvest"), ) friction_brake_parser = feature_parsers.add_parser( "frictionbrake", - help=t("help_frictionbrake"), + help=t("help.frictionbrake"), aliases=["fb"], parents=[parent_parser], formatter_class=Formatter, @@ -330,7 +324,7 @@ def num_fish(_num_fish: str) -> int: calculate_paser = feature_parsers.add_parser( "calculate", - help=t("help_calculate"), + help=t("help.calculate"), aliases=["cal"], parents=[parent_parser], formatter_class=Formatter, @@ -355,8 +349,8 @@ def display_features() -> None: Shows a formatted table with feature IDs and names. """ table = Table( - t("features"), - title=t("select_feature"), + t("main.features"), + title=t("main.select_feature"), show_header=False, box=box.HEAVY, min_width=36, @@ -373,28 +367,28 @@ def get_fid(parser: argparse.ArgumentParser) -> int: Continuously prompts until a valid feature ID is entered or the user chooses to quit. """ - utils.print_usage_box(t("enter_fid")) + utils.print_usage_box(t("main.enter_fid")) while True: user_input = input(">>> ") if user_input.isdigit() and 0 <= int(user_input) < len(FEATURES): break if user_input == "q": - print(t("bye")) + print(t("common.bye")) sys.exit() if user_input == "h": parser.print_help() continue - utils.print_error(t("invalid_input")) + utils.print_error(t("common.invalid_input")) return int(user_input) def get_launch_options(parser: argparse.ArgumentParser) -> str: - utils.print_usage_box(t("enter_launch_options")) + utils.print_usage_box(t("main.enter_launch_options")) while True: user_input = input(">>> ") if user_input == "q": - print(t("bye")) + print(t("common.bye")) sys.exit() if user_input == "h": parser.print_help() @@ -404,28 +398,28 @@ def get_launch_options(parser: argparse.ArgumentParser) -> str: def get_language(): - utils.print_usage_box(t("game_language_prompt")) + utils.print_usage_box(t("main.game_language_prompt")) while True: user_input = input(">>> ") if user_input.isdigit() and user_input in ("1", "2"): break if user_input == "q": - print(t("bye")) + print(t("common.bye")) sys.exit() - utils.print_error(t("invalid_input")) + utils.print_error(t("common.invalid_input")) return '"en"' if user_input == "1" else '"ru"' def get_click_lock(): - utils.print_usage_box(t("click_lock_prompt")) + utils.print_usage_box(t("main.click_lock_prompt")) while True: user_input = input(">>> ") if user_input.isdigit() and user_input in ("1", "2"): break if user_input == "q": - print(t("bye")) + print(t("common.bye")) sys.exit() - utils.print_error(t("invalid_input")) + utils.print_error(t("common.invalid_input")) return "true" if user_input == "1" else "false" diff --git a/pyproject.toml b/pyproject.toml index 30a2103..8b2f9b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ "pynput==1.7.6", "pyscreeze==0.1.29", "python-dotenv==1.0.1", - "python-i18n[YAML]>=0.3.9", + "python-i18n[YAML]==0.3.9", "pywin32==306", "pyyaml==6.0.2", "rich==13.9.4", diff --git a/requirements.txt b/requirements.txt index 8273675..e5568a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,7 +45,7 @@ pyscreenshot==3.1 pyscreeze==0.1.29 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-i18n[YAML]>=0.3.9 +python-i18n[YAML]==0.3.9 pytweening==1.2.0 pywin32==306 pyyaml==6.0.2 diff --git a/rf4s/app/app.py b/rf4s/app/app.py index 0ed2eea..6869c1e 100644 --- a/rf4s/app/app.py +++ b/rf4s/app/app.py @@ -83,7 +83,7 @@ def display_result(self) -> None: if not result_dict: return - result = Table(title=t("running_result"), box=box.HEAVY, show_header=False) + result = Table(title=t("ui.running_result"), box=box.HEAVY, show_header=False) for name, value in self.result.as_dict().items(): result.add_row(name, str(value)) print(result) @@ -137,9 +137,9 @@ def validate_cfg(self): def display_info(self): settings = Table( - title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 + title=t("ui.settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) + settings.add_row(t("ui.launch_options_final"), " ".join(sys.argv[1:])) for k, v in self.cfg.PROFILE.items(): if k != "DESCRIPTION": settings.add_row(k, str(v)) @@ -147,7 +147,7 @@ def display_info(self): if self.cfg.PROFILE.DESCRIPTION: utils.print_description_box(self.cfg.PROFILE.DESCRIPTION) utils.print_usage_box( - t("press_pause_quit", pause=self.cfg.KEY.PAUSE, quit=self.cfg.KEY.QUIT) + t("ui.press_pause_quit", pause=self.cfg.KEY.PAUSE, quit=self.cfg.KEY.QUIT) ) def validate_smtp(self) -> None: @@ -254,7 +254,7 @@ def display_profiles(self) -> None: Shows a formatted table with profile IDs and names. """ profiles = Table( - title=t("select_profile"), + title=t("ui.select_profile"), box=box.HEAVY, show_header=False, min_width=36, @@ -269,16 +269,16 @@ def get_pid(self) -> None: Continuously prompts until a valid profile ID is entered or the user chooses to quit. """ - utils.print_usage_box(t("enter_pid")) + utils.print_usage_box(t("ui.enter_pid")) while True: user_input = input(">>> ") if user_input.isdigit() and 0 <= int(user_input) < len(self.cfg.PROFILE): break if user_input == "q": - print(t("bye")) + print(t("common.bye")) sys.exit() - utils.print_error(t("invalid_pid")) + utils.print_error(t("ui.invalid_pid")) self.args.pid = int(user_input) @@ -449,9 +449,9 @@ def start(self) -> None: break utils.print_usage_box( - t("press_restart", pause=self.cfg.KEY.PAUSE) + t("ui.press_restart", pause=self.cfg.KEY.PAUSE) ) - utils.print_hint_box(t("hint_launch_ignored")) + utils.print_hint_box(t("ui.hint_launch_ignored")) with self.player.hold_keys(mouse=False, shift=False, reset=True): pause_listener = keyboard.Listener(on_release=self._pause_wait) pause_listener.start() @@ -469,7 +469,7 @@ def start(self) -> None: ) self.paused = False - self.player.handle_termination("Terminated by user", shutdown=False, send=False) + self.player.handle_termination("stop.terminated", shutdown=False, send=False) class CraftApp(App): @@ -488,9 +488,9 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() settings = Table( - title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 + title=t("ui.settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) + settings.add_row(t("ui.launch_options_final"), " ".join(sys.argv[1:])) print(settings) self.result = CraftResult() @@ -550,7 +550,7 @@ def start(self) -> None: listener.start() try: - utils.print_usage_box(t("press_quit", quit=self.cfg.KEY.QUIT)) + utils.print_usage_box(t("ui.press_quit", quit=self.cfg.KEY.QUIT)) logger.warning("This might get you banned, use at your own risk") logger.warning("Use Razor or Logitech macros instead") random.seed(datetime.now().timestamp()) @@ -605,7 +605,7 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() utils.print_usage_box( - t("press_move", pause=self.cfg.KEY.MOVE_PAUSE, quit=self.cfg.KEY.MOVE_QUIT) + t("ui.press_move", pause=self.cfg.KEY.MOVE_PAUSE, quit=self.cfg.KEY.MOVE_QUIT) ) self.result = Result() @@ -664,16 +664,16 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() settings = Table( - title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 + title=t("ui.settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) - settings.add_row(t("power_saving"), str(self.cfg.HARVEST.POWER_SAVING)) - settings.add_row(t("check_delay"), str(self.cfg.HARVEST.CHECK_DELAY)) - settings.add_row(t("energy_threshold"), str(self.cfg.STAT.ENERGY_THRESHOLD)) - settings.add_row(t("hunger_threshold"), str(self.cfg.STAT.HUNGER_THRESHOLD)) - settings.add_row(t("comfort_threshold"), str(self.cfg.STAT.COMFORT_THRESHOLD)) + settings.add_row(t("ui.launch_options_final"), " ".join(sys.argv[1:])) + settings.add_row(t("harvest.power_saving"), str(self.cfg.HARVEST.POWER_SAVING)) + settings.add_row(t("harvest.check_delay"), str(self.cfg.HARVEST.CHECK_DELAY)) + settings.add_row(t("harvest.energy_threshold"), str(self.cfg.STAT.ENERGY_THRESHOLD)) + settings.add_row(t("harvest.hunger_threshold"), str(self.cfg.STAT.HUNGER_THRESHOLD)) + settings.add_row(t("harvest.comfort_threshold"), str(self.cfg.STAT.COMFORT_THRESHOLD)) print(settings) - utils.print_usage_box(t("press_quit", quit=self.cfg.KEY.QUIT)) + utils.print_usage_box(t("ui.press_quit", quit=self.cfg.KEY.QUIT)) self.result = HarvestResult() self.timer = Timer(self.cfg) @@ -809,12 +809,12 @@ def __init__(self, cfg, args, parser): _ = cfg, args, parser self.result = None self.parts = [ - Part(key="rod", name_key="part_rod", prompt_key="prompt_load_capacity", color="orange1", base=0.3), - Part(key="reel_mechanism", name_key="part_reel_mechanism", prompt_key="prompt_mech", color="plum1", base=0.3), - Part(key="reel_friction_brake", name_key="part_reel_fb", prompt_key="prompt_drag", color="gold1"), - Part(key="fishing_line", name_key="part_fishing_line", prompt_key="prompt_load_capacity", color="salmon1"), - Part(key="leader", name_key="part_leader", prompt_key="prompt_load_capacity", color="pale_green1"), - Part(key="hook", name_key="part_hook", prompt_key="prompt_load_capacity", color="sky_blue1"), + Part(key="rod", name_key="part.rod", prompt_key="prompt.load_capacity", color="orange1", base=0.3), + Part(key="reel_mechanism", name_key="part.reel_mechanism", prompt_key="prompt.mech", color="plum1", base=0.3), + Part(key="reel_friction_brake", name_key="part.reel_fb", prompt_key="prompt.drag", color="gold1"), + Part(key="fishing_line", name_key="part.fishing_line", prompt_key="prompt.load_capacity", color="salmon1"), + Part(key="leader", name_key="part.leader", prompt_key="prompt.load_capacity", color="pale_green1"), + Part(key="hook", name_key="part.hook", prompt_key="prompt.load_capacity", color="sky_blue1"), ] self.friction_brake = next( part for part in self.parts if part.key == "reel_friction_brake" @@ -828,9 +828,9 @@ def calculate_tackle_stats(self): if part.pre_real_load_capacity is not None: raise exceptions.PreviousError else: - utils.print_error(t("value_not_found", name=part.name)) + utils.print_error(t("calculate.value_not_found", name=part.name)) part.load_capacity = self.get_validated_input(part, part.prompt) - part.wear = self.get_validated_input(part, t("prompt_wear"), is_wear=True) + part.wear = self.get_validated_input(part, t("prompt.wear"), is_wear=True) except exceptions.SkipError: if part.real_load_capacity is not None: part.real_load_capacity = None @@ -854,12 +854,12 @@ def get_validated_input(self, part: Part, prompt: str, is_wear: bool = False) -> raise exceptions.RestartError case CalculateCommand.PREVIOUS.value: if part.pre_real_load_capacity is None: - utils.print_error(t("value_not_found", name=part.name)) + utils.print_error(t("calculate.value_not_found", name=part.name)) continue raise exceptions.PreviousError case CalculateCommand.PREVIOUS_REMAINING.value: if part.pre_real_load_capacity is None: - utils.print_error(t("value_not_found", name=part.name)) + utils.print_error(t("calculate.value_not_found", name=part.name)) continue raise exceptions.PreviousRemainingError case CalculateCommand.SKIP.value: @@ -873,22 +873,22 @@ def get_validated_input(self, part: Part, prompt: str, is_wear: bool = False) -> number = float(user_input) if is_wear: if not (0 <= number <= 100): - utils.print_error(t("wear_range")) + utils.print_error(t("calculate.wear_range")) continue elif number < 0: - utils.print_error(t("non_negative")) + utils.print_error(t("calculate.non_negative")) continue return number except ValueError: - utils.print_error(t("invalid_input_retry")) + utils.print_error(t("calculate.invalid_input_retry")) def reset_stats(self) -> None: for part in self.parts: part.pre_real_load_capacity = part.real_load_capacity part.real_load_capacity = None self.result = Table( - t("results"), - title=t("tackle_stats"), + t("calculate.results"), + title=t("calculate.tackle_stats"), show_header=False, box=box.HEAVY, min_width=36, @@ -899,7 +899,7 @@ def update_result(self) -> None: if not valid_parts: return weakest_part = min(valid_parts, key=lambda x: x.real_load_capacity) - self.result.add_row(t("weakest_part"), weakest_part.name) + self.result.add_row(t("calculate.weakest_part"), weakest_part.name) if self.friction_brake.real_load_capacity is None: return @@ -914,7 +914,7 @@ def update_result(self) -> None: ), ) self.result.add_row( - t("recommend_fb"), + t("calculate.recommend_fb"), f"{int(recommend_friction_brake):2d}", ) except ZeroDivisionError: @@ -925,8 +925,8 @@ def start(self): Prompts the user for input, calculates the result, and displays them in a table. """ - utils.print_usage_box(t("commands_help")) - utils.print_hint_box(t("hint_view_parts")) + utils.print_usage_box(t("calculate.commands_help")) + utils.print_hint_box(t("calculate.hint_view_parts")) while True: self.reset_stats() @@ -937,7 +937,7 @@ def start(self): except exceptions.RestartError: continue except exceptions.QuitError: - print(t("bye")) + print(t("common.bye")) break if self.result.rows: self.update_result() @@ -970,18 +970,18 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() settings = Table( - title=t("settings"), show_header=False, box=box.HEAVY, min_width=36 + title=t("ui.settings"), show_header=False, box=box.HEAVY, min_width=36 ) - settings.add_row(t("launch_options_final"), " ".join(sys.argv[1:])) - settings.add_row(t("initial_fb"), str(self.cfg.FRICTION_BRAKE.INITIAL)) - settings.add_row(t("max_fb"), str(self.cfg.FRICTION_BRAKE.MAX)) - settings.add_row(t("start_delay"), str(self.cfg.FRICTION_BRAKE.START_DELAY)) - settings.add_row(t("increase_delay"), str(self.cfg.FRICTION_BRAKE.INCREASE_DELAY)) - settings.add_row(t("sensitivity"), self.cfg.FRICTION_BRAKE.SENSITIVITY) + settings.add_row(t("ui.launch_options_final"), " ".join(sys.argv[1:])) + settings.add_row(t("friction_brake.initial_fb"), str(self.cfg.FRICTION_BRAKE.INITIAL)) + settings.add_row(t("friction_brake.max_fb"), str(self.cfg.FRICTION_BRAKE.MAX)) + settings.add_row(t("friction_brake.start_delay"), str(self.cfg.FRICTION_BRAKE.START_DELAY)) + settings.add_row(t("friction_brake.increase_delay"), str(self.cfg.FRICTION_BRAKE.INCREASE_DELAY)) + settings.add_row(t("friction_brake.sensitivity"), self.cfg.FRICTION_BRAKE.SENSITIVITY) print(settings) utils.print_usage_box( - t("press_fb", reset=self.cfg.KEY.FRICTION_BRAKE_RESET, quit=self.cfg.KEY.FRICTION_BRAKE_QUIT) + t("ui.press_fb", reset=self.cfg.KEY.FRICTION_BRAKE_RESET, quit=self.cfg.KEY.FRICTION_BRAKE_QUIT) ) self.window = Window() diff --git a/rf4s/controller/player.py b/rf4s/controller/player.py index 05830e8..9a52894 100644 --- a/rf4s/controller/player.py +++ b/rf4s/controller/player.py @@ -203,13 +203,13 @@ def loop_restart_handler(self): if self.cfg.ARGS.FRICTION_BRAKE: with self.friction_brake.lock: self.friction_brake.change(increase=False) - self.general_quit("Fishing line is at its end") + self.general_quit("stop.line_at_end") except exceptions.LineSnaggedError: self._handle_snagged_line() except exceptions.DisconnectedError: self.disconnected_quit() except exceptions.TackleBrokenError: - self.general_quit("Tackle is broken") + self.general_quit("stop.tackle_broken") except exceptions.BaitNotChosenError: self.handle_bait_not_chosen() except exceptions.DryMixNotFoundError: @@ -420,7 +420,7 @@ def _drink_coffee(self) -> None: mouse = True if self.mouse_pressed and self.cfg.KEY["COFFEE"] != -1 else False with self.hold_keys(mouse=mouse, shift=False, reset=True): if self.cur_coffee > self.cfg.STAT.COFFEE_LIMIT: - self.general_quit("Coffee limit reached") + self.general_quit("stop.coffee_limit") logger.info("Drinking coffee") for _ in range(self.cfg.STAT.COFFEE_PER_DRINK): @@ -505,7 +505,7 @@ def error_handler(self): def handle_bait_not_chosen(self) -> None: if len(self.tackles) == 1: - self.general_quit("Run out of bait") + self.general_quit("stop.no_bait") self.tackle.available = False def cast_spod_rod(self) -> None: @@ -672,7 +672,7 @@ def _update_tackle(self) -> None: """Update the current tackle (rod) being used.""" candidates = self._get_available_rods() if not candidates: - self.general_quit("All rods are unavailable") + self.general_quit("stop.no_rods") if self.cfg.PROFILE.RANDOM_ROD_SELECTION: self.tackle_idx = random.choice(candidates) else: @@ -791,7 +791,7 @@ def _pause_script(self) -> None: def _handle_timeout(self) -> None: """Handle common timeout events.""" if self.detection.is_tackle_broken(): - self.general_quit("Tackle is broken") + self.general_quit("stop.tackle_broken") if self.detection.is_disconnected(): self.disconnected_quit() @@ -805,7 +805,7 @@ def _handle_broken_lure(self) -> None: with self.hold_keys(mouse=False, shift=False): self._replace_broken_lures() else: - self.general_quit("Lure is broken") + self.general_quit("stop.lure_broken") def handle_termination(self, msg: str, shutdown: bool, send: bool) -> None: """Handle script termination. @@ -840,7 +840,7 @@ def handle_termination(self, msg: str, shutdown: bool, send: bool) -> None: def _handle_snagged_line(self) -> None: """Handle a snagged line event.""" if len(self.tackles) == 1: - self.general_quit("Line is snagged") + self.general_quit("stop.line_snagged") self.tackle.available = False def handle_fish(self) -> None: @@ -860,7 +860,7 @@ def handle_fish(self) -> None: self.timer.add_cast_time() limit = self.cfg.BOT.KEEPNET.CAPACITY - self.cfg.ARGS.FISHES_IN_KEEPNET if self.result.kept == limit: - self.general_quit("Keepnet is full") + self.general_quit("stop.keepnet_full") def _handle_fish(self) -> None: """Keep or release the fish and record the fish count.""" @@ -909,7 +909,7 @@ def _handle_fish(self) -> None: pag.press("esc") pag.press("backspace") sleep(ANIMATION_DELAY) - self.general_quit("Keepnet is full") + self.general_quit("stop.keepnet_full") for tag_color in tag_colors: setattr(self.result, tag_color, getattr(self.result, tag_color) + 1) @@ -980,7 +980,7 @@ def disconnected_quit(self) -> None: pag.moveTo(self.detection.get_confirm_button_position()) pag.click() - self.handle_termination("Game disconnected", shutdown=True, send=True) + self.handle_termination("stop.disconnected", shutdown=True, send=True) def get_result_dict(self, msg: str): return self.result.as_dict(msg, self.timer) @@ -991,7 +991,7 @@ def get_result_table(self, result) -> Table: :return: formatted running result table :rtype: Table """ - table = Table(title=t("running_result"), box=box.HEAVY, show_header=False) + table = Table(title=t("ui.running_result"), box=box.HEAVY, show_header=False) for k, v in result.items(): table.add_row(k, str(v)) @@ -1003,14 +1003,14 @@ def _handle_expired_ticket(self) -> None: if self.cfg.ARGS.BOAT_TICKET == 0: pag.press("esc") sleep(TICKET_EXPIRE_DELAY) - self.general_quit("Boat ticket expired") + self.general_quit("stop.ticket_expired") logger.info("Renewing boat ticket") ticket_loc = self.detection.get_ticket_position(self.cfg.ARGS.BOAT_TICKET) if ticket_loc is None: pag.press("esc") # Close ticket menu sleep(TICKET_EXPIRE_DELAY) - self.general_quit("New boat ticket not found") + self.general_quit("stop.no_ticket") pag.moveTo(ticket_loc) pag.click(clicks=2, interval=0.1) # pag.doubleClick() not implemented self.result.ticket += 1 @@ -1070,7 +1070,7 @@ def _replace_item(self) -> None: pag.press("esc") sleep(ANIMATION_DELAY) pag.press("esc") - self.general_quit("Favorite item not found") + self.general_quit("stop.no_favorite") # Check if the lure for replacement is already broken x, y = utils.get_box_center_integers(favorite_item_position) diff --git a/rf4s/i18n/en.yml b/rf4s/i18n/en.yml index 063a1b7..a7b69f8 100644 --- a/rf4s/i18n/en.yml +++ b/rf4s/i18n/en.yml @@ -1,169 +1,167 @@ en: - # Common - bye: "Bye." - invalid_input: "Invalid input, please try again." - press_any_key: "Press any key to quit." - using: "You're now using: %{msg}" - hint: "Hint: %{msg}" + common: + bye: "Bye." + invalid_input: "Invalid input, please try again." + press_any_key: "Press any key to quit." + using: "You're now using: %{msg}" + hint: "Hint: %{msg}" - # main.py - feature selection - select_feature: "Select a feature to start 🚀" - features: "Features" - enter_fid: "Enter feature id to use, h to see help message, q to quit." - enter_launch_options: "Enter launch options, Enter to skip, h to see help message, q to quit." - game_language_prompt: "What's your game language? [(1) en (2) ru (3) q (quit)]" - click_lock_prompt: "Is Windows Mouse ClickLock enabled? [(1) yes (2) no (3) q (quit)]" + main: + select_feature: "Select a feature to start 🚀" + features: "Features" + enter_fid: "Enter feature id to use, h to see help message, q to quit." + enter_launch_options: "Enter launch options, Enter to skip, h to see help message, q to quit." + game_language_prompt: "What's your game language? [(1) en (2) ru (3) q (quit)]" + click_lock_prompt: "Is Windows Mouse ClickLock enabled? [(1) yes (2) no (3) q (quit)]" - # Feature names - feature_bot: "Fishing Bot" - feature_craft: "Craft Items" - feature_move: "Move Forward" - feature_harvest: "Harvest Baits" - feature_frictionbrake: "Auto Friction Brake" - feature_calculate: "Calculate Tackle's Stats" + feature: + bot: "Fishing Bot" + craft: "Craft Items" + move: "Move Forward" + harvest: "Harvest Baits" + frictionbrake: "Auto Friction Brake" + calculate: "Calculate Tackle's Stats" - # Argparse help (subcommands) - help_bot: "start fishing bot" - help_craft: "craft items" - help_move: "toggle moving forward" - help_harvest: "harvest baits" - help_frictionbrake: "automate friction brake" - help_calculate: "calculate tackle's stats" + help: + bot: "start fishing bot" + craft: "craft items" + move: "toggle moving forward" + harvest: "harvest baits" + frictionbrake: "automate friction brake" + calculate: "calculate tackle's stats" + tag: "keep only tagged fishes" + coffee: "drink coffee if stamina is low during fish fight" + alcohol: "drink alcohol before keeping the fish" + refill: "consume tea and carrot if hunger or comfort is low" + harvest_arg: "harvest baits before casting the rod" + lure: "change current lure with a random favorite one, mode: spin" + mouse: "move mouse randomly before casting the rod" + pause: "pause the script before casting the rod occasionally" + random_cast: "do a redundant rod cast randomly" + skip_cast: "skip the first rod cast" + lift: "lift the tackle constantly during a fish fight" + electro: "enable electric mode for Electro Raptor series reel" + friction_brake: "adjust friction brake automatically" + gear_ratio: "switch the gear ratio or mode after the retrieval timed out" + bite: "save a screenshot in screenshots/ when a fish bite" + screenshot: "save a screenshot in screenshots/ after you caught a fish" + data: "save fishing data in /logs" + email: "send email noticication after the script stop" + miaotixing: "send miaotixing notification after the script stop" + discord: "send Discord notification after the script stop" + telegram: "send Telegram notification after the script stop" + shutdown: "shutdown computer after the script stop" + signout: "sign out instead of closing the game" + broken_lure: "replace broken lures with favorite ones" + spod_rod: "recast spod rod" + dry_mix: "enable dry mix refill, mode: bottom" + groundbait: "enable groundbait refill, mode: bottom" + pva: "enable pva refill, mode: bottom" + no_animation: "disable waiting for trophy and gift animations, gift\nchange 'Catch screen style' to 'Simple' in game settings to use this flag" + opts: "overwrite configuration" + pid: "specify the id of the profile to use" + pname: "specify the name of the profile to use" + fishes_in_keepnet: "specify the number of fishes in your keepnet, (default: %(default)s)" + trolling: "enable trolling mode and specify the direction\n(default: %(default)s, no argument: %(const)s)" + rainbow: "enable rainbow line mode and specify the meter to lift the rod\n(default: %(default)s, no argument: %(const)s)" + boat_ticket: "enable boat ticket renewal and specify the duration\n(default: %(default)s, no argument: %(const)s)" + discard: "discard all the crafted items (for groundbaits)" + ignore: "ignore unselected material slots" + craft_limit: "specify the number of items to craft, (default: %(default)s)" + shift: "Hold down the Shift key while moving" + refill_harvest: "refill hunger and comfort by consuming tea and carrot" - # Argparse help (bot boolean arguments) - help_tag: "keep only tagged fishes" - help_coffee: "drink coffee if stamina is low during fish fight" - help_alcohol: "drink alcohol before keeping the fish" - help_refill: "consume tea and carrot if hunger or comfort is low" - help_harvest_arg: "harvest baits before casting the rod" - help_lure: "change current lure with a random favorite one, mode: spin" - help_mouse: "move mouse randomly before casting the rod" - help_pause: "pause the script before casting the rod occasionally" - help_random_cast: "do a redundant rod cast randomly" - help_skip_cast: "skip the first rod cast" - help_lift: "lift the tackle constantly during a fish fight" - help_electro: "enable electric mode for Electro Raptor series reel" - help_friction_brake: "adjust friction brake automatically" - help_gear_ratio: "switch the gear ratio or mode after the retrieval timed out" - help_bite: "save a screenshot in screenshots/ when a fish bite" - help_screenshot: "save a screenshot in screenshots/ after you caught a fish" - help_data: "save fishing data in /logs" - help_email: "send email noticication after the script stop" - help_miaotixing: "send miaotixing notification after the script stop" - help_discord: "send Discord notification after the script stop" - help_telegram: "send Telegram notification after the script stop" - help_shutdown: "shutdown computer after the script stop" - help_signout: "sign out instead of closing the game" - help_broken_lure: "replace broken lures with favorite ones" - help_spod_rod: "recast spod rod" - help_dry_mix: "enable dry mix refill, mode: bottom" - help_groundbait: "enable groundbait refill, mode: bottom" - help_pva: "enable pva refill, mode: bottom" - help_no_animation: "disable waiting for trophy and gift animations, gift\nchange 'Catch screen style' to 'Simple' in game settings to use this flag" + ui: + settings: "Settings" + launch_options_final: "LAUNCH OPTIONS (FINAL)" + running_result: "Running Result" + select_profile: "Select a profile to start ⚙️" + enter_pid: "Enter profile id to use, q to quit." + invalid_pid: "Invalid profile id, please try again." + press_pause_quit: "Press %{pause} to pause, %{quit} to quit." + press_quit: "Press %{quit} to quit." + press_move: "Press %{pause} to pause, %{quit} to quit." + press_restart: "Press %{pause} to reload config and restart." + hint_launch_ignored: "Any modifications made to LAUNCH_OPTIONS will be ignored." + press_fb: "Press %{reset} to reset friction brake, %{quit} to quit." - # Argparse help (other arguments) - help_opts: "overwrite configuration" - help_pid: "specify the id of the profile to use" - help_pname: "specify the name of the profile to use" - help_fishes_in_keepnet: "specify the number of fishes in your keepnet, (default: %(default)s)" - help_trolling: "enable trolling mode and specify the direction\n(default: %(default)s, no argument: %(const)s)" - help_rainbow: "enable rainbow line mode and specify the meter to lift the rod\n(default: %(default)s, no argument: %(const)s)" - help_boat_ticket: "enable boat ticket renewal and specify the duration\n(default: %(default)s, no argument: %(const)s)" - help_discard: "discard all the crafted items (for groundbaits)" - help_ignore: "ignore unselected material slots" - help_craft_limit: "specify the number of items to craft, (default: %(default)s)" - help_shift: "Hold down the Shift key while moving" - help_refill_harvest: "refill hunger and comfort by consuming tea and carrot" + harvest: + power_saving: "Power saving" + check_delay: "Check delay" + energy_threshold: "Energy threshold" + hunger_threshold: "Hunger threshold" + comfort_threshold: "Comfort threshold" - # app.py - common UI - settings: "Settings" - launch_options_final: "LAUNCH OPTIONS (FINAL)" - running_result: "Running Result" - select_profile: "Select a profile to start ⚙️" - enter_pid: "Enter profile id to use, q to quit." - invalid_pid: "Invalid profile id, please try again." - press_pause_quit: "Press %{pause} to pause, %{quit} to quit." - press_quit: "Press %{quit} to quit." - press_move: "Press %{pause} to pause, %{quit} to quit." - press_restart: "Press %{pause} to reload config and restart." - hint_launch_ignored: "Any modifications made to LAUNCH_OPTIONS will be ignored." - press_fb: "Press %{reset} to reset friction brake, %{quit} to quit." + friction_brake: + initial_fb: "Initial friction brake" + max_fb: "Max friction brake" + start_delay: "Start delay" + increase_delay: "Increase delay" + sensitivity: "Sensitivity" - # Harvest settings - power_saving: "Power saving" - check_delay: "Check delay" - energy_threshold: "Energy threshold" - hunger_threshold: "Hunger threshold" - comfort_threshold: "Comfort threshold" + calculate: + tackle_stats: "Tackle's Stats" + results: "Results" + weakest_part: "Weakest part" + recommend_fb: "Recommend friction brake" + commands_help: "Commands:\nr: Restart\ns: Skip a part\nS: Skip the remaining parts\np: Use previous value for a part\nP: Use previous value for the remaining parts\nq: Quit" + hint_view_parts: "Press V and click the gear icon to view the parts." + value_not_found: "%{name}'s value not found." + wear_range: "Wear must be between 0 and 100." + non_negative: "Value must be non-negative." + invalid_input_retry: "Invalid input. Please try again." - # Friction brake settings - initial_fb: "Initial friction brake" - max_fb: "Max friction brake" - start_delay: "Start delay" - increase_delay: "Increase delay" - sensitivity: "Sensitivity" + part: + rod: "Rod" + reel_mechanism: "Reel mechanism" + reel_fb: "Reel friction brake" + fishing_line: "Fishing line" + leader: "Leader" + hook: "Hook" - # CalculateApp - tackle_stats: "Tackle's Stats" - results: "Results" - weakest_part: "Weakest part" - recommend_fb: "Recommend friction brake" - commands_help: "Commands:\nr: Restart\ns: Skip a part\nS: Skip the remaining parts\np: Use previous value for a part\nP: Use previous value for the remaining parts\nq: Quit" - hint_view_parts: "Press V and click the gear icon to view the parts." - value_not_found: "%{name}'s value not found." - wear_range: "Wear must be between 0 and 100." - non_negative: "Value must be non-negative." - invalid_input_retry: "Invalid input. Please try again." + prompt: + load_capacity: "Load capacity (kg)" + mech: "Mech (kg)" + drag: "Drag (kg)" + wear: "Wear (%)" - # Tackle part names - part_rod: "Rod" - part_reel_mechanism: "Reel mechanism" - part_reel_fb: "Reel friction brake" - part_fishing_line: "Fishing line" - part_leader: "Leader" - part_hook: "Hook" - prompt_load_capacity: "Load capacity (kg)" - prompt_mech: "Mech (kg)" - prompt_drag: "Drag (kg)" - prompt_wear: "Wear (%)" + result: + stop_reason: "Stop reason" + start_time: "Start time" + end_time: "End time" + running_time: "Running time" + bite_rate: "Bite rate" + total_fish: "Total fish" + kept_fish: "Kept fish" + kept_ratio: "Kept ratio" + green_tag: "Green tag fish" + yellow_tag: "Yellow tag fish" + blue_tag: "Blue tag fish" + purple_tag: "Purple tag fish" + pink_tag: "Pink tag fish" + card: "Card" + gift: "Gift" + tea: "Tea consumed" + carrot: "Carrot consumed" + alcohol: "Alcohol consumed" + coffee: "Coffee consumed" + bait: "Bait harvested" + ticket: "Ticket used" + success_crafts: "Successful crafts" + fail_crafts: "Failed crafts" + materials: "Materials used" - # result.py - result labels - result_stop_reason: "Stop reason" - result_start_time: "Start time" - result_end_time: "End time" - result_running_time: "Running time" - result_bite_rate: "Bite rate" - result_total_fish: "Total fish" - result_kept_fish: "Kept fish" - result_kept_ratio: "Kept ratio" - result_green_tag: "Green tag fish" - result_yellow_tag: "Yellow tag fish" - result_blue_tag: "Blue tag fish" - result_purple_tag: "Purple tag fish" - result_pink_tag: "Pink tag fish" - result_card: "Card" - result_gift: "Gift" - result_tea: "Tea consumed" - result_carrot: "Carrot consumed" - result_alcohol: "Alcohol consumed" - result_coffee: "Coffee consumed" - result_bait: "Bait harvested" - result_ticket: "Ticket used" - result_success_crafts: "Successful crafts" - result_fail_crafts: "Failed crafts" - result_materials: "Materials used" - - # Stop reasons - stop_keepnet_full: "Keepnet is full" - stop_tackle_broken: "Tackle is broken" - stop_line_at_end: "Fishing line is at its end" - stop_coffee_limit: "Coffee limit reached" - stop_no_bait: "Run out of bait" - stop_no_rods: "All rods are unavailable" - stop_lure_broken: "Lure is broken" - stop_line_snagged: "Line is snagged" - stop_ticket_expired: "Boat ticket expired" - stop_no_ticket: "New boat ticket not found" - stop_no_favorite: "Favorite item not found" - stop_disconnected: "Game disconnected" - stop_terminated: "Terminated by user" + stop: + keepnet_full: "Keepnet is full" + tackle_broken: "Tackle is broken" + line_at_end: "Fishing line is at its end" + coffee_limit: "Coffee limit reached" + no_bait: "Run out of bait" + no_rods: "All rods are unavailable" + lure_broken: "Lure is broken" + line_snagged: "Line is snagged" + ticket_expired: "Boat ticket expired" + no_ticket: "New boat ticket not found" + no_favorite: "Favorite item not found" + disconnected: "Game disconnected" + terminated: "Terminated by user" diff --git a/rf4s/i18n/ru.yml b/rf4s/i18n/ru.yml index 4240559..f87bf73 100644 --- a/rf4s/i18n/ru.yml +++ b/rf4s/i18n/ru.yml @@ -1,169 +1,167 @@ ru: - # Общие - bye: "Пока." - invalid_input: "Неверный ввод, попробуйте ещё раз." - press_any_key: "Нажмите любую клавишу для выхода." - using: "Вы используете: %{msg}" - hint: "Подсказка: %{msg}" + common: + bye: "Пока." + invalid_input: "Неверный ввод, попробуйте ещё раз." + press_any_key: "Нажмите любую клавишу для выхода." + using: "Вы используете: %{msg}" + hint: "Подсказка: %{msg}" - # main.py - выбор функций - select_feature: "Выберите функцию для запуска 🚀" - features: "Функции" - enter_fid: "Введите id функции, h — справка, q — выход." - enter_launch_options: "Введите параметры запуска, Enter — пропустить, h — справка, q — выход." - game_language_prompt: "Какой язык в игре? [(1) en (2) ru (3) q (выход)]" - click_lock_prompt: "Включена ли залипание кнопки мыши Windows? [(1) да (2) нет (3) q (выход)]" + main: + select_feature: "Выберите функцию для запуска 🚀" + features: "Функции" + enter_fid: "Введите id функции, h — справка, q — выход." + enter_launch_options: "Введите параметры запуска, Enter — пропустить, h — справка, q — выход." + game_language_prompt: "Какой язык в игре? [(1) en (2) ru (3) q (выход)]" + click_lock_prompt: "Включена ли залипание кнопки мыши Windows? [(1) да (2) нет (3) q (выход)]" - # Названия функций - feature_bot: "Рыболовный бот" - feature_craft: "Крафт предметов" - feature_move: "Движение вперёд" - feature_harvest: "Сбор наживки" - feature_frictionbrake: "Авто фрикцион" - feature_calculate: "Расчёт характеристик снасти" + feature: + bot: "Рыболовный бот" + craft: "Крафт предметов" + move: "Движение вперёд" + harvest: "Сбор наживки" + frictionbrake: "Авто фрикцион" + calculate: "Расчёт характеристик снасти" - # Argparse help (подкоманды) - help_bot: "запустить рыболовного бота" - help_craft: "крафтить предметы" - help_move: "переключить движение вперёд" - help_harvest: "собирать наживку" - help_frictionbrake: "автоматизировать фрикцион" - help_calculate: "рассчитать характеристики снасти" + help: + bot: "запустить рыболовного бота" + craft: "крафтить предметы" + move: "переключить движение вперёд" + harvest: "собирать наживку" + frictionbrake: "автоматизировать фрикцион" + calculate: "рассчитать характеристики снасти" + tag: "оставлять только помеченных рыб" + coffee: "пить кофе при низкой выносливости во время вываживания" + alcohol: "пить алкоголь перед сохранением рыбы" + refill: "употреблять чай и морковь при низком голоде или комфорте" + harvest_arg: "собирать наживку перед забросом" + lure: "менять приманку на случайную избранную, режим: спиннинг" + mouse: "случайно двигать мышь перед забросом" + pause: "иногда ставить скрипт на паузу перед забросом" + random_cast: "случайно делать лишний заброс" + skip_cast: "пропустить первый заброс" + lift: "постоянно поднимать снасть во время вываживания" + electro: "включить электрический режим для катушки Electro Raptor" + friction_brake: "автоматически регулировать фрикцион" + gear_ratio: "переключить передаточное число после таймаута подмотки" + bite: "сохранять скриншот в screenshots/ при поклёвке" + screenshot: "сохранять скриншот в screenshots/ после поимки рыбы" + data: "сохранять данные рыбалки в /logs" + email: "отправить email-уведомление после остановки скрипта" + miaotixing: "отправить уведомление miaotixing после остановки скрипта" + discord: "отправить уведомление Discord после остановки скрипта" + telegram: "отправить уведомление Telegram после остановки скрипта" + shutdown: "выключить компьютер после остановки скрипта" + signout: "выйти из аккаунта вместо закрытия игры" + broken_lure: "заменять сломанные приманки на избранные" + spod_rod: "перезабрасывать спод-род" + dry_mix: "включить пополнение сухой смеси, режим: донка" + groundbait: "включить пополнение прикормки, режим: донка" + pva: "включить пополнение PVA, режим: донка" + no_animation: "отключить ожидание анимации трофея и подарка\nизмените 'Стиль экрана поимки' на 'Простой' в настройках игры" + opts: "перезаписать конфигурацию" + pid: "указать id профиля" + pname: "указать имя профиля" + fishes_in_keepnet: "указать количество рыб в садке, (по умолчанию: %(default)s)" + trolling: "включить режим троллинга и указать направление\n(по умолчанию: %(default)s, без аргумента: %(const)s)" + rainbow: "включить режим радужной лески и указать метры подъёма\n(по умолчанию: %(default)s, без аргумента: %(const)s)" + boat_ticket: "включить продление билета на лодку и указать длительность\n(по умолчанию: %(default)s, без аргумента: %(const)s)" + discard: "выбрасывать все скрафченные предметы (для прикормки)" + ignore: "игнорировать невыбранные слоты материалов" + craft_limit: "указать количество предметов для крафта, (по умолчанию: %(default)s)" + shift: "удерживать Shift при движении" + refill_harvest: "восполнять голод и комфорт чаем и морковью" - # Argparse help (булевые аргументы бота) - help_tag: "оставлять только помеченных рыб" - help_coffee: "пить кофе при низкой выносливости во время вываживания" - help_alcohol: "пить алкоголь перед сохранением рыбы" - help_refill: "употреблять чай и морковь при низком голоде или комфорте" - help_harvest_arg: "собирать наживку перед забросом" - help_lure: "менять приманку на случайную избранную, режим: спиннинг" - help_mouse: "случайно двигать мышь перед забросом" - help_pause: "иногда ставить скрипт на паузу перед забросом" - help_random_cast: "случайно делать лишний заброс" - help_skip_cast: "пропустить первый заброс" - help_lift: "постоянно поднимать снасть во время вываживания" - help_electro: "включить электрический режим для катушки Electro Raptor" - help_friction_brake: "автоматически регулировать фрикцион" - help_gear_ratio: "переключить передаточное число после таймаута подмотки" - help_bite: "сохранять скриншот в screenshots/ при поклёвке" - help_screenshot: "сохранять скриншот в screenshots/ после поимки рыбы" - help_data: "сохранять данные рыбалки в /logs" - help_email: "отправить email-уведомление после остановки скрипта" - help_miaotixing: "отправить уведомление miaotixing после остановки скрипта" - help_discord: "отправить уведомление Discord после остановки скрипта" - help_telegram: "отправить уведомление Telegram после остановки скрипта" - help_shutdown: "выключить компьютер после остановки скрипта" - help_signout: "выйти из аккаунта вместо закрытия игры" - help_broken_lure: "заменять сломанные приманки на избранные" - help_spod_rod: "перезабрасывать спод-род" - help_dry_mix: "включить пополнение сухой смеси, режим: донка" - help_groundbait: "включить пополнение прикормки, режим: донка" - help_pva: "включить пополнение PVA, режим: донка" - help_no_animation: "отключить ожидание анимации трофея и подарка\nизмените 'Стиль экрана поимки' на 'Простой' в настройках игры" + ui: + settings: "Настройки" + launch_options_final: "ПАРАМЕТРЫ ЗАПУСКА (ИТОГ)" + running_result: "Результат работы" + select_profile: "Выберите профиль для запуска ⚙️" + enter_pid: "Введите id профиля, q — выход." + invalid_pid: "Неверный id профиля, попробуйте ещё раз." + press_pause_quit: "Нажмите %{pause} для паузы, %{quit} для выхода." + press_quit: "Нажмите %{quit} для выхода." + press_move: "Нажмите %{pause} для паузы, %{quit} для выхода." + press_restart: "Нажмите %{pause} для перезагрузки конфига и перезапуска." + hint_launch_ignored: "Любые изменения LAUNCH_OPTIONS будут проигнорированы." + press_fb: "Нажмите %{reset} для сброса фрикциона, %{quit} для выхода." - # Argparse help (другие аргументы) - help_opts: "перезаписать конфигурацию" - help_pid: "указать id профиля" - help_pname: "указать имя профиля" - help_fishes_in_keepnet: "указать количество рыб в садке, (по умолчанию: %(default)s)" - help_trolling: "включить режим троллинга и указать направление\n(по умолчанию: %(default)s, без аргумента: %(const)s)" - help_rainbow: "включить режим радужной лески и указать метры подъёма\n(по умолчанию: %(default)s, без аргумента: %(const)s)" - help_boat_ticket: "включить продление билета на лодку и указать длительность\n(по умолчанию: %(default)s, без аргумента: %(const)s)" - help_discard: "выбрасывать все скрафченные предметы (для прикормки)" - help_ignore: "игнорировать невыбранные слоты материалов" - help_craft_limit: "указать количество предметов для крафта, (по умолчанию: %(default)s)" - help_shift: "удерживать Shift при движении" - help_refill_harvest: "восполнять голод и комфорт чаем и морковью" + harvest: + power_saving: "Энергосбережение" + check_delay: "Задержка проверки" + energy_threshold: "Порог энергии" + hunger_threshold: "Порог голода" + comfort_threshold: "Порог комфорта" - # app.py - общие элементы UI - settings: "Настройки" - launch_options_final: "ПАРАМЕТРЫ ЗАПУСКА (ИТОГ)" - running_result: "Результат работы" - select_profile: "Выберите профиль для запуска ⚙️" - enter_pid: "Введите id профиля, q — выход." - invalid_pid: "Неверный id профиля, попробуйте ещё раз." - press_pause_quit: "Нажмите %{pause} для паузы, %{quit} для выхода." - press_quit: "Нажмите %{quit} для выхода." - press_move: "Нажмите %{pause} для паузы, %{quit} для выхода." - press_restart: "Нажмите %{pause} для перезагрузки конфига и перезапуска." - hint_launch_ignored: "Любые изменения LAUNCH_OPTIONS будут проигнорированы." - press_fb: "Нажмите %{reset} для сброса фрикциона, %{quit} для выхода." + friction_brake: + initial_fb: "Начальный фрикцион" + max_fb: "Макс. фрикцион" + start_delay: "Задержка старта" + increase_delay: "Задержка увеличения" + sensitivity: "Чувствительность" - # Harvest settings - power_saving: "Энергосбережение" - check_delay: "Задержка проверки" - energy_threshold: "Порог энергии" - hunger_threshold: "Порог голода" - comfort_threshold: "Порог комфорта" + calculate: + tackle_stats: "Характеристики снасти" + results: "Результаты" + weakest_part: "Слабое звено" + recommend_fb: "Рекомендуемый фрикцион" + commands_help: "Команды:\nr: Перезапуск\ns: Пропустить часть\nS: Пропустить оставшиеся части\np: Использовать предыдущее значение\nP: Использовать предыдущее значение для оставшихся\nq: Выход" + hint_view_parts: "Нажмите V и кликните на иконку шестерёнки для просмотра частей." + value_not_found: "Значение %{name} не найдено." + wear_range: "Износ должен быть от 0 до 100." + non_negative: "Значение должно быть неотрицательным." + invalid_input_retry: "Неверный ввод. Попробуйте ещё раз." - # Friction brake settings - initial_fb: "Начальный фрикцион" - max_fb: "Макс. фрикцион" - start_delay: "Задержка старта" - increase_delay: "Задержка увеличения" - sensitivity: "Чувствительность" + part: + rod: "Удилище" + reel_mechanism: "Механизм катушки" + reel_fb: "Фрикцион катушки" + fishing_line: "Леска" + leader: "Поводок" + hook: "Крючок" - # CalculateApp - tackle_stats: "Характеристики снасти" - results: "Результаты" - weakest_part: "Слабое звено" - recommend_fb: "Рекомендуемый фрикцион" - commands_help: "Команды:\nr: Перезапуск\ns: Пропустить часть\nS: Пропустить оставшиеся части\np: Использовать предыдущее значение\nP: Использовать предыдущее значение для оставшихся\nq: Выход" - hint_view_parts: "Нажмите V и кликните на иконку шестерёнки для просмотра частей." - value_not_found: "Значение %{name} не найдено." - wear_range: "Износ должен быть от 0 до 100." - non_negative: "Значение должно быть неотрицательным." - invalid_input_retry: "Неверный ввод. Попробуйте ещё раз." + prompt: + load_capacity: "Нагрузка (кг)" + mech: "Мех (кг)" + drag: "Тяга (кг)" + wear: "Износ (%)" - # Названия частей снасти - part_rod: "Удилище" - part_reel_mechanism: "Механизм катушки" - part_reel_fb: "Фрикцион катушки" - part_fishing_line: "Леска" - part_leader: "Поводок" - part_hook: "Крючок" - prompt_load_capacity: "Нагрузка (кг)" - prompt_mech: "Мех (кг)" - prompt_drag: "Тяга (кг)" - prompt_wear: "Износ (%)" + result: + stop_reason: "Причина остановки" + start_time: "Время начала" + end_time: "Время окончания" + running_time: "Время работы" + bite_rate: "Частота поклёвок" + total_fish: "Всего рыб" + kept_fish: "Оставлено рыб" + kept_ratio: "Доля оставленных" + green_tag: "Зелёная метка" + yellow_tag: "Жёлтая метка" + blue_tag: "Синяя метка" + purple_tag: "Фиолетовая метка" + pink_tag: "Розовая метка" + card: "Карточка" + gift: "Подарок" + tea: "Чай выпито" + carrot: "Морковь съедено" + alcohol: "Алкоголь выпито" + coffee: "Кофе выпито" + bait: "Наживки собрано" + ticket: "Билетов использовано" + success_crafts: "Успешных крафтов" + fail_crafts: "Неудачных крафтов" + materials: "Материалов использовано" - # result.py - метки результатов - result_stop_reason: "Причина остановки" - result_start_time: "Время начала" - result_end_time: "Время окончания" - result_running_time: "Время работы" - result_bite_rate: "Частота поклёвок" - result_total_fish: "Всего рыб" - result_kept_fish: "Оставлено рыб" - result_kept_ratio: "Доля оставленных" - result_green_tag: "Зелёная метка" - result_yellow_tag: "Жёлтая метка" - result_blue_tag: "Синяя метка" - result_purple_tag: "Фиолетовая метка" - result_pink_tag: "Розовая метка" - result_card: "Карточка" - result_gift: "Подарок" - result_tea: "Чай выпито" - result_carrot: "Морковь съедено" - result_alcohol: "Алкоголь выпито" - result_coffee: "Кофе выпито" - result_bait: "Наживки собрано" - result_ticket: "Билетов использовано" - result_success_crafts: "Успешных крафтов" - result_fail_crafts: "Неудачных крафтов" - result_materials: "Материалов использовано" - - # Причины остановки - stop_keepnet_full: "Садок полон" - stop_tackle_broken: "Снасть сломана" - stop_line_at_end: "Леска закончилась" - stop_coffee_limit: "Лимит кофе достигнут" - stop_no_bait: "Наживка закончилась" - stop_no_rods: "Все удилища недоступны" - stop_lure_broken: "Приманка сломана" - stop_line_snagged: "Леска зацепилась" - stop_ticket_expired: "Билет на лодку истёк" - stop_no_ticket: "Новый билет на лодку не найден" - stop_no_favorite: "Избранный предмет не найден" - stop_disconnected: "Игра отключена" - stop_terminated: "Остановлено пользователем" + stop: + keepnet_full: "Садок полон" + tackle_broken: "Снасть сломана" + line_at_end: "Леска закончилась" + coffee_limit: "Лимит кофе достигнут" + no_bait: "Наживка закончилась" + no_rods: "Все удилища недоступны" + lure_broken: "Приманка сломана" + line_snagged: "Леска зацепилась" + ticket_expired: "Билет на лодку истёк" + no_ticket: "Новый билет на лодку не найден" + no_favorite: "Избранный предмет не найден" + disconnected: "Игра отключена" + terminated: "Остановлено пользователем" diff --git a/rf4s/i18n/zh-TW.yml b/rf4s/i18n/zh-TW.yml index c073887..e4505d4 100644 --- a/rf4s/i18n/zh-TW.yml +++ b/rf4s/i18n/zh-TW.yml @@ -1,169 +1,167 @@ zh-TW: - # 通用 - bye: "再見。" - invalid_input: "輸入無效,請再試一次。" - press_any_key: "按任意鍵退出。" - using: "您正在使用:%{msg}" - hint: "提示:%{msg}" + common: + bye: "再見。" + invalid_input: "輸入無效,請再試一次。" + press_any_key: "按任意鍵退出。" + using: "您正在使用:%{msg}" + hint: "提示:%{msg}" - # main.py - 功能選擇 - select_feature: "選擇要啟動的功能 🚀" - features: "功能" - enter_fid: "輸入功能 id,h 查看說明,q 退出。" - enter_launch_options: "輸入啟動選項,Enter 跳過,h 查看說明,q 退出。" - game_language_prompt: "您的遊戲語言是什麼?[(1) en (2) ru (3) q (退出)]" - click_lock_prompt: "是否啟用了 Windows 滑鼠鎖定點擊?[(1) 是 (2) 否 (3) q (退出)]" + main: + select_feature: "選擇要啟動的功能 🚀" + features: "功能" + enter_fid: "輸入功能 id,h 查看說明,q 退出。" + enter_launch_options: "輸入啟動選項,Enter 跳過,h 查看說明,q 退出。" + game_language_prompt: "您的遊戲語言是什麼?[(1) en (2) ru (3) q (退出)]" + click_lock_prompt: "是否啟用了 Windows 滑鼠鎖定點擊?[(1) 是 (2) 否 (3) q (退出)]" - # 功能名稱 - feature_bot: "釣魚機器人" - feature_craft: "製作物品" - feature_move: "向前移動" - feature_harvest: "收集餌料" - feature_frictionbrake: "自動煞車" - feature_calculate: "計算釣具數據" + feature: + bot: "釣魚機器人" + craft: "製作物品" + move: "向前移動" + harvest: "收集餌料" + frictionbrake: "自動煞車" + calculate: "計算釣具數據" - # Argparse help(子命令) - help_bot: "啟動釣魚機器人" - help_craft: "製作物品" - help_move: "切換向前移動" - help_harvest: "收集餌料" - help_frictionbrake: "自動化煞車" - help_calculate: "計算釣具數據" + help: + bot: "啟動釣魚機器人" + craft: "製作物品" + move: "切換向前移動" + harvest: "收集餌料" + frictionbrake: "自動化煞車" + calculate: "計算釣具數據" + tag: "只保留有標記的魚" + coffee: "戰鬥中體力低時喝咖啡" + alcohol: "保留魚之前喝酒" + refill: "飢餓或舒適度低時消耗茶和胡蘿蔔" + harvest_arg: "拋竿前收集餌料" + lure: "用隨機收藏的餌更換當前餌,模式:路亞" + mouse: "拋竿前隨機移動滑鼠" + pause: "偶爾在拋竿前暫停腳本" + random_cast: "隨機進行多餘的拋竿" + skip_cast: "跳過第一次拋竿" + lift: "戰鬥中持續提竿" + electro: "啟用 Electro Raptor 系列捲線器的電動模式" + friction_brake: "自動調整煞車" + gear_ratio: "收線超時後切換齒輪比或模式" + bite: "魚咬鉤時在 screenshots/ 保存截圖" + screenshot: "釣到魚後在 screenshots/ 保存截圖" + data: "將釣魚數據保存到 /logs" + email: "腳本停止後發送電子郵件通知" + miaotixing: "腳本停止後發送喵提醒通知" + discord: "腳本停止後發送 Discord 通知" + telegram: "腳本停止後發送 Telegram 通知" + shutdown: "腳本停止後關閉電腦" + signout: "登出而不是關閉遊戲" + broken_lure: "用收藏的餌替換損壞的餌" + spod_rod: "重新拋擲誘餌竿" + dry_mix: "啟用乾粉補充,模式:底釣" + groundbait: "啟用窩料補充,模式:底釣" + pva: "啟用 PVA 補充,模式:底釣" + no_animation: "停用獎盃和禮物動畫等待\n在遊戲設定中將「捕獲畫面樣式」改為「簡單」以使用此選項" + opts: "覆寫設定" + pid: "指定要使用的設定檔 id" + pname: "指定要使用的設定檔名稱" + fishes_in_keepnet: "指定魚護中的魚數,(預設:%(default)s)" + trolling: "啟用拖釣模式並指定方向\n(預設:%(default)s,無參數:%(const)s)" + rainbow: "啟用彩虹線模式並指定提竿公尺數\n(預設:%(default)s,無參數:%(const)s)" + boat_ticket: "啟用船票續期並指定時長\n(預設:%(default)s,無參數:%(const)s)" + discard: "丟棄所有製作的物品(用於窩料)" + ignore: "忽略未選擇的材料欄位" + craft_limit: "指定要製作的物品數量,(預設:%(default)s)" + shift: "移動時按住 Shift 鍵" + refill_harvest: "透過消耗茶和胡蘿蔔來補充飢餓和舒適度" - # Argparse help(機器人布林參數) - help_tag: "只保留有標記的魚" - help_coffee: "戰鬥中體力低時喝咖啡" - help_alcohol: "保留魚之前喝酒" - help_refill: "飢餓或舒適度低時消耗茶和胡蘿蔔" - help_harvest_arg: "拋竿前收集餌料" - help_lure: "用隨機收藏的餌更換當前餌,模式:路亞" - help_mouse: "拋竿前隨機移動滑鼠" - help_pause: "偶爾在拋竿前暫停腳本" - help_random_cast: "隨機進行多餘的拋竿" - help_skip_cast: "跳過第一次拋竿" - help_lift: "戰鬥中持續提竿" - help_electro: "啟用 Electro Raptor 系列捲線器的電動模式" - help_friction_brake: "自動調整煞車" - help_gear_ratio: "收線超時後切換齒輪比或模式" - help_bite: "魚咬鉤時在 screenshots/ 保存截圖" - help_screenshot: "釣到魚後在 screenshots/ 保存截圖" - help_data: "將釣魚數據保存到 /logs" - help_email: "腳本停止後發送電子郵件通知" - help_miaotixing: "腳本停止後發送喵提醒通知" - help_discord: "腳本停止後發送 Discord 通知" - help_telegram: "腳本停止後發送 Telegram 通知" - help_shutdown: "腳本停止後關閉電腦" - help_signout: "登出而不是關閉遊戲" - help_broken_lure: "用收藏的餌替換損壞的餌" - help_spod_rod: "重新拋擲誘餌竿" - help_dry_mix: "啟用乾粉補充,模式:底釣" - help_groundbait: "啟用窩料補充,模式:底釣" - help_pva: "啟用 PVA 補充,模式:底釣" - help_no_animation: "停用獎盃和禮物動畫等待\n在遊戲設定中將「捕獲畫面樣式」改為「簡單」以使用此選項" + ui: + settings: "設定" + launch_options_final: "啟動選項(最終)" + running_result: "執行結果" + select_profile: "選擇要啟動的設定檔 ⚙️" + enter_pid: "輸入設定檔 id,q 退出。" + invalid_pid: "無效的設定檔 id,請再試一次。" + press_pause_quit: "按 %{pause} 暫停,%{quit} 退出。" + press_quit: "按 %{quit} 退出。" + press_move: "按 %{pause} 暫停,%{quit} 退出。" + press_restart: "按 %{pause} 重新載入設定並重啟。" + hint_launch_ignored: "對 LAUNCH_OPTIONS 的任何修改將被忽略。" + press_fb: "按 %{reset} 重置煞車,%{quit} 退出。" - # Argparse help(其他參數) - help_opts: "覆寫設定" - help_pid: "指定要使用的設定檔 id" - help_pname: "指定要使用的設定檔名稱" - help_fishes_in_keepnet: "指定魚護中的魚數,(預設:%(default)s)" - help_trolling: "啟用拖釣模式並指定方向\n(預設:%(default)s,無參數:%(const)s)" - help_rainbow: "啟用彩虹線模式並指定提竿公尺數\n(預設:%(default)s,無參數:%(const)s)" - help_boat_ticket: "啟用船票續期並指定時長\n(預設:%(default)s,無參數:%(const)s)" - help_discard: "丟棄所有製作的物品(用於窩料)" - help_ignore: "忽略未選擇的材料欄位" - help_craft_limit: "指定要製作的物品數量,(預設:%(default)s)" - help_shift: "移動時按住 Shift 鍵" - help_refill_harvest: "透過消耗茶和胡蘿蔔來補充飢餓和舒適度" + harvest: + power_saving: "省電模式" + check_delay: "檢查延遲" + energy_threshold: "能量閾值" + hunger_threshold: "飢餓閾值" + comfort_threshold: "舒適度閾值" - # app.py - 通用 UI - settings: "設定" - launch_options_final: "啟動選項(最終)" - running_result: "執行結果" - select_profile: "選擇要啟動的設定檔 ⚙️" - enter_pid: "輸入設定檔 id,q 退出。" - invalid_pid: "無效的設定檔 id,請再試一次。" - press_pause_quit: "按 %{pause} 暫停,%{quit} 退出。" - press_quit: "按 %{quit} 退出。" - press_move: "按 %{pause} 暫停,%{quit} 退出。" - press_restart: "按 %{pause} 重新載入設定並重啟。" - hint_launch_ignored: "對 LAUNCH_OPTIONS 的任何修改將被忽略。" - press_fb: "按 %{reset} 重置煞車,%{quit} 退出。" + friction_brake: + initial_fb: "初始煞車" + max_fb: "最大煞車" + start_delay: "啟動延遲" + increase_delay: "增加延遲" + sensitivity: "靈敏度" - # Harvest settings - power_saving: "省電模式" - check_delay: "檢查延遲" - energy_threshold: "能量閾值" - hunger_threshold: "飢餓閾值" - comfort_threshold: "舒適度閾值" + calculate: + tackle_stats: "釣具數據" + results: "結果" + weakest_part: "最弱部件" + recommend_fb: "建議煞車值" + commands_help: "指令:\nr:重新開始\ns:跳過一個部件\nS:跳過剩餘部件\np:使用上次的值\nP:對剩餘部件使用上次的值\nq:退出" + hint_view_parts: "按 V 並點擊齒輪圖示查看部件。" + value_not_found: "找不到 %{name} 的值。" + wear_range: "磨損必須在 0 到 100 之間。" + non_negative: "值必須為非負數。" + invalid_input_retry: "輸入無效,請再試一次。" - # Friction brake settings - initial_fb: "初始煞車" - max_fb: "最大煞車" - start_delay: "啟動延遲" - increase_delay: "增加延遲" - sensitivity: "靈敏度" + part: + rod: "釣竿" + reel_mechanism: "捲線器機構" + reel_fb: "捲線器煞車" + fishing_line: "釣線" + leader: "子線" + hook: "魚鉤" - # CalculateApp - tackle_stats: "釣具數據" - results: "結果" - weakest_part: "最弱部件" - recommend_fb: "建議煞車值" - commands_help: "指令:\nr:重新開始\ns:跳過一個部件\nS:跳過剩餘部件\np:使用上次的值\nP:對剩餘部件使用上次的值\nq:退出" - hint_view_parts: "按 V 並點擊齒輪圖示查看部件。" - value_not_found: "找不到 %{name} 的值。" - wear_range: "磨損必須在 0 到 100 之間。" - non_negative: "值必須為非負數。" - invalid_input_retry: "輸入無效,請再試一次。" + prompt: + load_capacity: "承載力(kg)" + mech: "機械(kg)" + drag: "阻力(kg)" + wear: "磨損(%)" - # 釣具部件名稱 - part_rod: "釣竿" - part_reel_mechanism: "捲線器機構" - part_reel_fb: "捲線器煞車" - part_fishing_line: "釣線" - part_leader: "子線" - part_hook: "魚鉤" - prompt_load_capacity: "承載力(kg)" - prompt_mech: "機械(kg)" - prompt_drag: "阻力(kg)" - prompt_wear: "磨損(%)" + result: + stop_reason: "停止原因" + start_time: "開始時間" + end_time: "結束時間" + running_time: "執行時間" + bite_rate: "咬鉤率" + total_fish: "總魚數" + kept_fish: "保留魚數" + kept_ratio: "保留比例" + green_tag: "綠色標記魚" + yellow_tag: "黃色標記魚" + blue_tag: "藍色標記魚" + purple_tag: "紫色標記魚" + pink_tag: "粉色標記魚" + card: "卡片" + gift: "禮物" + tea: "消耗茶" + carrot: "消耗胡蘿蔔" + alcohol: "消耗酒" + coffee: "消耗咖啡" + bait: "收穫餌料" + ticket: "使用船票" + success_crafts: "成功製作" + fail_crafts: "失敗製作" + materials: "使用材料" - # result.py - 結果標籤 - result_stop_reason: "停止原因" - result_start_time: "開始時間" - result_end_time: "結束時間" - result_running_time: "執行時間" - result_bite_rate: "咬鉤率" - result_total_fish: "總魚數" - result_kept_fish: "保留魚數" - result_kept_ratio: "保留比例" - result_green_tag: "綠色標記魚" - result_yellow_tag: "黃色標記魚" - result_blue_tag: "藍色標記魚" - result_purple_tag: "紫色標記魚" - result_pink_tag: "粉色標記魚" - result_card: "卡片" - result_gift: "禮物" - result_tea: "消耗茶" - result_carrot: "消耗胡蘿蔔" - result_alcohol: "消耗酒" - result_coffee: "消耗咖啡" - result_bait: "收穫餌料" - result_ticket: "使用船票" - result_success_crafts: "成功製作" - result_fail_crafts: "失敗製作" - result_materials: "使用材料" - - # 停止原因 - stop_keepnet_full: "魚護已滿" - stop_tackle_broken: "釣具損壞" - stop_line_at_end: "釣線已到盡頭" - stop_coffee_limit: "咖啡上限已達" - stop_no_bait: "餌料用完" - stop_no_rods: "所有釣竿不可用" - stop_lure_broken: "餌已損壞" - stop_line_snagged: "釣線纏住" - stop_ticket_expired: "船票已過期" - stop_no_ticket: "找不到新船票" - stop_no_favorite: "找不到收藏物品" - stop_disconnected: "遊戲已斷線" - stop_terminated: "使用者終止" + stop: + keepnet_full: "魚護已滿" + tackle_broken: "釣具損壞" + line_at_end: "釣線已到盡頭" + coffee_limit: "咖啡上限已達" + no_bait: "餌料用完" + no_rods: "所有釣竿不可用" + lure_broken: "餌已損壞" + line_snagged: "釣線纏住" + ticket_expired: "船票已過期" + no_ticket: "找不到新船票" + no_favorite: "找不到收藏物品" + disconnected: "遊戲已斷線" + terminated: "使用者終止" diff --git a/rf4s/result/result.py b/rf4s/result/result.py index 4674ccd..ed77a6d 100644 --- a/rf4s/result/result.py +++ b/rf4s/result/result.py @@ -3,22 +3,6 @@ from rf4s.controller.timer import Timer from rf4s.i18n import t -STOP_REASON_KEYS = { - "Keepnet is full": "stop_keepnet_full", - "Tackle is broken": "stop_tackle_broken", - "Fishing line is at its end": "stop_line_at_end", - "Coffee limit reached": "stop_coffee_limit", - "Run out of bait": "stop_no_bait", - "All rods are unavailable": "stop_no_rods", - "Lure is broken": "stop_lure_broken", - "Line is snagged": "stop_line_snagged", - "Boat ticket expired": "stop_ticket_expired", - "New boat ticket not found": "stop_no_ticket", - "Favorite item not found": "stop_no_favorite", - "Game disconnected": "stop_disconnected", - "Terminated by user": "stop_terminated", -} - @dataclass class Result: @@ -55,31 +39,30 @@ def as_dict(self, msg: str, timer: Timer) -> dict: kept_ratio = "0%" bite_rate = "0/hr" - stop_key = STOP_REASON_KEYS.get(msg) - stop_reason = t(stop_key) if stop_key else msg + stop_reason = t(msg) return { - t("result_stop_reason"): stop_reason, - t("result_start_time"): timer.get_start_datetime(), - t("result_end_time"): timer.get_cur_datetime(), - t("result_running_time"): timer.get_running_time_str(), - t("result_bite_rate"): bite_rate, - t("result_total_fish"): self.total, - t("result_kept_fish"): self.kept, - t("result_kept_ratio"): kept_ratio, - t("result_green_tag"): self.green, - t("result_yellow_tag"): self.yellow, - t("result_blue_tag"): self.blue, - t("result_purple_tag"): self.purple, - t("result_pink_tag"): self.pink, - t("result_card"): self.card, - t("result_gift"): self.gift, - t("result_tea"): self.tea, - t("result_carrot"): self.carrot, - t("result_alcohol"): self.alcohol, - t("result_coffee"): self.coffee, - t("result_bait"): self.bait, - t("result_ticket"): self.ticket, + t("result.stop_reason"): stop_reason, + t("result.start_time"): timer.get_start_datetime(), + t("result.end_time"): timer.get_cur_datetime(), + t("result.running_time"): timer.get_running_time_str(), + t("result.bite_rate"): bite_rate, + t("result.total_fish"): self.total, + t("result.kept_fish"): self.kept, + t("result.kept_ratio"): kept_ratio, + t("result.green_tag"): self.green, + t("result.yellow_tag"): self.yellow, + t("result.blue_tag"): self.blue, + t("result.purple_tag"): self.purple, + t("result.pink_tag"): self.pink, + t("result.card"): self.card, + t("result.gift"): self.gift, + t("result.tea"): self.tea, + t("result.carrot"): self.carrot, + t("result.alcohol"): self.alcohol, + t("result.coffee"): self.coffee, + t("result.bait"): self.bait, + t("result.ticket"): self.ticket, } @@ -91,9 +74,9 @@ class CraftResult: def as_dict(self) -> dict: return { - t("result_success_crafts"): self.success, - t("result_fail_crafts"): self.fail, - t("result_materials"): self.material, + t("result.success_crafts"): self.success, + t("result.fail_crafts"): self.fail, + t("result.materials"): self.material, } @@ -105,7 +88,7 @@ class HarvestResult: def as_dict(self) -> dict: return { - t("result_tea"): self.tea, - t("result_carrot"): self.carrot, - t("result_bait"): self.bait, + t("result.tea"): self.tea, + t("result.carrot"): self.carrot, + t("result.bait"): self.bait, } diff --git a/rf4s/utils.py b/rf4s/utils.py index 3b52684..bfc81bb 100644 --- a/rf4s/utils.py +++ b/rf4s/utils.py @@ -126,11 +126,11 @@ def print_usage_box(msg: str) -> None: def print_description_box(msg: str) -> None: - print(Panel.fit(t("using", msg=msg))) + print(Panel.fit(t("common.using", msg=msg))) def print_hint_box(msg: str) -> None: - print(Panel.fit(t("hint", msg=msg), style="green")) + print(Panel.fit(t("common.hint", msg=msg), style="green")) def print_error(msg: str) -> None: @@ -139,7 +139,7 @@ def print_error(msg: str) -> None: def safe_exit(): if is_run_by_clicking(): - print_usage_box(t("press_any_key")) + print_usage_box(t("common.press_any_key")) # KeyboardInterrupt will mess with stdin, input will crash silently # Use msvcrt.getch() because it doesn't depends on stdin msvcrt.getch() From 0e374ee1833e1afe4b8091c476643891731a0d9d Mon Sep 17 00:00:00 2001 From: Zuckerig Date: Tue, 17 Feb 2026 19:01:06 +0500 Subject: [PATCH 3/3] Correcting a formatting error --- main.py | 9 +++- rf4s/app/app.py | 109 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 93 insertions(+), 25 deletions(-) diff --git a/main.py b/main.py index ed8aae9..9d41834 100644 --- a/main.py +++ b/main.py @@ -162,7 +162,9 @@ def setup_parser(cfg: CN) -> tuple[argparse.ArgumentParser, tuple]: "-V", "--version", action="version", version=f"RF4S {VERSION}" ) - feature_parsers = main_parser.add_subparsers(title=t("main.features"), dest="feature") + feature_parsers = main_parser.add_subparsers( + title=t("main.features"), dest="feature" + ) bot_parser = feature_parsers.add_parser( "bot", @@ -253,7 +255,10 @@ def num_fish(_num_fish: str) -> int: ) craft_parser = feature_parsers.add_parser( - "craft", help=t("help.craft"), parents=[parent_parser], formatter_class=Formatter + "craft", + help=t("help.craft"), + parents=[parent_parser], + formatter_class=Formatter, ) craft_parser.add_argument( "-V", "--version", action="version", version=f"RF4S-craft {VERSION}" diff --git a/rf4s/app/app.py b/rf4s/app/app.py index 6869c1e..2b1aa3d 100644 --- a/rf4s/app/app.py +++ b/rf4s/app/app.py @@ -448,9 +448,7 @@ def start(self) -> None: if not self.paused: break - utils.print_usage_box( - t("ui.press_restart", pause=self.cfg.KEY.PAUSE) - ) + utils.print_usage_box(t("ui.press_restart", pause=self.cfg.KEY.PAUSE)) utils.print_hint_box(t("ui.hint_launch_ignored")) with self.player.hold_keys(mouse=False, shift=False, reset=True): pause_listener = keyboard.Listener(on_release=self._pause_wait) @@ -605,7 +603,11 @@ def __init__(self, cfg, args, parser): self.cfg.freeze() utils.print_usage_box( - t("ui.press_move", pause=self.cfg.KEY.MOVE_PAUSE, quit=self.cfg.KEY.MOVE_QUIT) + t( + "ui.press_move", + pause=self.cfg.KEY.MOVE_PAUSE, + quit=self.cfg.KEY.MOVE_QUIT, + ) ) self.result = Result() @@ -669,9 +671,15 @@ def __init__(self, cfg, args, parser): settings.add_row(t("ui.launch_options_final"), " ".join(sys.argv[1:])) settings.add_row(t("harvest.power_saving"), str(self.cfg.HARVEST.POWER_SAVING)) settings.add_row(t("harvest.check_delay"), str(self.cfg.HARVEST.CHECK_DELAY)) - settings.add_row(t("harvest.energy_threshold"), str(self.cfg.STAT.ENERGY_THRESHOLD)) - settings.add_row(t("harvest.hunger_threshold"), str(self.cfg.STAT.HUNGER_THRESHOLD)) - settings.add_row(t("harvest.comfort_threshold"), str(self.cfg.STAT.COMFORT_THRESHOLD)) + settings.add_row( + t("harvest.energy_threshold"), str(self.cfg.STAT.ENERGY_THRESHOLD) + ) + settings.add_row( + t("harvest.hunger_threshold"), str(self.cfg.STAT.HUNGER_THRESHOLD) + ) + settings.add_row( + t("harvest.comfort_threshold"), str(self.cfg.STAT.COMFORT_THRESHOLD) + ) print(settings) utils.print_usage_box(t("ui.press_quit", quit=self.cfg.KEY.QUIT)) @@ -809,12 +817,44 @@ def __init__(self, cfg, args, parser): _ = cfg, args, parser self.result = None self.parts = [ - Part(key="rod", name_key="part.rod", prompt_key="prompt.load_capacity", color="orange1", base=0.3), - Part(key="reel_mechanism", name_key="part.reel_mechanism", prompt_key="prompt.mech", color="plum1", base=0.3), - Part(key="reel_friction_brake", name_key="part.reel_fb", prompt_key="prompt.drag", color="gold1"), - Part(key="fishing_line", name_key="part.fishing_line", prompt_key="prompt.load_capacity", color="salmon1"), - Part(key="leader", name_key="part.leader", prompt_key="prompt.load_capacity", color="pale_green1"), - Part(key="hook", name_key="part.hook", prompt_key="prompt.load_capacity", color="sky_blue1"), + Part( + key="rod", + name_key="part.rod", + prompt_key="prompt.load_capacity", + color="orange1", + base=0.3, + ), + Part( + key="reel_mechanism", + name_key="part.reel_mechanism", + prompt_key="prompt.mech", + color="plum1", + base=0.3, + ), + Part( + key="reel_friction_brake", + name_key="part.reel_fb", + prompt_key="prompt.drag", + color="gold1", + ), + Part( + key="fishing_line", + name_key="part.fishing_line", + prompt_key="prompt.load_capacity", + color="salmon1", + ), + Part( + key="leader", + name_key="part.leader", + prompt_key="prompt.load_capacity", + color="pale_green1", + ), + Part( + key="hook", + name_key="part.hook", + prompt_key="prompt.load_capacity", + color="sky_blue1", + ), ] self.friction_brake = next( part for part in self.parts if part.key == "reel_friction_brake" @@ -828,9 +868,13 @@ def calculate_tackle_stats(self): if part.pre_real_load_capacity is not None: raise exceptions.PreviousError else: - utils.print_error(t("calculate.value_not_found", name=part.name)) + utils.print_error( + t("calculate.value_not_found", name=part.name) + ) part.load_capacity = self.get_validated_input(part, part.prompt) - part.wear = self.get_validated_input(part, t("prompt.wear"), is_wear=True) + part.wear = self.get_validated_input( + part, t("prompt.wear"), is_wear=True + ) except exceptions.SkipError: if part.real_load_capacity is not None: part.real_load_capacity = None @@ -844,7 +888,9 @@ def calculate_tackle_stats(self): part.calculate_real_load_capacity() self.result.add_row(part.name, f"{part.real_load_capacity:.2f} kg") - def get_validated_input(self, part: Part, prompt: str, is_wear: bool = False) -> float: + def get_validated_input( + self, part: Part, prompt: str, is_wear: bool = False + ) -> float: while True: user_input = Prompt.ask( f"[{part.color}][{part.name}][/{part.color}] {prompt}" @@ -854,12 +900,16 @@ def get_validated_input(self, part: Part, prompt: str, is_wear: bool = False) -> raise exceptions.RestartError case CalculateCommand.PREVIOUS.value: if part.pre_real_load_capacity is None: - utils.print_error(t("calculate.value_not_found", name=part.name)) + utils.print_error( + t("calculate.value_not_found", name=part.name) + ) continue raise exceptions.PreviousError case CalculateCommand.PREVIOUS_REMAINING.value: if part.pre_real_load_capacity is None: - utils.print_error(t("calculate.value_not_found", name=part.name)) + utils.print_error( + t("calculate.value_not_found", name=part.name) + ) continue raise exceptions.PreviousRemainingError case CalculateCommand.SKIP.value: @@ -973,15 +1023,28 @@ def __init__(self, cfg, args, parser): title=t("ui.settings"), show_header=False, box=box.HEAVY, min_width=36 ) settings.add_row(t("ui.launch_options_final"), " ".join(sys.argv[1:])) - settings.add_row(t("friction_brake.initial_fb"), str(self.cfg.FRICTION_BRAKE.INITIAL)) + settings.add_row( + t("friction_brake.initial_fb"), str(self.cfg.FRICTION_BRAKE.INITIAL) + ) settings.add_row(t("friction_brake.max_fb"), str(self.cfg.FRICTION_BRAKE.MAX)) - settings.add_row(t("friction_brake.start_delay"), str(self.cfg.FRICTION_BRAKE.START_DELAY)) - settings.add_row(t("friction_brake.increase_delay"), str(self.cfg.FRICTION_BRAKE.INCREASE_DELAY)) - settings.add_row(t("friction_brake.sensitivity"), self.cfg.FRICTION_BRAKE.SENSITIVITY) + settings.add_row( + t("friction_brake.start_delay"), str(self.cfg.FRICTION_BRAKE.START_DELAY) + ) + settings.add_row( + t("friction_brake.increase_delay"), + str(self.cfg.FRICTION_BRAKE.INCREASE_DELAY), + ) + settings.add_row( + t("friction_brake.sensitivity"), self.cfg.FRICTION_BRAKE.SENSITIVITY + ) print(settings) utils.print_usage_box( - t("ui.press_fb", reset=self.cfg.KEY.FRICTION_BRAKE_RESET, quit=self.cfg.KEY.FRICTION_BRAKE_QUIT) + t( + "ui.press_fb", + reset=self.cfg.KEY.FRICTION_BRAKE_RESET, + quit=self.cfg.KEY.FRICTION_BRAKE_QUIT, + ) ) self.window = Window()