2026-03-17 22:47:42 +01:00
|
|
|
"""Command-line interface for the translator module.
|
|
|
|
|
|
|
|
|
|
Provides argument parsing, CLI handlers, and the main entry point
|
|
|
|
|
for the offline translator using Argos Translate.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
import sys
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
|
|
|
|
import python_pkg.word_frequency.translator as _trans
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from collections.abc import Sequence
|
|
|
|
|
|
|
|
|
|
logger = __import__("logging").getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _build_parser() -> argparse.ArgumentParser:
|
|
|
|
|
"""Build the argument parser for the translator CLI."""
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
|
description="Offline translator using Argos Translate.",
|
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
action_group = parser.add_mutually_exclusive_group()
|
|
|
|
|
action_group.add_argument(
|
|
|
|
|
"--list-languages",
|
|
|
|
|
"-l",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="List installed languages",
|
|
|
|
|
)
|
|
|
|
|
action_group.add_argument(
|
|
|
|
|
"--list-available",
|
|
|
|
|
"-L",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="List available language packages for download",
|
|
|
|
|
)
|
|
|
|
|
action_group.add_argument(
|
|
|
|
|
"--download",
|
|
|
|
|
"-d",
|
|
|
|
|
nargs="+",
|
|
|
|
|
metavar="LANG",
|
2026-03-21 17:51:36 +01:00
|
|
|
help=("Download language packs (e.g., --download en es pl)"),
|
2026-03-17 22:47:42 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
input_group = parser.add_mutually_exclusive_group()
|
|
|
|
|
input_group.add_argument(
|
|
|
|
|
"--text",
|
|
|
|
|
"-t",
|
|
|
|
|
type=str,
|
|
|
|
|
help="Single text/word to translate",
|
|
|
|
|
)
|
|
|
|
|
input_group.add_argument(
|
|
|
|
|
"--words",
|
|
|
|
|
"-w",
|
|
|
|
|
nargs="+",
|
|
|
|
|
help="Words to translate",
|
|
|
|
|
)
|
|
|
|
|
input_group.add_argument(
|
|
|
|
|
"--words-file",
|
|
|
|
|
"-W",
|
|
|
|
|
type=str,
|
|
|
|
|
help="File with words to translate (one per line)",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--from",
|
|
|
|
|
"-f",
|
|
|
|
|
dest="from_lang",
|
|
|
|
|
type=str,
|
|
|
|
|
default="en",
|
|
|
|
|
help="Source language code (default: en)",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--to",
|
|
|
|
|
"-T",
|
|
|
|
|
dest="to_lang",
|
|
|
|
|
type=str,
|
|
|
|
|
default="en",
|
|
|
|
|
help="Target language code (default: en)",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--output",
|
|
|
|
|
"-o",
|
|
|
|
|
type=str,
|
|
|
|
|
help="Output file path",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_list_languages() -> int:
|
|
|
|
|
"""Handle --list-languages command."""
|
|
|
|
|
langs = _trans.get_installed_languages()
|
|
|
|
|
if not langs:
|
|
|
|
|
sys.stdout.write("No languages installed.\n")
|
|
|
|
|
sys.stdout.write(
|
|
|
|
|
"Download some with: --download en es pl de fr\n",
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
sys.stdout.write("Installed languages:\n")
|
|
|
|
|
for code, name in sorted(langs):
|
|
|
|
|
sys.stdout.write(f" {code}: {name}\n")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_list_available() -> int:
|
|
|
|
|
"""Handle --list-available command."""
|
|
|
|
|
packages = _trans.get_available_packages()
|
|
|
|
|
if not packages:
|
|
|
|
|
sys.stdout.write(
|
2026-03-21 17:51:36 +01:00
|
|
|
"No packages available (check internet connection).\n",
|
2026-03-17 22:47:42 +01:00
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
sys.stdout.write("Available language packages:\n")
|
|
|
|
|
for from_code, from_name, to_code, to_name in sorted(
|
|
|
|
|
packages,
|
|
|
|
|
):
|
|
|
|
|
sys.stdout.write(
|
2026-03-21 17:51:36 +01:00
|
|
|
f" {from_code} ({from_name}) -> {to_code} ({to_name})\n",
|
2026-03-17 22:47:42 +01:00
|
|
|
)
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_download(lang_codes: list[str]) -> int:
|
|
|
|
|
"""Handle --download command."""
|
|
|
|
|
download_results = _trans.download_languages(lang_codes)
|
2026-03-18 22:20:05 +01:00
|
|
|
success_count = sum(1 for v in download_results.values() if v)
|
2026-03-17 22:47:42 +01:00
|
|
|
sys.stdout.write(
|
2026-03-21 17:51:36 +01:00
|
|
|
f"\nDownloaded {success_count}/{len(download_results)} language pairs.\n",
|
2026-03-17 22:47:42 +01:00
|
|
|
)
|
|
|
|
|
return 0 if success_count > 0 else 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _collect_words(
|
|
|
|
|
args: argparse.Namespace,
|
|
|
|
|
) -> list[str] | None:
|
|
|
|
|
"""Collect words from args. Returns None on error."""
|
|
|
|
|
if args.text:
|
|
|
|
|
return [args.text]
|
|
|
|
|
if args.words:
|
|
|
|
|
return args.words
|
|
|
|
|
if args.words_file:
|
|
|
|
|
try:
|
|
|
|
|
content = _trans.read_file(args.words_file)
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
sys.stderr.write(
|
|
|
|
|
f"Error: File not found: {args.words_file}\n",
|
|
|
|
|
)
|
|
|
|
|
return None
|
2026-03-18 22:20:05 +01:00
|
|
|
return [w.strip() for w in content.splitlines() if w.strip()]
|
2026-03-17 22:47:42 +01:00
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_translation(args: argparse.Namespace) -> int:
|
|
|
|
|
"""Handle the translation action."""
|
|
|
|
|
try:
|
|
|
|
|
results = _trans.translate_words_batch(
|
2026-03-18 22:20:05 +01:00
|
|
|
args.words,
|
|
|
|
|
args.from_lang,
|
|
|
|
|
args.to_lang,
|
2026-03-17 22:47:42 +01:00
|
|
|
)
|
|
|
|
|
except ImportError:
|
|
|
|
|
logger.exception("Translation import error")
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
output = _trans.format_translations(results)
|
|
|
|
|
|
|
|
|
|
if args.output:
|
|
|
|
|
Path(args.output).write_text(output, encoding="utf-8")
|
|
|
|
|
sys.stdout.write(
|
|
|
|
|
f"Translations written to {args.output}\n",
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
sys.stdout.write(output + "\n")
|
|
|
|
|
|
|
|
|
|
if any(not r.success for r in results):
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
|
|
|
"""Main entry point for the translator.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
argv: Command line arguments.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Exit code.
|
|
|
|
|
"""
|
|
|
|
|
parser = _build_parser()
|
|
|
|
|
args = parser.parse_args(argv)
|
|
|
|
|
|
Reduce per-file-ignores by fixing lint violations across codebase
Fix ruff violations in ~15 source files and ~60+ test files to minimize
per-file-ignores in pyproject.toml. Remaining ignores are justified with
comments explaining why each suppression is necessary.
Source fixes: FBT003 (keyword args), S310 (URL validation), SLF001
(private access), T201 (print→logging), C901 (complexity), E501 (line
length), E402 (import order).
Test fixes: SIM117 (combined with), FBT (boolean args), PERF203 (try in
loop), S310/S607 (URLs/executables), E402/E501 (imports/lines), S108
(tmp paths), PLR0913 (too many args), ARG (unused args), ANN (type
annotations), RUF059 (unused unpacked vars), PT019 (fixture naming).
Remaining per-file-ignores (with justifications):
- Tests: ARG, D, PLC0415, PLR2004, S101, SLF001
- music_gen sources: PLC0415 (heavy ML lazy imports)
- moviepy_showcase: PLC0415 (circular dependency)
- generate_images: PLR0913 (matplotlib helpers need many params)
- praca_magisterska_video: E501, E402 (long paths, mpl.use)
2026-03-25 18:58:05 +01:00
|
|
|
if not _trans.check_argos():
|
2026-03-17 22:47:42 +01:00
|
|
|
sys.stderr.write(
|
|
|
|
|
"Error: argostranslate is not installed.\n"
|
|
|
|
|
"Install it with: pip install argostranslate\n",
|
|
|
|
|
)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
if args.list_languages:
|
|
|
|
|
return _handle_list_languages()
|
|
|
|
|
if args.list_available:
|
|
|
|
|
return _handle_list_available()
|
|
|
|
|
if args.download:
|
|
|
|
|
return _handle_download(args.download)
|
|
|
|
|
|
|
|
|
|
words = _collect_words(args)
|
|
|
|
|
if not words:
|
|
|
|
|
if words is not None:
|
|
|
|
|
parser.print_help()
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
args.words = words
|
|
|
|
|
return _handle_translation(args)
|