From 0456b5ce03f59eb512d0d6ecbfb5bf16dfa1a10d Mon Sep 17 00:00:00 2001 From: Simon Dick Date: Tue, 28 Jul 2026 14:45:55 +0100 Subject: [PATCH 1/2] Make CLI re-key prompts localizable; add a catalog structural linter maybe_rekey()'s three re-key confirmation prompts previously matched the user's typed answer against hardcoded English letters/words in C, so a translation could only cosmetically reword them - a translated "yes" would silently be misread as "no" (or worse, as "never ask again", silently persisting an opt-out the user never chose). prompt_letter()/ prompt_quoted_word() now extract the accepted letter(s)/word from the translated prompt itself at runtime, the same self-describing-string technique src/gui/main.c's LBL_* already uses for button mnemonics. On a malformed translation (colliding accept/never-ask letters), it degrades safely rather than misfiring. Adds tools/check_catalog.py (wired into `make check-catalog` and a new CI job) to catch this class of mistake mechanically: placeholder-sequence mismatches between a .cd entry and its translation, missing/colliding GUI button mnemonics, and malformed CLI re-key prompt markers. Also includes a first-pass (AI-generated, unreviewed) German translation under locale/drafts/ - not wired into any build, see its README for the promotion workflow. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01K5bAsAyoYLpkX3rWwrTBPh --- .github/workflows/ci.yml | 12 + Makefile | 11 +- locale/AmiAuth.cd | 23 +- locale/drafts/README.md | 48 ++++ locale/drafts/deutsch.ct | 523 +++++++++++++++++++++++++++++++++++++ src/cli/main.c | 80 +++++- src/core/catalog_strings.h | 6 +- tools/check_catalog.py | 186 +++++++++++++ userdocs/Localization.md | 16 ++ 9 files changed, 890 insertions(+), 15 deletions(-) create mode 100644 locale/drafts/README.md create mode 100644 locale/drafts/deutsch.ct create mode 100644 tools/check_catalog.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a32a810..74ee97b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: build: ${{ steps.filter.outputs.build }} target: ${{ steps.filter.outputs.target }} docs: ${{ steps.filter.outputs.docs }} + catalog: ${{ steps.filter.outputs.catalog }} steps: - uses: actions/checkout@v7 with: @@ -43,6 +44,7 @@ jobs: echo "build=$(match "$always|^src/")" echo "target=$(match "$always|^(src/|tests/copperline/)")" echo "docs=$(match "$always|^(userdocs/|mkdocs\.yml|tools/docs2guide\.py|tools/docs-requirements\.txt)")" + echo "catalog=$(match "$always|^(locale/|tools/check_catalog\.py)")" } >> "$GITHUB_OUTPUT" docs-build: @@ -76,6 +78,16 @@ jobs: - name: CLI smoke test run: make smoke + catalog-lint: + name: Catalog structural checks + runs-on: ubuntu-latest + needs: changes + if: needs.changes.outputs.catalog == 'true' + steps: + - uses: actions/checkout@v7 + - name: Check placeholders, mnemonics, re-key prompt markers + run: make check-catalog + differential: name: Differential fuzz (vs OpenSSL) runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 138357e..84f2561 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ QUIRC_M68K_OBJS := $(patsubst src/qr/%.c,$(BUILD)/qr-m68k/%.o,$(QUIRC_SRCS)) QRCODEGEN_HOST_OBJ := $(BUILD)/qr-host/qrcodegen.o QRCODEGEN_M68K_OBJ := $(BUILD)/qr-m68k/qrcodegen.o -.PHONY: all test cli smoke diff m68k m68k-docker gui gui-docker gui-smoke qr-onhw qr-onhw-docker qr-onhw-smoke arexx-onhw arexx-onhw-docker arexx-onhw-smoke serialtest-m68k serialtest-m68k-docker copperline-smoke pbkdf2-bench asm-bench amissl-bench flexcat flexcat-docker catalog-strings catalog-strings-docker catalog-onhw-smoke catalog-nolib-onhw catalog-nolib-onhw-docker clean +.PHONY: all test cli smoke diff m68k m68k-docker gui gui-docker gui-smoke qr-onhw qr-onhw-docker qr-onhw-smoke arexx-onhw arexx-onhw-docker arexx-onhw-smoke serialtest-m68k serialtest-m68k-docker copperline-smoke pbkdf2-bench asm-bench amissl-bench flexcat flexcat-docker catalog-strings catalog-strings-docker check-catalog catalog-onhw-smoke catalog-nolib-onhw catalog-nolib-onhw-docker clean all: test cli @@ -204,6 +204,15 @@ catalog-strings-docker: $(DOCKER) run --rm --platform linux/amd64 $(DOCKER_USER) -v "$(CURDIR)":/work -w /work \ $(AMIGA_GCC_IMAGE) sh -lc 'make catalog-strings' +# Structural sanity checks on locale/AmiAuth.cd and any .ct translation - +# placeholder consistency, button-mnemonic uniqueness, CLI re-key prompt +# markers (tools/check_catalog.py). Pure Python, no FlexCat/Docker needed. +# Runs against every .ct this repo has, including unreviewed locale/drafts/ +# ones - these are purely structural checks, not a translation-quality +# review, so there's no reason to exempt a draft from them. +check-catalog: + python3 tools/check_catalog.py locale/AmiAuth.cd $(wildcard locale/*.ct) $(wildcard locale/drafts/*.ct) + # --- Headless GUI smoke test: boot WB 3.2 under Copperline, render AmiAuthGUI -- # Boots an A1200/OS 3.2 under native Copperline, auto-launches AmiAuthGUI, and # asserts the ReAction window rendered (screenshot in build/gui-smoke/). No VNC diff --git a/locale/AmiAuth.cd b/locale/AmiAuth.cd index 15e96f0..4b0b355 100644 --- a/locale/AmiAuth.cd +++ b/locale/AmiAuth.cd @@ -30,6 +30,27 @@ ; translation MUST keep exactly one '_' immediately before an ASCII letter, ; ideally one that doesn't collide with another button's mnemonic. ; +; Same idea for the CLI's three re-key confirmation prompts +; (MSG_CLI_REKEY_STRENGTHEN_PROMPT/MSG_CLI_REKEY_LOWER_PROMPT): the letter +; typed to accept/decline is parsed out of the string itself at runtime - +; the *first* "(x)" in the string is the accept letter, and for +; MSG_CLI_REKEY_STRENGTHEN_PROMPT the *third* "(x)" is the "never ask +; again" letter (the second, "no", is never checked - typing anything else +; falls through to "no"). A translation is free to pick its own letters +; (they need not be the English y/N/v, and need not be the first letter of +; the translated word) - just keep exactly one "(x)" per choice, in the +; same left-to-right order, and make the accept/never-ask letters distinct +; from each other. If they DO collide (both "(x)" use the same letter), the +; code degrades safely rather than misfiring: "never ask again" - which +; writes a persisted opt-out pref - simply becomes unreachable that run, so +; a mistranslation can only under-deliver (lose the never-ask shortcut), +; never silently write a preference the user didn't ask for. Still worth +; getting right, though - distinct letters give the user the full choice. +; MSG_CLI_CONFIRM_YES_PROMPT works the same way but with a whole word +; instead of a letter: the text between the first pair of ' quotes is what +; the user must type back - translate that word freely, the check follows +; whatever is inside the quotes. +; #language english #version 0 ; @@ -140,7 +161,7 @@ MSG_CLI_REKEY_SLOW_NOTICE (30//) %s: unlock took ~%lus; this vault was tuned for faster hardware.\n ; MSG_CLI_REKEY_LOWER_PROMPT (31//) -Re-key LOWER for quicker unlocks here? This REDUCES security. [y/N] +Re-key LOWER for quicker unlocks here? This REDUCES security. [(y)es/(N)o] ; MSG_CLI_CONFIRM_YES_PROMPT (32//) Type 'yes' to confirm: diff --git a/locale/drafts/README.md b/locale/drafts/README.md new file mode 100644 index 0000000..9e09980 --- /dev/null +++ b/locale/drafts/README.md @@ -0,0 +1,48 @@ +# Draft translations - NOT reviewed, NOT installed by anything + +Everything in this directory is a **machine-generated first pass** at a +translation of `locale/AmiAuth.cd`, produced by an AI coding assistant with +no native-speaker review. Nothing here is built, tested, or referenced by +any `make` target - that's deliberate, so a draft can never end up in a +release or get picked up by AmigaOS's catalog search just by existing in +the tree. + +**Do not install a `.ct` file from here as-is.** Treat it the same as any +other unreviewed contribution: it needs a native (or at least fluent) +speaker to check it, especially the security-relevant prompts (unencrypted +storage, no-RNG, re-key strengthen/weaken) where a subtly wrong translation +could mislead someone about what they're agreeing to. + +## Promoting a draft to a real translation + +1. A fluent speaker reviews the `.ct` file line by line against + `locale/AmiAuth.cd` (the English source of truth) - fixing wording, + register/tone, and any button-mnemonic collisions (see the underscore + note in `AmiAuth.cd`'s header). +2. Build and smoke-test it locally: + ``` + FlexCat ../AmiAuth.cd yourlanguage.ct CATALOG AmiAuth.catalog + ``` + then drop the result at `LOCALE:Catalogs//AmiAuth.catalog` and + exercise the CLI/GUI to see it in practice (see + [Localization](../../userdocs/Localization.md#installing-a-translation)). +3. Move the reviewed `.ct` out of `drafts/` and open a pull request per the + normal [translator workflow](../../userdocs/Localization.md#contributing-a-translation). + +## The three CLI re-key prompts are now fully localizable + +`maybe_rekey()`'s three re-key confirmation prompts in `src/cli/main.c` +originally parsed the user's typed answer against hardcoded English +letters/words in C, found and fixed while drafting this translation: +`prompt_letter()`/`prompt_quoted_word()` (`src/cli/main.c`) now extract the +accepted letter(s)/word straight out of the *translated* prompt at runtime +- the same self-describing-string technique `src/gui/main.c`'s `LBL_*` +already uses for button mnemonics. See the convention documented in +`locale/AmiAuth.cd`'s header (search for "MSG_CLI_REKEY_STRENGTHEN_PROMPT"): +a translation is free to pick its own letters/word (they need not be +English `y`/`N`/`v`/`yes`, and a letter need not be the first letter of the +translated word) - just keep exactly one `"(x)"` per choice, in the same +left-to-right order, with the accept and never-ask letters distinct from +each other, and the confirm word inside `'...'` quotes. `deutsch.ct` below +already uses real German letters/word (`j`/`N`/`i`, `'ja'`) as a worked +example. diff --git a/locale/drafts/deutsch.ct b/locale/drafts/deutsch.ct new file mode 100644 index 0000000..3e85048 --- /dev/null +++ b/locale/drafts/deutsch.ct @@ -0,0 +1,523 @@ +; deutsch.ct - DRAFT German translation of locale/AmiAuth.cd. +; +; MACHINE-GENERATED FIRST PASS, NOT REVIEWED BY A NATIVE SPEAKER. +; See locale/drafts/README.md before using or building this file - it is +; not wired into any build target and must not be installed as-is. +; +; Terminology used throughout, for reviewer consistency: +; vault -> Tresor passphrase -> Passphrase (not Passwort) +; account -> Konto secret -> Secret (loanword, RFC term) +; issuer -> Aussteller label -> Bezeichnung +; re-key -> neu verschlüsseln / Neuverschlüsselung +; always-unlocked -> immer entsperrt +; Tone: neutral/impersonal (no "Du"/"Sie" address needed for most strings; +; imperative infinitive for buttons, e.g. "Hinzufügen" not "Füge hinzu"). +; +##version $VER: AmiAuth.catalog 1.0 (28.07.2026) +##language deutsch +##codeset 0 +; +; --- CLI (src/cli/main.c) --------------------------------------------- +; +MSG_CLI_ALREADY_EXISTS +%s: %s existiert bereits\n +; +MSG_CLI_ADDED +%s:%s hinzugefügt\n +; +MSG_CLI_REMOVED +'%s' entfernt\n +; +MSG_CLI_USAGE_RUNAS +Aufruf als '%s ...' ('%s ?' zeigt die Argumentvorlage):\n +; +; --- GUI (src/gui/main.c): account list column headers ----------------- +; +MSG_GUI_COL_ACCOUNT +Konto +; +MSG_GUI_COL_CODE +Code +; +MSG_GUI_COL_LEFT +Rest +; +; --- GUI: requester text ------------------------------------------------- +; +MSG_GUI_CANNOT_OPEN_VAULT +Der Tresor kann nicht geöffnet werden:\n%s +; +MSG_GUI_BAD_URI +Das ist keine gültige otpauth://-URI. +; +MSG_GUI_BAD_SECRET +Das ist weder eine otpauth://-URI noch ein Base32-Secret. +; +MSG_GUI_NO_QR +In diesem Bild wurde kein otpauth-QR-Code gefunden. +; +MSG_GUI_SAVE_FAILED +Der Tresor konnte nicht gespeichert werden. +; +MSG_GUI_REKEY_DONE +Tresor wurde für diesen Rechner neu verschlüsselt. +; +MSG_GUI_REKEY_FAILED +Neuverschlüsselung fehlgeschlagen; der Tresor bleibt unverändert. +; +; --- GUI: common requester button labels ---------------------------------- +; +MSG_GUI_OK +OK +; +MSG_GUI_CANCEL +Abbrechen +; +; --- CLI: vault_err()'s own strings, wrapped by MSG_CLI_ERR below --------- +; +MSG_ERR_IO +die Tresordatei kann nicht gelesen/geschrieben werden +; +MSG_ERR_FORMAT +keine gültige Tresordatei +; +MSG_ERR_AUTH +falsche Passphrase, oder die Datei wurde manipuliert +; +MSG_ERR_LOCKED +Tresor ist gesperrt +; +MSG_ERR_FULL +Tresor ist voll +; +MSG_ERR_RANGE +kein solches Konto +; +MSG_ERR_UNKNOWN +Fehler +; +; --- CLI: generic error wrapper ("AmiAuth" + vault_err()/other message) -- +; +MSG_CLI_ERR +%s: %s\n +; +MSG_CLI_WARN_HOTP_PERSIST +%s: Warnung: HOTP-Zähler konnte nicht gespeichert werden (%s)\n +; +; --- CLI: passphrase prompts ---------------------------------------------- +; +MSG_CLI_PROMPT_PASSPHRASE +Passphrase: +; +MSG_CLI_NO_TTY +%s: dieser Tresor ist verschlüsselt; bitte in einem interaktiven Terminal ausführen, oder für Skripte einen immer entsperrten Tresor verwenden\n +; +MSG_CLI_NO_RNG_SAVE +%s: keine sichere Zufallsquelle vorhanden, um einen verschlüsselten Tresor zu speichern (Phase 4)\n +; +; --- CLI: adaptive re-key (maybe_rekey) ----------------------------------- +; +; REVIEWER: the letter(s) in "(x)" and the word in '...' below are read back +; out of this translated string at runtime (locale/AmiAuth.cd's header has +; the full convention) - j/N/i and 'ja' are just this draft's choice, freely +; change them, just keep exactly one "(x)" per choice in the same +; left-to-right order, with the accept and never-ask letters distinct. +MSG_CLI_REKEY_STRENGTHEN_PROMPT +Dieser Rechner ist deutlich schneller als der, der diesen Tresor gesichert hat.\nJetzt stärken? [(j)a/(N)ein/n(i)e hier fragen] +; +MSG_CLI_REKEY_SLOW_NOTICE +%s: Entsperren dauerte ~%lus; dieser Tresor wurde für schnellere Hardware eingestellt.\n +; +MSG_CLI_REKEY_LOWER_PROMPT +Für schnelleres Entsperren hier ABSCHWÄCHEN? Dies VERRINGERT die Sicherheit. [(j)a/(N)ein] +; +MSG_CLI_CONFIRM_YES_PROMPT +Zur Bestätigung 'ja' eingeben: +; +MSG_CLI_REKEYED +%s: neu verschlüsselt mit %lu Iterationen\n +; +MSG_CLI_REKEY_FAILED +%s: Neuverschlüsselung fehlgeschlagen; Tresor unverändert\n +; +; --- CLI: CODE --------------------------------------------------------- +; +MSG_CLI_BAD_SECRET +%s: ungültiges oder leeres Base32-Secret\n +; +MSG_CLI_SECONDS_REMAINING +(noch %u Sekunden)\n +; +; --- CLI: CLOCK/SYNC/OFFSET/NUDGE --------------------------------------- +; +MSG_CLI_CLOCK_SYNCED +synchronisiert (grün) +; +MSG_CLI_CLOCK_MANUAL +Offset gesetzt (gelb) +; +MSG_CLI_CLOCK_UNVERIFIED +nicht geprüft (rot) +; +MSG_CLI_CLOCK_OFFSET_LINE +UTC-Offset : %+ld Sekunden (%+ld Min)\n +; +MSG_CLI_CLOCK_STATUS_LINE +Status : %s\n +; +MSG_CLI_CLOCK_CORRECTED_LINE +korrigiert : %04d-%02d-%02d %02d:%02d:%02d UTC\n +; +MSG_CLI_QUERYING +Frage %s ab ...\n +; +MSG_CLI_SYNC_FAILED +%s: SNTP-Abgleich fehlgeschlagen (kein TCP/IP-Stack oder keine Antwort von %s)\n +; +MSG_CLI_OFFSET_SAVE_FAILED +%s: Offset konnte nicht gespeichert werden\n +; +; --- CLI: INIT ----------------------------------------------------------- +; +MSG_CLI_INIT_PASS_PROMPT +Neue Passphrase (leer lassen für einen immer entsperrten Tresor): +; +MSG_CLI_INIT_NO_TTY +%s: INIT benötigt ein interaktives Terminal (für einen nicht-interaktiven, immer entsperrten Tresor 'INIT --open' verwenden)\n +; +MSG_CONFIRM_PASSPHRASE +Passphrase bestätigen: +; +MSG_CLI_PASS_MISMATCH +%s: Passphrasen stimmen nicht überein\n +; +MSG_CLI_INIT_NO_RNG +%s: keine sichere Zufallsquelle für einen verschlüsselten Tresor (Phase 4); mit 'INIT --open' einen immer entsperrten Tresor anlegen\n +; +MSG_CLI_KDF_ITERATIONS +%s: KDF-Iterationen = %lu\n +; +MSG_CLI_CREATED +Tresor (%s) angelegt unter %s\n +; +MSG_CLI_ALWAYS_UNLOCKED +immer entsperrt +; +MSG_CLI_ENCRYPTED +verschlüsselt +; +; --- CLI: GUI forwarding (try_forward) ------------------------------------ +; +MSG_CLI_GUI_LOCKED +%s: der Tresor der laufenden GUI ist gesperrt; bitte dort zuerst entsperren\n +; +MSG_CLI_NO_ACCOUNT +%s: kein Konto passend zu '%s'\n +; +MSG_CLI_VAULT_FULL +%s: der Tresor ist voll (max. 64 Konten)\n +; +MSG_CLI_BAD_OTPURI +%s: keine gültige otpauth://-URI\n +; +MSG_CLI_GUI_SAVEFAIL +%s: in der GUI übernommen, aber das erneute Speichern ist fehlgeschlagen\n +; +MSG_CLI_URI_TOO_LONG +%s: die URI dieses Kontos ist zu lang, um als QR-Code kodiert zu werden\n +; +MSG_CLI_GUI_UNKNOWN +%s: die laufende GUI konnte das nicht verarbeiten\n +; +; --- CLI: ADD -------------------------------------------------------------- +; +MSG_CLI_PARSE_FAILED +%s: otpauth://-URI konnte nicht gelesen werden\n +; +MSG_CLI_ADDED_NOISSUER +%s hinzugefügt\n +; +MSG_CLI_BAD_BASE32 +%s: das sieht nicht wie ein Base32-Secret aus\n +; +; --- CLI: SHOW / usage ----------------------------------------------------- +; +MSG_CLI_NO_GUI +%s: keine laufende GUI zum Anzeigen\n +; +MSG_CLI_TAGLINE +%s - TOTP/HOTP-Authentifizierung für AmigaOS\n\n +; +MSG_CLI_ENCRYPTED_NOTE +Verschlüsselte Tresore fragen die Passphrase im Terminal ab.\n +; +; --- GUI: main menu (g_menu[]) --------------------------------------------- +; +MSG_GUI_MENU_PROJECT +Projekt +; +MSG_GUI_QUIT +Beenden +; +MSG_GUI_MENU_ACCOUNT +Konto +; +MSG_GUI_MENU_ADD_CLIP +Aus Zwischenablage hinzufügen +; +MSG_GUI_MENU_ADD_TYPE +Hinzufügen (URI/Secret eingeben)... +; +MSG_GUI_MENU_ADD_QR +Aus QR-Bild hinzufügen... +; +MSG_GUI_MENU_EDIT +Auswahl bearbeiten... +; +MSG_GUI_MENU_COPY +Code kopieren +; +MSG_GUI_MENU_SHOW_QR +QR-Code anzeigen... +; +MSG_GUI_MENU_REMOVE +Auswahl entfernen... +; +; --- GUI: toolbar button mnemonics (LBL_*, see the underscore note above) - +; REVIEWER: the six mnemonic letters below (H/B/E/K/M/P) are chosen to be +; distinct from each other, since all six buttons are visible together in +; the same window - keep them distinct if you change the wording. +; +MSG_GUI_BTN_ADD +_Hinzufügen +; +MSG_GUI_BTN_EDIT +_Bearbeiten +; +MSG_GUI_BTN_REMOVE +_Entfernen +; +MSG_GUI_BTN_COPY +_Kopieren +; +MSG_GUI_BTN_NUDGE_DOWN +_M -10s +; +MSG_GUI_BTN_NUDGE_UP +_P +10s +; +; --- GUI: copy-code button's transient states (no mnemonic) --------------- +; +MSG_GUI_COPY_PLAIN +Kopieren +; +MSG_GUI_COPIED +Kopiert +; +; --- GUI: clock-status line -------------------------------------------- +; +MSG_GUI_CLOCK_SYNCED +synchron +; +MSG_GUI_CLOCK_MANUAL +manuell +; +MSG_GUI_CLOCK_UNVERIFIED +ungeprüft +; +MSG_GUI_CLOCK_LINE_SHORT +Uhr: %s +; +MSG_GUI_CLOCK_LINE_FULL +Uhr: %s %s%lu:%02lu +; +; --- GUI: adaptive re-key (gui_maybe_rekey) ------------------------------- +; +MSG_GUI_REKEY_STRENGTHEN_BODY +Dieser Rechner ist deutlich schneller als der, der diesen Tresor gesichert hat.\nJetzt stärken (Neuverschlüsselung mit mehr PBKDF2-Iterationen)? +; +MSG_GUI_REKEY_STRENGTHEN_BTNS +Stärken|Jetzt nicht|Hier nie +; +MSG_GUI_REKEY_SLOW_BODY +Entsperren dauerte etwa %lu Sekunden; dieser Tresor wurde für schnellere Hardware eingestellt.\nHier für schnelleres Entsperren ABSCHWÄCHEN? Dies VERRINGERT die Sicherheit. +; +MSG_GUI_REKEY_LOWER_BTNS +Abschwächen|Abbrechen +; +MSG_GUI_REKEY_CONFIRM_BODY +Sicherheit dieses Tresors wirklich verringern? +; +MSG_GUI_REKEY_REDUCE_BTNS +Verringern|Abbrechen +; +; --- GUI: add account (uri/secret, shared gui_add_uri/gui_add_secret) ---- +; +MSG_GUI_VAULT_FULL +Der Tresor ist voll (max. 64 Konten). +; +; --- GUI: QR import (do_add_qr_impl) -------------------------------------- +; +MSG_GUI_IMG_TOOBIG +Dieses Bild ist zu groß, um decodiert zu werden. +; +MSG_GUI_QR_UNAVAIL +Der QR-Import benötigt datatypes.library. +; +MSG_GUI_IMG_NOMEM +Nicht genug Speicher, um dieses Bild zu laden. +; +MSG_GUI_IMG_READ_FAILED +Dieses Bild konnte nicht gelesen werden (benötigt picture.datatype). +; +MSG_GUI_TITLE_DECODING +%s - QR-Bild wird decodiert... +; +MSG_GUI_QR_FILE_TITLE +QR-Bild auswählen +; +; --- GUI: unlock prompt (passphrase_request title) ------------------------ +; +MSG_GUI_TITLE_UNLOCK +%s - Entsperren +; +; --- GUI: first-run vault creation ----------------------------------------- +; +MSG_GUI_CHOOSE_LOCATION +Speicherort für den Tresor wählen +; +MSG_GUI_WELCOME +Willkommen bei %s!\n\nEs gibt noch keinen Tresor - er wird angelegt unter\n%s +; +MSG_GUI_CREATE_VAULT_BTNS_DEFER +Tresor anlegen...|Jetzt nicht +; +MSG_GUI_CREATE_VAULT_BTNS_QUIT +Tresor anlegen...|Beenden +; +MSG_GUI_NEW_PASS_PROMPT +Neue Passphrase (leer = immer entsperrt): +; +MSG_GUI_STORE_UNENCRYPTED_BODY +Tresor UNVERSCHLÜSSELT speichern?\n\nOhne Passphrase gibt es keinen Schutz im Ruhezustand:\njeder mit Zugriff auf die Datei kann Ihre Secrets lesen.\n(Eine Passphrase kann später hinzugefügt werden.) +; +MSG_GUI_STORE_UNENCRYPTED_BTNS +Unverschlüsselt speichern|Zurück +; +MSG_GUI_PASS_MISMATCH +Die Passphrasen stimmen nicht überein. +; +MSG_GUI_TRY_AGAIN +Erneut versuchen +; +MSG_GUI_NO_RNG_BODY +Es ist keine sichere Zufallsquelle verfügbar, daher kann\nkein verschlüsselter Tresor sicher angelegt werden. +; +MSG_GUI_CANNOT_WRITE_BODY +Der Tresor kann nicht geschrieben werden nach\n%s\n\nAnderen Speicherort wählen? +; +MSG_GUI_CHOOSE_LOCATION_BTNS +Ort wählen...|Beenden +; +MSG_GUI_CREATE_FAILED +Anlegen des Tresors fehlgeschlagen. +; +; --- GUI: unlock loop (gui_open_vault) -------------------------------- +; +MSG_GUI_ENTER_PASS +Passphrase eingeben: +; +MSG_GUI_WRONG_PASS +Falsche Passphrase - erneut versuchen: +; +MSG_GUI_CANNOT_OPEN_PRINTF +%s: Tresor kann nicht geöffnet werden (%ld)\n +; +; --- GUI: add account form (uri_request) ------------------------------ +; +MSG_GUI_URI_PASTE_LABEL +otpauth://-URI oder Base32-Secret einfügen oder eingeben: +; +MSG_GUI_TITLE_ADD_ACCOUNT +%s - Konto hinzufügen +; +; --- GUI: edit account form (edit_request) ------------------------------ +; +MSG_GUI_LABEL_ISSUER +Aussteller: +; +MSG_GUI_LABEL_LABEL +Bezeichnung: +; +MSG_GUI_LABEL_DIGITS +Stellen: +; +MSG_GUI_LABEL_PERIOD +Intervall: +; +MSG_GUI_TITLE_EDIT_ACCOUNT +%s - Konto bearbeiten +; +MSG_GUI_LABEL_REQUIRED_STEAM +Bezeichnung ist erforderlich. +; +MSG_GUI_LABEL_REQUIRED_FULL +Bezeichnung ist erforderlich; Stellen müssen 6-8, Intervall 1-86400 sein. +; +; --- GUI: QR export display (qr_display) -------------------------------- +; +MSG_GUI_QR_BUILD_FAILED +Für dieses Konto konnte kein QR-Code erstellt werden. +; +MSG_GUI_QR_TOO_LONG +Die URI dieses Kontos ist zu lang, um als QR-Code kodiert zu werden. +; +MSG_GUI_QR_DISPLAY_FAILED +Die QR-Anzeige konnte nicht erstellt werden. +; +MSG_GUI_QR_WINDOW_FAILED +Das QR-Fenster konnte nicht geöffnet werden. +; +MSG_GUI_TITLE_QR_ISSUER +%s - QR: %s:%s +; +MSG_GUI_TITLE_QR_NOISSUER +%s - QR: %s +; +; --- GUI: bare-secret add form (secret_meta_request) ---------------------- +; +MSG_GUI_SECRET_META_HINT +Ein reines Secret trägt keinen Namen - für wen ist dieses Konto? +; +MSG_GUI_ADD_BTN +Hinzufügen +; +MSG_GUI_ISSUER_LABEL_REQUIRED +Aussteller und Bezeichnung sind beide erforderlich. +; +; --- GUI: commodity broker (Exchange) ------------------------------------- +; +MSG_GUI_CX_DESCR +TOTP/HOTP-Authentifizierung +; +; --- GUI: remove confirmation --------------------------------------------- +; +MSG_GUI_REMOVE_CONFIRM +%s entfernen? +; +MSG_GUI_REMOVE_BTNS +Entfernen|Abbrechen +; +; --- GUI: startup failures (open_libs/main) ------------------------------- +; +MSG_GUI_NEEDS_INTUITION +benötigt intuition.library / utility.library v37+ (OS 2.04) +; +MSG_GUI_NEEDS_REACTION +benötigt ReAction/ClassAct-Klassen (window/layout/listbrowser/fuelgauge/button) +; +MSG_GUI_WINDOW_CREATE_FAILED +%s: Fenster konnte nicht erstellt werden\n +; +MSG_GUI_WINDOW_OPEN_FAILED +%s: Fenster konnte nicht geöffnet werden\n diff --git a/src/cli/main.c b/src/cli/main.c index 40f7d29..b8dd1d4 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -296,6 +296,44 @@ static const char *vault_err(vault_result rc) static vault_result save_vault(const vault *v, const char *path); +/* The Nth "(x)" single-letter mnemonic in a translated prompt (the same + * "(y)es/(N)o/ne(v)er" convention already visible in the English text) - + * so the accepted keystroke always matches what the catalog actually says, + * the same self-describing-string technique src/gui/main.c's LBL_* uses for + * its '_'-marked button mnemonics. 1-based nth. Returns the lowercased + * letter, or dflt if the Nth "(x)" isn't present (e.g. a mistranslation + * dropped it - falls back to the English default rather than being unusable). */ +static int prompt_letter(const char *s, int nth, int dflt) +{ + int n = 0; + for (; *s; s++) { + if (s[0] == '(' && s[1] != '\0' && s[1] != ')' && s[2] == ')') { + if (++n == nth) return s[1] | 0x20; + s += 2; + } + } + return dflt; +} + +/* The word between the first pair of '...' quotes in a translated prompt + * (e.g. "Type 'yes' to confirm:") - what the user must type back. Copies + * into buf (cap bytes); returns 0 on success, -1 if no quoted word was + * found (buf untouched - caller supplies its own English fallback). */ +static int prompt_quoted_word(const char *s, char *buf, size_t cap) +{ + const char *start = strchr(s, '\''); + const char *end; + size_t len; + if (!start) return -1; + end = strchr(start + 1, '\''); + if (!end || end == start + 1) return -1; + len = (size_t)(end - start - 1); + if (len >= cap) len = cap - 1; + memcpy(buf, start + 1, len); + buf[len] = '\0'; + return 0; +} + /* After a successful unlock we know the stored iteration count and how long the * KDF took, so we can tell whether this machine is much faster or slower than the * one that secured the vault, and offer to re-key (recalibrate to ~1s here and @@ -307,6 +345,7 @@ static void maybe_rekey(vault *v, const char *path, const char *pass, uint32_t u uint32_t ideal; char prefbuf[8], line[16]; char prompt[160]; /* MSG() text + the trailing space FlexCat trims */ + const char *ptext; if (v->cipher != VAULT_CIPHER_CHACHA20) return; /* encrypted only */ if (g_no_rekey || unlock_ms == 0) return; /* opted out / no timer */ @@ -316,25 +355,46 @@ static void maybe_rekey(vault *v, const char *path, const char *pass, uint32_t u ideal = vault_calibrate_iterations(v->iterations, unlock_ms); if (unlock_ms < KDF_TARGET_MS / 8 && ideal > v->iterations) { - /* Much faster machine -> offer to strengthen (safe; one confirm). */ - int c; - sprintf(prompt, "%s ", MSG(MSG_CLI_REKEY_STRENGTHEN_PROMPT)); + /* Much faster machine -> offer to strengthen (safe; one confirm). + * Accept/never-ask letters come from the prompt itself (1st/3rd + * "(x)"), not hardcoded - see the MSG_CLI_REKEY_STRENGTHEN_PROMPT + * note in locale/AmiAuth.cd. */ + int c, yes_c, never_c; + ptext = MSG(MSG_CLI_REKEY_STRENGTHEN_PROMPT); + yes_c = prompt_letter(ptext, 1, 'y'); + never_c = prompt_letter(ptext, 3, 'v'); + sprintf(prompt, "%s ", ptext); if (cli_readline(prompt, line, sizeof line) != 0) return; c = line[0] | 0x20; - if (c == 'v') { prefs_set("rekey", "off"); return; } - if (c != 'y') return; + /* never_c != yes_c guards a mistranslation that gave both the same + * letter: without it, a "yes" keypress would always be swallowed + * into "never ask again" (this check runs first) and silently + * write a persisted opt-out the user never actually chose. On a + * collision, "never" is simply unreachable this run - safer than + * either letter winning unpredictably. */ + if (c == never_c && never_c != yes_c) { prefs_set("rekey", "off"); return; } + if (c != yes_c) return; } else if (unlock_ms > KDF_TARGET_MS * 8) { - /* Much slower machine -> offer to speed up, but this weakens it: friction. */ + /* Much slower machine -> offer to speed up, but this weakens it: + * friction. Same self-describing-prompt convention as above; the + * confirm word comes from the quoted word in its own prompt. */ + int yes_c; + char confirm_word[32]; fprintf(stderr, MSG(MSG_CLI_REKEY_SLOW_NOTICE), "AmiAuth", (unsigned long)((unlock_ms + 500) / 1000)); - sprintf(prompt, "%s ", MSG(MSG_CLI_REKEY_LOWER_PROMPT)); + ptext = MSG(MSG_CLI_REKEY_LOWER_PROMPT); + yes_c = prompt_letter(ptext, 1, 'y'); + sprintf(prompt, "%s ", ptext); if (cli_readline(prompt, line, sizeof line) != 0 - || (line[0] | 0x20) != 'y') + || (line[0] | 0x20) != yes_c) return; - sprintf(prompt, "%s ", MSG(MSG_CLI_CONFIRM_YES_PROMPT)); + ptext = MSG(MSG_CLI_CONFIRM_YES_PROMPT); + if (prompt_quoted_word(ptext, confirm_word, sizeof confirm_word) != 0) + strcpy(confirm_word, "yes"); /* malformed translation: fall back */ + sprintf(prompt, "%s ", ptext); if (cli_readline(prompt, line, sizeof line) != 0 - || !ci_streq(line, "yes")) + || !ci_streq(line, confirm_word)) return; } else { return; /* within range; nothing to do */ diff --git a/src/core/catalog_strings.h b/src/core/catalog_strings.h index bfc5df8..ecc234f 100644 --- a/src/core/catalog_strings.h +++ b/src/core/catalog_strings.h @@ -235,7 +235,7 @@ extern struct Library *LocaleBase; #define MSG_CLI_NO_RNG_SAVE_STR "%s: no secure random source to save an encrypted vault (Phase 4)\n" #define MSG_CLI_REKEY_STRENGTHEN_PROMPT_STR "This machine is much faster than the one that secured this vault.\nStrengthen it now? [(y)es/(N)o/ne(v)er ask here]" #define MSG_CLI_REKEY_SLOW_NOTICE_STR "%s: unlock took ~%lus; this vault was tuned for faster hardware.\n" -#define MSG_CLI_REKEY_LOWER_PROMPT_STR "Re-key LOWER for quicker unlocks here? This REDUCES security. [y/N]" +#define MSG_CLI_REKEY_LOWER_PROMPT_STR "Re-key LOWER for quicker unlocks here? This REDUCES security. [(y)es/(N)o]" #define MSG_CLI_CONFIRM_YES_PROMPT_STR "Type 'yes' to confirm:" #define MSG_CLI_REKEYED_STR "%s: re-keyed to %lu iterations\n" #define MSG_CLI_REKEY_FAILED_STR "%s: re-key failed; vault left unchanged\n" @@ -586,8 +586,8 @@ static const char AmiAuth_Block[] = MSG_CLI_REKEY_STRENGTHEN_PROMPT_STR "" "\x00\x00\x00\x1e" "\x00\x42" MSG_CLI_REKEY_SLOW_NOTICE_STR "\x00" - "\x00\x00\x00\x1f" "\x00\x44" - MSG_CLI_REKEY_LOWER_PROMPT_STR "\x00" + "\x00\x00\x00\x1f" "\x00\x4a" + MSG_CLI_REKEY_LOWER_PROMPT_STR "" "\x00\x00\x00\x20" "\x00\x16" MSG_CLI_CONFIRM_YES_PROMPT_STR "" "\x00\x00\x00\x21" "\x00\x20" diff --git a/tools/check_catalog.py b/tools/check_catalog.py new file mode 100644 index 0000000..77a2224 --- /dev/null +++ b/tools/check_catalog.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +"""check_catalog.py - structural sanity checks for locale/AmiAuth.cd and any +.ct translation, catching the class of bug a translator can introduce without +ever reading src/cli/main.c or src/gui/main.c: a dropped/reordered printf +placeholder, a missing button mnemonic, or two shortcuts sharing one letter. + +This is NOT a translation-quality check - wording/tone/register still needs a +human fluent in the language. It only checks structure: the same mechanical +things that would otherwise surface as a garbled string, a silently-dead +keyboard shortcut, or (for the CLI re-key prompts) a mismatch between what a +prompt displays and what it actually accepts. + +Usage: + tools/check_catalog.py locale/AmiAuth.cd [translation.ct ...] + +The first argument is always the .cd (the English source of truth, and the +baseline every .ct is checked against); every following argument is a .ct +translation. Exits 1 if any FAIL-level issue is found in any file; WARN-level +issues (see MSG_GUI_BTN_*/MSG_CLI_REKEY_STRENGTHEN_PROMPT below) are printed +but don't fail the build - the code has a documented runtime fallback for them. + +Parsing note: this assumes THIS repo's own .cd/.ct convention - one line of +body text per entry, terminated by a lone ';' line (see locale/AmiAuth.cd) - +not FlexCat's full multi-line grammar, which this project doesn't use. +""" +import re +import sys + +NAME_RE = re.compile(r'^(MSG_[A-Za-z0-9_]+)\b') +PLACEHOLDER_RE = re.compile(r'%[-+ #0]*[0-9]*l?[a-zA-Z]') +PAREN_LETTER_RE = re.compile(r'\(([^()])\)') + +# Entries whose body must carry exactly one '_' keyboard mnemonic, +# with all such letters distinct within one file (no runtime collision +# protection for these - the FIRST match in the GUI's if/else-if VANILLAKEY +# chain silently wins, and the loser's shortcut becomes keyboard-unreachable). +MNEMONIC_PREFIX = 'MSG_GUI_BTN_' + +# The three CLI re-key prompts (src/cli/main.c's prompt_letter()/ +# prompt_quoted_word()) and their structural requirements. +REKEY_STRENGTHEN = 'MSG_CLI_REKEY_STRENGTHEN_PROMPT' # >=3 "(x)"; 1st=yes, 3rd=never +REKEY_LOWER = 'MSG_CLI_REKEY_LOWER_PROMPT' # >=1 "(x)"; 1st=yes +REKEY_CONFIRM = 'MSG_CLI_CONFIRM_YES_PROMPT' # one 'word' to type back + + +def parse_entries(path): + """.cd/.ct -> {NAME: body_text}, in this repo's one-line-body convention.""" + with open(path, encoding='latin-1') as f: + lines = f.read().splitlines() + entries = {} + i = 0 + while i < len(lines): + stripped = lines[i].strip() + if not stripped or stripped.startswith(';') or stripped.startswith('#'): + i += 1 + continue + m = NAME_RE.match(stripped) + if not m: + i += 1 + continue + name = m.group(1) + i += 1 + if i >= len(lines): + break + entries[name] = lines[i] + i += 1 + while i < len(lines) and lines[i].strip() != ';': + i += 1 + i += 1 # skip the ';' terminator + return entries + + +def placeholders(text): + return PLACEHOLDER_RE.findall(text) + + +def paren_letters(text): + return [m.group(1) for m in PAREN_LETTER_RE.finditer(text)] + + +def quoted_word(text): + start = text.find("'") + if start < 0: + return None + end = text.find("'", start + 1) + if end <= start + 1: + return None + return text[start + 1:end] + + +def check_mnemonics(label, entries, fails, warns): + seen = {} + for name, text in entries.items(): + if not name.startswith(MNEMONIC_PREFIX): + continue + marks = [i for i, c in enumerate(text) if c == '_'] + if len(marks) != 1: + fails.append(f"{label}: {name} has {len(marks)} '_' markers " + f"(need exactly 1): {text!r}") + continue + pos = marks[0] + if pos + 1 >= len(text) or not text[pos + 1].isalpha(): + fails.append(f"{label}: {name}'s '_' isn't followed by a letter: {text!r}") + continue + letter = text[pos + 1].lower() + if letter in seen: + fails.append(f"{label}: {name} and {seen[letter]} both use mnemonic " + f"'{letter}' - one becomes keyboard-unreachable " + f"(first match wins in the VANILLAKEY handler)") + else: + seen[letter] = name + + +def check_rekey_prompts(label, entries, fails, warns): + if REKEY_STRENGTHEN in entries: + text = entries[REKEY_STRENGTHEN] + letters = paren_letters(text) + if len(letters) < 3: + fails.append(f"{label}: {REKEY_STRENGTHEN} has {len(letters)} \"(x)\" " + f"markers, need >=3 (accept=1st, never-ask=3rd): {text!r}") + else: + yes_c, never_c = letters[0].lower(), letters[2].lower() + if yes_c == never_c: + warns.append(f"{label}: {REKEY_STRENGTHEN}'s accept and never-ask " + f"letters are both '{yes_c}' - runtime fallback makes " + f"'never ask again' unreachable this run (safe, but " + f"the shortcut is lost); distinct letters preferred") + if REKEY_LOWER in entries: + text = entries[REKEY_LOWER] + letters = paren_letters(text) + if len(letters) < 1: + fails.append(f"{label}: {REKEY_LOWER} has no \"(x)\" accept marker: {text!r}") + if REKEY_CONFIRM in entries: + text = entries[REKEY_CONFIRM] + if quoted_word(text) is None: + fails.append(f"{label}: {REKEY_CONFIRM} has no '...'-quoted confirm " + f"word: {text!r}") + + +def check_placeholders(label, source, translation, fails): + for name, src_text in source.items(): + if name not in translation: + continue # untranslated entry: falls back to English, not an error + src_ph = placeholders(src_text) + tr_ph = placeholders(translation[name]) + if src_ph != tr_ph: + fails.append(f"{label}: {name} placeholder mismatch\n" + f" source: {src_ph} <- {src_text!r}\n" + f" translation: {tr_ph} <- {translation[name]!r}") + + +def main(argv): + if len(argv) < 2: + sys.exit(f"usage: {argv[0]} locale/AmiAuth.cd [translation.ct ...]") + + cd_path, ct_paths = argv[1], argv[2:] + source = parse_entries(cd_path) + if not source: + sys.exit(f"{cd_path}: parsed zero entries - check the file/parser") + + fails, warns = [], [] + check_mnemonics(cd_path, source, fails, warns) + check_rekey_prompts(cd_path, source, fails, warns) + + for ct_path in ct_paths: + entries = parse_entries(ct_path) + check_mnemonics(ct_path, entries, fails, warns) + check_rekey_prompts(ct_path, entries, fails, warns) + check_placeholders(ct_path, source, entries, fails) + + for w in warns: + print(f"WARN: {w}") + for f in fails: + print(f"FAIL: {f}") + + checked = 1 + len(ct_paths) + if fails: + print(f"\n{len(fails)} failure(s), {len(warns)} warning(s) across " + f"{checked} file(s).") + return 1 + print(f"OK: {len(warns)} warning(s), 0 failures across {checked} file(s).") + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/userdocs/Localization.md b/userdocs/Localization.md index 0d80ab3..2ba2235 100644 --- a/userdocs/Localization.md +++ b/userdocs/Localization.md @@ -62,6 +62,22 @@ immediately before an ASCII letter (ideally one that doesn't collide with another button's shortcut); dropping it, or moving it in front of a digit or symbol, silently breaks that button's keyboard shortcut. +**CLI re-key prompts:** the three interactive re-key confirmations (offered +after unlocking a vault on much faster/slower hardware than it was tuned +for) work the same way — the accepted answer is read back out of the +translated prompt itself, not hardcoded. `Strengthen it now? [(y)es/(N)o/ +ne(v)er ask here]`'s 1st `"(x)"` is the accept letter and its 3rd is the +"never ask again" letter (the 2nd, "no", is never checked); `[(y)es/(N)o]`'s +1st `"(x)"` is its accept letter; and `Type 'yes' to confirm:`'s accepted +answer is whatever word sits between the first pair of `'` quotes. Translate +freely — the letters don't have to be `y`/`N`/`v`, and don't have to be the +first letter of the translated word — just keep exactly one `"(x)"` per +choice (same left-to-right order, accept and never-ask letters distinct from +each other) and one `'...'`-quoted word. If the accept and never-ask letters +do end up the same, AmiAuth degrades safely rather than misreading your +answer: the never-ask shortcut (which writes a persisted preference) simply +becomes unreachable, it never fires on a "yes" by mistake. + ## Baseline Locale.library catalogs require AmigaOS 2.1 (V38) — one step above AmiAuth's From 12e6f688fc5b72e6d2be12b7d35948d0eca57a7c Mon Sep 17 00:00:00 2001 From: Simon Dick Date: Tue, 28 Jul 2026 15:03:40 +0100 Subject: [PATCH 2/2] Fix GUI mnemonic-position bug, add UTF-8 detection, three more draft translations The GUI button-mnemonic docs (and the linter) only required "exactly one '_' followed by a letter" - but src/gui/main.c's WMHI_VANILLAKEY handler reads a HARDCODED index (LBL_ADD[1]), not a search for the underscore, so a mnemonic marked anywhere but the very first character silently breaks. check_catalog.py now enforces underscore-at-position-0; both locale/AmiAuth.cd and userdocs/Localization.md document the constraint. Adds a Latin-1-vs-UTF-8 encoding check to check_catalog.py, found the hard way: both new draft translations were accidentally saved as UTF-8 (FlexCat and AmigaOS expect Latin-1/ISO-8859-1). Also adds a safe `--fix-encoding` mode - the first hand-rolled fix command destroyed both files by opening the same path for read and write in one expression (write-mode open truncates before the read runs); the built-in mode reads fully into memory first. New drafts: locale/drafts/francais.ct, italiano.ct, polski.ct (still AI-generated and unreviewed, like the existing deutsch.ct). Polski deliberately omits Polish diacritics - the catalog format's ##codeset is constrained to Latin-1, which contains only one of the nine. Adds a "Draft translations awaiting review" section to userdocs/Localization.md pointing at locale/drafts/. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01K5bAsAyoYLpkX3rWwrTBPh --- locale/AmiAuth.cd | 15 +- locale/drafts/deutsch.ct | 124 ++++----- locale/drafts/francais.ct | 523 ++++++++++++++++++++++++++++++++++++ locale/drafts/italiano.ct | 522 ++++++++++++++++++++++++++++++++++++ locale/drafts/polski.ct | 539 ++++++++++++++++++++++++++++++++++++++ tools/check_catalog.py | 84 +++++- userdocs/Localization.md | 45 +++- 7 files changed, 1775 insertions(+), 77 deletions(-) create mode 100644 locale/drafts/francais.ct create mode 100644 locale/drafts/italiano.ct create mode 100644 locale/drafts/polski.ct diff --git a/locale/AmiAuth.cd b/locale/AmiAuth.cd index 4b0b355..134ca69 100644 --- a/locale/AmiAuth.cd +++ b/locale/AmiAuth.cd @@ -25,10 +25,17 @@ ; reused by the GUI for its own "%s: %s\n" library-load failure). ; ; A leading underscore in a button label (MSG_GUI_BTN_*, e.g. "_Add") marks -; the keyboard mnemonic letter - window.class reads it back out of the -; string at runtime (see the LBL_* comment in src/gui/main.c), so a -; translation MUST keep exactly one '_' immediately before an ASCII letter, -; ideally one that doesn't collide with another button's mnemonic. +; the keyboard mnemonic letter - the WMHI_VANILLAKEY handler reads it back +; out of the string at runtime via a HARDCODED index (LBL_ADD[1], see +; src/gui/main.c), not by searching for '_' wherever it happens to be. This +; means the '_' MUST be the very FIRST character of the string (index 0), +; immediately followed by an ASCII letter (index 1) - a translation can +; choose whichever word it likes for the button (the mnemonic is always +; that word's first letter, not a letter of your choosing mid-word), and +; the six mnemonics used together in the toolbar must be pairwise distinct. +; Get this wrong (underscore not at the very start) and the code silently +; reads whatever character actually sits at index 1 instead - not an error, +; just a dead or wrong shortcut. `tools/check_catalog.py` enforces this. ; ; Same idea for the CLI's three re-key confirmation prompts ; (MSG_CLI_REKEY_STRENGTHEN_PROMPT/MSG_CLI_REKEY_LOWER_PROMPT): the letter diff --git a/locale/drafts/deutsch.ct b/locale/drafts/deutsch.ct index 3e85048..a89db5c 100644 --- a/locale/drafts/deutsch.ct +++ b/locale/drafts/deutsch.ct @@ -8,10 +8,10 @@ ; vault -> Tresor passphrase -> Passphrase (not Passwort) ; account -> Konto secret -> Secret (loanword, RFC term) ; issuer -> Aussteller label -> Bezeichnung -; re-key -> neu verschlüsseln / Neuverschlüsselung +; re-key -> neu verschlüsseln / Neuverschlüsselung ; always-unlocked -> immer entsperrt ; Tone: neutral/impersonal (no "Du"/"Sie" address needed for most strings; -; imperative infinitive for buttons, e.g. "Hinzufügen" not "Füge hinzu"). +; imperative infinitive for buttons, e.g. "Hinzufügen" not "Füge hinzu"). ; ##version $VER: AmiAuth.catalog 1.0 (28.07.2026) ##language deutsch @@ -23,7 +23,7 @@ MSG_CLI_ALREADY_EXISTS %s: %s existiert bereits\n ; MSG_CLI_ADDED -%s:%s hinzugefügt\n +%s:%s hinzugefügt\n ; MSG_CLI_REMOVED '%s' entfernt\n @@ -45,10 +45,10 @@ Rest ; --- GUI: requester text ------------------------------------------------- ; MSG_GUI_CANNOT_OPEN_VAULT -Der Tresor kann nicht geöffnet werden:\n%s +Der Tresor kann nicht geöffnet werden:\n%s ; MSG_GUI_BAD_URI -Das ist keine gültige otpauth://-URI. +Das ist keine gültige otpauth://-URI. ; MSG_GUI_BAD_SECRET Das ist weder eine otpauth://-URI noch ein Base32-Secret. @@ -60,10 +60,10 @@ MSG_GUI_SAVE_FAILED Der Tresor konnte nicht gespeichert werden. ; MSG_GUI_REKEY_DONE -Tresor wurde für diesen Rechner neu verschlüsselt. +Tresor wurde für diesen Rechner neu verschlüsselt. ; MSG_GUI_REKEY_FAILED -Neuverschlüsselung fehlgeschlagen; der Tresor bleibt unverändert. +Neuverschlüsselung fehlgeschlagen; der Tresor bleibt unverändert. ; ; --- GUI: common requester button labels ---------------------------------- ; @@ -79,7 +79,7 @@ MSG_ERR_IO die Tresordatei kann nicht gelesen/geschrieben werden ; MSG_ERR_FORMAT -keine gültige Tresordatei +keine gültige Tresordatei ; MSG_ERR_AUTH falsche Passphrase, oder die Datei wurde manipuliert @@ -102,7 +102,7 @@ MSG_CLI_ERR %s: %s\n ; MSG_CLI_WARN_HOTP_PERSIST -%s: Warnung: HOTP-Zähler konnte nicht gespeichert werden (%s)\n +%s: Warnung: HOTP-Zähler konnte nicht gespeichert werden (%s)\n ; ; --- CLI: passphrase prompts ---------------------------------------------- ; @@ -110,10 +110,10 @@ MSG_CLI_PROMPT_PASSPHRASE Passphrase: ; MSG_CLI_NO_TTY -%s: dieser Tresor ist verschlüsselt; bitte in einem interaktiven Terminal ausführen, oder für Skripte einen immer entsperrten Tresor verwenden\n +%s: dieser Tresor ist verschlüsselt; bitte in einem interaktiven Terminal ausführen, oder für Skripte einen immer entsperrten Tresor verwenden\n ; MSG_CLI_NO_RNG_SAVE -%s: keine sichere Zufallsquelle vorhanden, um einen verschlüsselten Tresor zu speichern (Phase 4)\n +%s: keine sichere Zufallsquelle vorhanden, um einen verschlüsselten Tresor zu speichern (Phase 4)\n ; ; --- CLI: adaptive re-key (maybe_rekey) ----------------------------------- ; @@ -123,27 +123,27 @@ MSG_CLI_NO_RNG_SAVE ; change them, just keep exactly one "(x)" per choice in the same ; left-to-right order, with the accept and never-ask letters distinct. MSG_CLI_REKEY_STRENGTHEN_PROMPT -Dieser Rechner ist deutlich schneller als der, der diesen Tresor gesichert hat.\nJetzt stärken? [(j)a/(N)ein/n(i)e hier fragen] +Dieser Rechner ist deutlich schneller als der, der diesen Tresor gesichert hat.\nJetzt stärken? [(j)a/(N)ein/n(i)e hier fragen] ; MSG_CLI_REKEY_SLOW_NOTICE -%s: Entsperren dauerte ~%lus; dieser Tresor wurde für schnellere Hardware eingestellt.\n +%s: Entsperren dauerte ~%lus; dieser Tresor wurde für schnellere Hardware eingestellt.\n ; MSG_CLI_REKEY_LOWER_PROMPT -Für schnelleres Entsperren hier ABSCHWÄCHEN? Dies VERRINGERT die Sicherheit. [(j)a/(N)ein] +Für schnelleres Entsperren hier ABSCHWÄCHEN? Dies VERRINGERT die Sicherheit. [(j)a/(N)ein] ; MSG_CLI_CONFIRM_YES_PROMPT -Zur Bestätigung 'ja' eingeben: +Zur Bestätigung 'ja' eingeben: ; MSG_CLI_REKEYED -%s: neu verschlüsselt mit %lu Iterationen\n +%s: neu verschlüsselt mit %lu Iterationen\n ; MSG_CLI_REKEY_FAILED -%s: Neuverschlüsselung fehlgeschlagen; Tresor unverändert\n +%s: Neuverschlüsselung fehlgeschlagen; Tresor unverändert\n ; ; --- CLI: CODE --------------------------------------------------------- ; MSG_CLI_BAD_SECRET -%s: ungültiges oder leeres Base32-Secret\n +%s: ungültiges oder leeres Base32-Secret\n ; MSG_CLI_SECONDS_REMAINING (noch %u Sekunden)\n @@ -151,13 +151,13 @@ MSG_CLI_SECONDS_REMAINING ; --- CLI: CLOCK/SYNC/OFFSET/NUDGE --------------------------------------- ; MSG_CLI_CLOCK_SYNCED -synchronisiert (grün) +synchronisiert (grün) ; MSG_CLI_CLOCK_MANUAL Offset gesetzt (gelb) ; MSG_CLI_CLOCK_UNVERIFIED -nicht geprüft (rot) +nicht geprüft (rot) ; MSG_CLI_CLOCK_OFFSET_LINE UTC-Offset : %+ld Sekunden (%+ld Min)\n @@ -180,19 +180,19 @@ MSG_CLI_OFFSET_SAVE_FAILED ; --- CLI: INIT ----------------------------------------------------------- ; MSG_CLI_INIT_PASS_PROMPT -Neue Passphrase (leer lassen für einen immer entsperrten Tresor): +Neue Passphrase (leer lassen für einen immer entsperrten Tresor): ; MSG_CLI_INIT_NO_TTY -%s: INIT benötigt ein interaktives Terminal (für einen nicht-interaktiven, immer entsperrten Tresor 'INIT --open' verwenden)\n +%s: INIT benötigt ein interaktives Terminal (für einen nicht-interaktiven, immer entsperrten Tresor 'INIT --open' verwenden)\n ; MSG_CONFIRM_PASSPHRASE -Passphrase bestätigen: +Passphrase bestätigen: ; MSG_CLI_PASS_MISMATCH -%s: Passphrasen stimmen nicht überein\n +%s: Passphrasen stimmen nicht überein\n ; MSG_CLI_INIT_NO_RNG -%s: keine sichere Zufallsquelle für einen verschlüsselten Tresor (Phase 4); mit 'INIT --open' einen immer entsperrten Tresor anlegen\n +%s: keine sichere Zufallsquelle für einen verschlüsselten Tresor (Phase 4); mit 'INIT --open' einen immer entsperrten Tresor anlegen\n ; MSG_CLI_KDF_ITERATIONS %s: KDF-Iterationen = %lu\n @@ -204,7 +204,7 @@ MSG_CLI_ALWAYS_UNLOCKED immer entsperrt ; MSG_CLI_ENCRYPTED -verschlüsselt +verschlüsselt ; ; --- CLI: GUI forwarding (try_forward) ------------------------------------ ; @@ -218,10 +218,10 @@ MSG_CLI_VAULT_FULL %s: der Tresor ist voll (max. 64 Konten)\n ; MSG_CLI_BAD_OTPURI -%s: keine gültige otpauth://-URI\n +%s: keine gültige otpauth://-URI\n ; MSG_CLI_GUI_SAVEFAIL -%s: in der GUI übernommen, aber das erneute Speichern ist fehlgeschlagen\n +%s: in der GUI übernommen, aber das erneute Speichern ist fehlgeschlagen\n ; MSG_CLI_URI_TOO_LONG %s: die URI dieses Kontos ist zu lang, um als QR-Code kodiert zu werden\n @@ -235,7 +235,7 @@ MSG_CLI_PARSE_FAILED %s: otpauth://-URI konnte nicht gelesen werden\n ; MSG_CLI_ADDED_NOISSUER -%s hinzugefügt\n +%s hinzugefügt\n ; MSG_CLI_BAD_BASE32 %s: das sieht nicht wie ein Base32-Secret aus\n @@ -246,10 +246,10 @@ MSG_CLI_NO_GUI %s: keine laufende GUI zum Anzeigen\n ; MSG_CLI_TAGLINE -%s - TOTP/HOTP-Authentifizierung für AmigaOS\n\n +%s - TOTP/HOTP-Authentifizierung für AmigaOS\n\n ; MSG_CLI_ENCRYPTED_NOTE -Verschlüsselte Tresore fragen die Passphrase im Terminal ab.\n +Verschlüsselte Tresore fragen die Passphrase im Terminal ab.\n ; ; --- GUI: main menu (g_menu[]) --------------------------------------------- ; @@ -263,13 +263,13 @@ MSG_GUI_MENU_ACCOUNT Konto ; MSG_GUI_MENU_ADD_CLIP -Aus Zwischenablage hinzufügen +Aus Zwischenablage hinzufügen ; MSG_GUI_MENU_ADD_TYPE -Hinzufügen (URI/Secret eingeben)... +Hinzufügen (URI/Secret eingeben)... ; MSG_GUI_MENU_ADD_QR -Aus QR-Bild hinzufügen... +Aus QR-Bild hinzufügen... ; MSG_GUI_MENU_EDIT Auswahl bearbeiten... @@ -289,7 +289,7 @@ Auswahl entfernen... ; the same window - keep them distinct if you change the wording. ; MSG_GUI_BTN_ADD -_Hinzufügen +_Hinzufügen ; MSG_GUI_BTN_EDIT _Bearbeiten @@ -323,7 +323,7 @@ MSG_GUI_CLOCK_MANUAL manuell ; MSG_GUI_CLOCK_UNVERIFIED -ungeprüft +ungeprüft ; MSG_GUI_CLOCK_LINE_SHORT Uhr: %s @@ -334,16 +334,16 @@ Uhr: %s %s%lu:%02lu ; --- GUI: adaptive re-key (gui_maybe_rekey) ------------------------------- ; MSG_GUI_REKEY_STRENGTHEN_BODY -Dieser Rechner ist deutlich schneller als der, der diesen Tresor gesichert hat.\nJetzt stärken (Neuverschlüsselung mit mehr PBKDF2-Iterationen)? +Dieser Rechner ist deutlich schneller als der, der diesen Tresor gesichert hat.\nJetzt stärken (Neuverschlüsselung mit mehr PBKDF2-Iterationen)? ; MSG_GUI_REKEY_STRENGTHEN_BTNS -Stärken|Jetzt nicht|Hier nie +Stärken|Jetzt nicht|Hier nie ; MSG_GUI_REKEY_SLOW_BODY -Entsperren dauerte etwa %lu Sekunden; dieser Tresor wurde für schnellere Hardware eingestellt.\nHier für schnelleres Entsperren ABSCHWÄCHEN? Dies VERRINGERT die Sicherheit. +Entsperren dauerte etwa %lu Sekunden; dieser Tresor wurde für schnellere Hardware eingestellt.\nHier für schnelleres Entsperren ABSCHWÄCHEN? Dies VERRINGERT die Sicherheit. ; MSG_GUI_REKEY_LOWER_BTNS -Abschwächen|Abbrechen +Abschwächen|Abbrechen ; MSG_GUI_REKEY_CONFIRM_BODY Sicherheit dieses Tresors wirklich verringern? @@ -359,22 +359,22 @@ Der Tresor ist voll (max. 64 Konten). ; --- GUI: QR import (do_add_qr_impl) -------------------------------------- ; MSG_GUI_IMG_TOOBIG -Dieses Bild ist zu groß, um decodiert zu werden. +Dieses Bild ist zu groß, um decodiert zu werden. ; MSG_GUI_QR_UNAVAIL -Der QR-Import benötigt datatypes.library. +Der QR-Import benötigt datatypes.library. ; MSG_GUI_IMG_NOMEM Nicht genug Speicher, um dieses Bild zu laden. ; MSG_GUI_IMG_READ_FAILED -Dieses Bild konnte nicht gelesen werden (benötigt picture.datatype). +Dieses Bild konnte nicht gelesen werden (benötigt picture.datatype). ; MSG_GUI_TITLE_DECODING %s - QR-Bild wird decodiert... ; MSG_GUI_QR_FILE_TITLE -QR-Bild auswählen +QR-Bild auswählen ; ; --- GUI: unlock prompt (passphrase_request title) ------------------------ ; @@ -384,7 +384,7 @@ MSG_GUI_TITLE_UNLOCK ; --- GUI: first-run vault creation ----------------------------------------- ; MSG_GUI_CHOOSE_LOCATION -Speicherort für den Tresor wählen +Speicherort für den Tresor wählen ; MSG_GUI_WELCOME Willkommen bei %s!\n\nEs gibt noch keinen Tresor - er wird angelegt unter\n%s @@ -399,25 +399,25 @@ MSG_GUI_NEW_PASS_PROMPT Neue Passphrase (leer = immer entsperrt): ; MSG_GUI_STORE_UNENCRYPTED_BODY -Tresor UNVERSCHLÜSSELT speichern?\n\nOhne Passphrase gibt es keinen Schutz im Ruhezustand:\njeder mit Zugriff auf die Datei kann Ihre Secrets lesen.\n(Eine Passphrase kann später hinzugefügt werden.) +Tresor UNVERSCHLÜSSELT speichern?\n\nOhne Passphrase gibt es keinen Schutz im Ruhezustand:\njeder mit Zugriff auf die Datei kann Ihre Secrets lesen.\n(Eine Passphrase kann später hinzugefügt werden.) ; MSG_GUI_STORE_UNENCRYPTED_BTNS -Unverschlüsselt speichern|Zurück +Unverschlüsselt speichern|Zurück ; MSG_GUI_PASS_MISMATCH -Die Passphrasen stimmen nicht überein. +Die Passphrasen stimmen nicht überein. ; MSG_GUI_TRY_AGAIN Erneut versuchen ; MSG_GUI_NO_RNG_BODY -Es ist keine sichere Zufallsquelle verfügbar, daher kann\nkein verschlüsselter Tresor sicher angelegt werden. +Es ist keine sichere Zufallsquelle verfügbar, daher kann\nkein verschlüsselter Tresor sicher angelegt werden. ; MSG_GUI_CANNOT_WRITE_BODY -Der Tresor kann nicht geschrieben werden nach\n%s\n\nAnderen Speicherort wählen? +Der Tresor kann nicht geschrieben werden nach\n%s\n\nAnderen Speicherort wählen? ; MSG_GUI_CHOOSE_LOCATION_BTNS -Ort wählen...|Beenden +Ort wählen...|Beenden ; MSG_GUI_CREATE_FAILED Anlegen des Tresors fehlgeschlagen. @@ -431,15 +431,15 @@ MSG_GUI_WRONG_PASS Falsche Passphrase - erneut versuchen: ; MSG_GUI_CANNOT_OPEN_PRINTF -%s: Tresor kann nicht geöffnet werden (%ld)\n +%s: Tresor kann nicht geöffnet werden (%ld)\n ; ; --- GUI: add account form (uri_request) ------------------------------ ; MSG_GUI_URI_PASTE_LABEL -otpauth://-URI oder Base32-Secret einfügen oder eingeben: +otpauth://-URI oder Base32-Secret einfügen oder eingeben: ; MSG_GUI_TITLE_ADD_ACCOUNT -%s - Konto hinzufügen +%s - Konto hinzufügen ; ; --- GUI: edit account form (edit_request) ------------------------------ ; @@ -462,12 +462,12 @@ MSG_GUI_LABEL_REQUIRED_STEAM Bezeichnung ist erforderlich. ; MSG_GUI_LABEL_REQUIRED_FULL -Bezeichnung ist erforderlich; Stellen müssen 6-8, Intervall 1-86400 sein. +Bezeichnung ist erforderlich; Stellen müssen 6-8, Intervall 1-86400 sein. ; ; --- GUI: QR export display (qr_display) -------------------------------- ; MSG_GUI_QR_BUILD_FAILED -Für dieses Konto konnte kein QR-Code erstellt werden. +Für dieses Konto konnte kein QR-Code erstellt werden. ; MSG_GUI_QR_TOO_LONG Die URI dieses Kontos ist zu lang, um als QR-Code kodiert zu werden. @@ -476,7 +476,7 @@ MSG_GUI_QR_DISPLAY_FAILED Die QR-Anzeige konnte nicht erstellt werden. ; MSG_GUI_QR_WINDOW_FAILED -Das QR-Fenster konnte nicht geöffnet werden. +Das QR-Fenster konnte nicht geöffnet werden. ; MSG_GUI_TITLE_QR_ISSUER %s - QR: %s:%s @@ -487,10 +487,10 @@ MSG_GUI_TITLE_QR_NOISSUER ; --- GUI: bare-secret add form (secret_meta_request) ---------------------- ; MSG_GUI_SECRET_META_HINT -Ein reines Secret trägt keinen Namen - für wen ist dieses Konto? +Ein reines Secret trägt keinen Namen - für wen ist dieses Konto? ; MSG_GUI_ADD_BTN -Hinzufügen +Hinzufügen ; MSG_GUI_ISSUER_LABEL_REQUIRED Aussteller und Bezeichnung sind beide erforderlich. @@ -511,13 +511,13 @@ Entfernen|Abbrechen ; --- GUI: startup failures (open_libs/main) ------------------------------- ; MSG_GUI_NEEDS_INTUITION -benötigt intuition.library / utility.library v37+ (OS 2.04) +benötigt intuition.library / utility.library v37+ (OS 2.04) ; MSG_GUI_NEEDS_REACTION -benötigt ReAction/ClassAct-Klassen (window/layout/listbrowser/fuelgauge/button) +benötigt ReAction/ClassAct-Klassen (window/layout/listbrowser/fuelgauge/button) ; MSG_GUI_WINDOW_CREATE_FAILED %s: Fenster konnte nicht erstellt werden\n ; MSG_GUI_WINDOW_OPEN_FAILED -%s: Fenster konnte nicht geöffnet werden\n +%s: Fenster konnte nicht geöffnet werden\n diff --git a/locale/drafts/francais.ct b/locale/drafts/francais.ct new file mode 100644 index 0000000..8a08ed7 --- /dev/null +++ b/locale/drafts/francais.ct @@ -0,0 +1,523 @@ +; francais.ct - DRAFT French translation of locale/AmiAuth.cd. +; +; MACHINE-GENERATED FIRST PASS, NOT REVIEWED BY A NATIVE SPEAKER. +; See locale/drafts/README.md before using or building this file - it is +; not wired into any build target and must not be installed as-is. +; +; Terminology used throughout, for reviewer consistency: +; vault -> coffre-fort passphrase -> phrase de passe +; account -> compte secret -> secret (cognate, kept as-is) +; issuer -> emetteur label -> etiquette +; re-key -> rechiffrer / rechiffrement +; always-unlocked -> toujours deverrouille +; (accents intact below - AmigaOS's ISO-8859-1 charset covers French fully) +; +##version $VER: AmiAuth.catalog 1.0 (28.07.2026) +##language francais +##codeset 0 +; +; --- CLI (src/cli/main.c) --------------------------------------------- +; +MSG_CLI_ALREADY_EXISTS +%s : %s existe déjà\n +; +MSG_CLI_ADDED +%s:%s ajouté\n +; +MSG_CLI_REMOVED +'%s' supprimé\n +; +MSG_CLI_USAGE_RUNAS +Lancez avec '%s ...' ('%s ?' affiche le modèle d'arguments) :\n +; +; --- GUI (src/gui/main.c): account list column headers ----------------- +; +MSG_GUI_COL_ACCOUNT +Compte +; +MSG_GUI_COL_CODE +Code +; +MSG_GUI_COL_LEFT +Reste +; +; --- GUI: requester text ------------------------------------------------- +; +MSG_GUI_CANNOT_OPEN_VAULT +Impossible d'ouvrir le coffre-fort :\n%s +; +MSG_GUI_BAD_URI +Ce n'est pas une URI otpauth:// valide. +; +MSG_GUI_BAD_SECRET +Ce n'est ni une URI otpauth:// ni un secret Base32. +; +MSG_GUI_NO_QR +Aucun code QR otpauth n'a été trouvé dans cette image. +; +MSG_GUI_SAVE_FAILED +Impossible d'enregistrer le coffre-fort. +; +MSG_GUI_REKEY_DONE +Coffre-fort rechiffré pour cet ordinateur. +; +MSG_GUI_REKEY_FAILED +Échec du rechiffrement ; le coffre-fort reste inchangé. +; +; --- GUI: common requester button labels ---------------------------------- +; +MSG_GUI_OK +OK +; +MSG_GUI_CANCEL +Annuler +; +; --- CLI: vault_err()'s own strings, wrapped by MSG_CLI_ERR below --------- +; +MSG_ERR_IO +impossible de lire/écrire le fichier du coffre-fort +; +MSG_ERR_FORMAT +fichier de coffre-fort invalide +; +MSG_ERR_AUTH +phrase de passe incorrecte ou fichier altéré +; +MSG_ERR_LOCKED +le coffre-fort est verrouillé +; +MSG_ERR_FULL +le coffre-fort est plein +; +MSG_ERR_RANGE +compte introuvable +; +MSG_ERR_UNKNOWN +erreur +; +; --- CLI: generic error wrapper ("AmiAuth" + vault_err()/other message) -- +; +MSG_CLI_ERR +%s : %s\n +; +MSG_CLI_WARN_HOTP_PERSIST +%s : attention : le compteur HOTP n'a pas pu être enregistré (%s)\n +; +; --- CLI: passphrase prompts ---------------------------------------------- +; +MSG_CLI_PROMPT_PASSPHRASE +Phrase de passe : +; +MSG_CLI_NO_TTY +%s : ce coffre-fort est chiffré ; lancez depuis un terminal interactif, ou utilisez un coffre-fort toujours déverrouillé pour les scripts\n +; +MSG_CLI_NO_RNG_SAVE +%s : aucune source aléatoire sécurisée pour enregistrer un coffre-fort chiffré (Phase 4)\n +; +; --- CLI: adaptive re-key (maybe_rekey) ----------------------------------- +; +; REVIEWER: the letter(s) in "(x)" and the word in '...' below are read back +; out of this translated string at runtime (locale/AmiAuth.cd's header has +; the full convention) - o/n/j and 'oui' are just this draft's choice, +; freely change them, just keep exactly one "(x)" per choice in the same +; left-to-right order, with the accept and never-ask letters distinct. +MSG_CLI_REKEY_STRENGTHEN_PROMPT +Cet ordinateur est beaucoup plus rapide que celui qui a sécurisé ce coffre-fort.\nLe renforcer maintenant ? [(o)ui/(n)on/(j)amais redemander ici] +; +MSG_CLI_REKEY_SLOW_NOTICE +%s : déverrouillage en ~%lus ; ce coffre-fort a été calibré pour du matériel plus rapide.\n +; +MSG_CLI_REKEY_LOWER_PROMPT +Affaiblir ici pour des déverrouillages plus rapides ? Cela RÉDUIT la sécurité. [(o)ui/(n)on] +; +MSG_CLI_CONFIRM_YES_PROMPT +Tapez 'oui' pour confirmer : +; +MSG_CLI_REKEYED +%s : rechiffré avec %lu itérations\n +; +MSG_CLI_REKEY_FAILED +%s : échec du rechiffrement ; coffre-fort inchangé\n +; +; --- CLI: CODE --------------------------------------------------------- +; +MSG_CLI_BAD_SECRET +%s : secret Base32 invalide ou vide\n +; +MSG_CLI_SECONDS_REMAINING +(encore %u secondes)\n +; +; --- CLI: CLOCK/SYNC/OFFSET/NUDGE --------------------------------------- +; +MSG_CLI_CLOCK_SYNCED +synchronisé (vert) +; +MSG_CLI_CLOCK_MANUAL +décalage appliqué (orange) +; +MSG_CLI_CLOCK_UNVERIFIED +non vérifié (rouge) +; +MSG_CLI_CLOCK_OFFSET_LINE +décalage UTC : %+ld secondes (%+ld min)\n +; +MSG_CLI_CLOCK_STATUS_LINE +statut : %s\n +; +MSG_CLI_CLOCK_CORRECTED_LINE +corrigé : %04d-%02d-%02d %02d:%02d:%02d UTC\n +; +MSG_CLI_QUERYING +Interrogation de %s ...\n +; +MSG_CLI_SYNC_FAILED +%s : échec de la synchronisation SNTP (pas de pile TCP/IP, ou pas de réponse de %s)\n +; +MSG_CLI_OFFSET_SAVE_FAILED +%s : impossible d'enregistrer le décalage\n +; +; --- CLI: INIT ----------------------------------------------------------- +; +MSG_CLI_INIT_PASS_PROMPT +Nouvelle phrase de passe (vide pour un coffre-fort toujours déverrouillé) : +; +MSG_CLI_INIT_NO_TTY +%s : INIT nécessite un terminal interactif (utilisez 'INIT --open' pour un coffre-fort toujours déverrouillé non interactif)\n +; +MSG_CONFIRM_PASSPHRASE +Confirmez la phrase de passe : +; +MSG_CLI_PASS_MISMATCH +%s : les phrases de passe ne correspondent pas\n +; +MSG_CLI_INIT_NO_RNG +%s : aucune source aléatoire sécurisée pour un coffre-fort chiffré (Phase 4) ; créez un coffre-fort toujours déverrouillé avec 'INIT --open'\n +; +MSG_CLI_KDF_ITERATIONS +%s : itérations KDF = %lu\n +; +MSG_CLI_CREATED +Coffre-fort (%s) créé à %s\n +; +MSG_CLI_ALWAYS_UNLOCKED +toujours déverrouillé +; +MSG_CLI_ENCRYPTED +chiffré +; +; --- CLI: GUI forwarding (try_forward) ------------------------------------ +; +MSG_CLI_GUI_LOCKED +%s : le coffre-fort de l'interface graphique en cours est verrouillé ; déverrouillez-le d'abord là-bas\n +; +MSG_CLI_NO_ACCOUNT +%s : aucun compte correspondant à '%s'\n +; +MSG_CLI_VAULT_FULL +%s : le coffre-fort est plein (64 comptes max.)\n +; +MSG_CLI_BAD_OTPURI +%s : ce n'est pas une URI otpauth:// valide\n +; +MSG_CLI_GUI_SAVEFAIL +%s : appliqué dans l'interface graphique mais le nouvel enregistrement a échoué\n +; +MSG_CLI_URI_TOO_LONG +%s : l'URI de ce compte est trop longue pour être encodée en code QR\n +; +MSG_CLI_GUI_UNKNOWN +%s : l'interface graphique en cours n'a pas pu traiter cela\n +; +; --- CLI: ADD -------------------------------------------------------------- +; +MSG_CLI_PARSE_FAILED +%s : impossible d'analyser l'URI otpauth://\n +; +MSG_CLI_ADDED_NOISSUER +%s ajouté\n +; +MSG_CLI_BAD_BASE32 +%s : cela ne ressemble pas à un secret Base32\n +; +; --- CLI: SHOW / usage ----------------------------------------------------- +; +MSG_CLI_NO_GUI +%s : aucune interface graphique en cours à afficher\n +; +MSG_CLI_TAGLINE +%s - authentification TOTP/HOTP pour AmigaOS\n\n +; +MSG_CLI_ENCRYPTED_NOTE +Les coffres-forts chiffrés demandent la phrase de passe dans le terminal.\n +; +; --- GUI: main menu (g_menu[]) --------------------------------------------- +; +MSG_GUI_MENU_PROJECT +Projet +; +MSG_GUI_QUIT +Quitter +; +MSG_GUI_MENU_ACCOUNT +Compte +; +MSG_GUI_MENU_ADD_CLIP +Ajouter depuis le presse-papiers +; +MSG_GUI_MENU_ADD_TYPE +Ajouter (saisir URI/secret)... +; +MSG_GUI_MENU_ADD_QR +Ajouter depuis une image QR... +; +MSG_GUI_MENU_EDIT +Modifier la sélection... +; +MSG_GUI_MENU_COPY +Copier le code +; +MSG_GUI_MENU_SHOW_QR +Afficher le code QR... +; +MSG_GUI_MENU_REMOVE +Supprimer la sélection... +; +; --- GUI: toolbar button mnemonics (LBL_*, see the underscore note above) - +; REVIEWER: the '_' MUST stay the very first character of each string (the +; code reads a fixed index, not "wherever the underscore is") - so the +; mnemonic is always the first letter of whichever word you choose. The six +; letters below (A/M/S/C/R/P) are distinct from each other. +; +MSG_GUI_BTN_ADD +_Ajouter +; +MSG_GUI_BTN_EDIT +_Modifier +; +MSG_GUI_BTN_REMOVE +_Supprimer +; +MSG_GUI_BTN_COPY +_Copier +; +MSG_GUI_BTN_NUDGE_DOWN +_R -10s +; +MSG_GUI_BTN_NUDGE_UP +_P +10s +; +; --- GUI: copy-code button's transient states (no mnemonic) --------------- +; +MSG_GUI_COPY_PLAIN +Copier +; +MSG_GUI_COPIED +Copié +; +; --- GUI: clock-status line -------------------------------------------- +; +MSG_GUI_CLOCK_SYNCED +synchronisé +; +MSG_GUI_CLOCK_MANUAL +manuel +; +MSG_GUI_CLOCK_UNVERIFIED +non vérifié +; +MSG_GUI_CLOCK_LINE_SHORT +Horloge : %s +; +MSG_GUI_CLOCK_LINE_FULL +Horloge : %s %s%lu:%02lu +; +; --- GUI: adaptive re-key (gui_maybe_rekey) ------------------------------- +; +MSG_GUI_REKEY_STRENGTHEN_BODY +Cet ordinateur est beaucoup plus rapide que celui qui a sécurisé ce coffre-fort.\nLe renforcer maintenant (rechiffrer avec plus d'itérations PBKDF2) ? +; +MSG_GUI_REKEY_STRENGTHEN_BTNS +Renforcer|Pas maintenant|Jamais ici +; +MSG_GUI_REKEY_SLOW_BODY +Le déverrouillage a pris environ %lu secondes ; ce coffre-fort a été calibré pour du matériel plus rapide.\nAffaiblir ici pour des déverrouillages plus rapides ? Cela RÉDUIT la sécurité. +; +MSG_GUI_REKEY_LOWER_BTNS +Affaiblir|Annuler +; +MSG_GUI_REKEY_CONFIRM_BODY +Vraiment réduire la sécurité de ce coffre-fort ? +; +MSG_GUI_REKEY_REDUCE_BTNS +Réduire|Annuler +; +; --- GUI: add account (uri/secret, shared gui_add_uri/gui_add_secret) ---- +; +MSG_GUI_VAULT_FULL +Le coffre-fort est plein (64 comptes max.). +; +; --- GUI: QR import (do_add_qr_impl) -------------------------------------- +; +MSG_GUI_IMG_TOOBIG +Cette image est trop grande pour être décodée. +; +MSG_GUI_QR_UNAVAIL +L'import QR nécessite datatypes.library. +; +MSG_GUI_IMG_NOMEM +Pas assez de mémoire pour charger cette image. +; +MSG_GUI_IMG_READ_FAILED +Impossible de lire cette image (nécessite picture.datatype). +; +MSG_GUI_TITLE_DECODING +%s - Décodage de l'image QR... +; +MSG_GUI_QR_FILE_TITLE +Sélectionnez une image QR +; +; --- GUI: unlock prompt (passphrase_request title) ------------------------ +; +MSG_GUI_TITLE_UNLOCK +%s - Déverrouiller +; +; --- GUI: first-run vault creation ----------------------------------------- +; +MSG_GUI_CHOOSE_LOCATION +Choisissez où conserver le coffre-fort +; +MSG_GUI_WELCOME +Bienvenue dans %s !\n\nIl n'y a pas encore de coffre-fort - il sera créé à\n%s +; +MSG_GUI_CREATE_VAULT_BTNS_DEFER +Créer un coffre-fort...|Pas maintenant +; +MSG_GUI_CREATE_VAULT_BTNS_QUIT +Créer un coffre-fort...|Quitter +; +MSG_GUI_NEW_PASS_PROMPT +Nouvelle phrase de passe (vide = toujours déverrouillé) : +; +MSG_GUI_STORE_UNENCRYPTED_BODY +Enregistrer le coffre-fort NON CHIFFRÉ ?\n\nSans phrase de passe, il n'y a aucune protection au repos :\ntoute personne ayant accès au fichier peut lire vos secrets.\n(Vous pourrez ajouter une phrase de passe plus tard.) +; +MSG_GUI_STORE_UNENCRYPTED_BTNS +Enregistrer sans chiffrer|Retour +; +MSG_GUI_PASS_MISMATCH +Les phrases de passe ne correspondent pas. +; +MSG_GUI_TRY_AGAIN +Réessayer +; +MSG_GUI_NO_RNG_BODY +Aucune source aléatoire sécurisée n'est disponible, donc un\ncoffre-fort chiffré ne peut pas être créé en toute sécurité. +; +MSG_GUI_CANNOT_WRITE_BODY +Impossible d'écrire le coffre-fort vers\n%s\n\nChoisir un autre emplacement ? +; +MSG_GUI_CHOOSE_LOCATION_BTNS +Choisir un emplacement...|Quitter +; +MSG_GUI_CREATE_FAILED +Échec de la création du coffre-fort. +; +; --- GUI: unlock loop (gui_open_vault) -------------------------------- +; +MSG_GUI_ENTER_PASS +Entrez la phrase de passe : +; +MSG_GUI_WRONG_PASS +Phrase de passe incorrecte - réessayez : +; +MSG_GUI_CANNOT_OPEN_PRINTF +%s : impossible d'ouvrir le coffre-fort (%ld)\n +; +; --- GUI: add account form (uri_request) ------------------------------ +; +MSG_GUI_URI_PASTE_LABEL +Collez ou saisissez une URI otpauth:// ou un secret Base32 : +; +MSG_GUI_TITLE_ADD_ACCOUNT +%s - Ajouter un compte +; +; --- GUI: edit account form (edit_request) ------------------------------ +; +MSG_GUI_LABEL_ISSUER +Émetteur : +; +MSG_GUI_LABEL_LABEL +Étiquette : +; +MSG_GUI_LABEL_DIGITS +Chiffres : +; +MSG_GUI_LABEL_PERIOD +Intervalle : +; +MSG_GUI_TITLE_EDIT_ACCOUNT +%s - Modifier le compte +; +MSG_GUI_LABEL_REQUIRED_STEAM +L'étiquette est requise. +; +MSG_GUI_LABEL_REQUIRED_FULL +L'étiquette est requise ; les chiffres doivent être 6-8, l'intervalle 1-86400. +; +; --- GUI: QR export display (qr_display) -------------------------------- +; +MSG_GUI_QR_BUILD_FAILED +Impossible de créer un code QR pour ce compte. +; +MSG_GUI_QR_TOO_LONG +L'URI de ce compte est trop longue pour être encodée en code QR. +; +MSG_GUI_QR_DISPLAY_FAILED +Impossible de créer l'affichage QR. +; +MSG_GUI_QR_WINDOW_FAILED +Impossible d'ouvrir la fenêtre QR. +; +MSG_GUI_TITLE_QR_ISSUER +%s - QR : %s:%s +; +MSG_GUI_TITLE_QR_NOISSUER +%s - QR : %s +; +; --- GUI: bare-secret add form (secret_meta_request) ---------------------- +; +MSG_GUI_SECRET_META_HINT +Un secret nu ne porte aucun nom - pour qui est ce compte ? +; +MSG_GUI_ADD_BTN +Ajouter +; +MSG_GUI_ISSUER_LABEL_REQUIRED +L'émetteur et l'étiquette sont tous deux requis. +; +; --- GUI: commodity broker (Exchange) ------------------------------------- +; +MSG_GUI_CX_DESCR +Authentification TOTP/HOTP +; +; --- GUI: remove confirmation --------------------------------------------- +; +MSG_GUI_REMOVE_CONFIRM +Supprimer %s ? +; +MSG_GUI_REMOVE_BTNS +Supprimer|Annuler +; +; --- GUI: startup failures (open_libs/main) ------------------------------- +; +MSG_GUI_NEEDS_INTUITION +nécessite intuition.library / utility.library v37+ (OS 2.04) +; +MSG_GUI_NEEDS_REACTION +nécessite les classes ReAction/ClassAct (window/layout/listbrowser/fuelgauge/button) +; +MSG_GUI_WINDOW_CREATE_FAILED +%s : impossible de créer la fenêtre\n +; +MSG_GUI_WINDOW_OPEN_FAILED +%s : impossible d'ouvrir la fenêtre\n diff --git a/locale/drafts/italiano.ct b/locale/drafts/italiano.ct new file mode 100644 index 0000000..c41df7d --- /dev/null +++ b/locale/drafts/italiano.ct @@ -0,0 +1,522 @@ +; italiano.ct - DRAFT Italian translation of locale/AmiAuth.cd. +; +; MACHINE-GENERATED FIRST PASS, NOT REVIEWED BY A NATIVE SPEAKER. +; See locale/drafts/README.md before using or building this file - it is +; not wired into any build target and must not be installed as-is. +; +; Terminology used throughout, for reviewer consistency: +; vault -> cassaforte passphrase -> passphrase (kept, common IT usage) +; account -> account (kept, common IT tech usage) / conto avoided (bank sense) +; issuer -> emittente label -> etichetta +; re-key -> ricifrare / ricifratura +; always-unlocked -> sempre sbloccato +; +##version $VER: AmiAuth.catalog 1.0 (28.07.2026) +##language italiano +##codeset 0 +; +; --- CLI (src/cli/main.c) --------------------------------------------- +; +MSG_CLI_ALREADY_EXISTS +%s: %s esiste già\n +; +MSG_CLI_ADDED +%s:%s aggiunto\n +; +MSG_CLI_REMOVED +'%s' rimosso\n +; +MSG_CLI_USAGE_RUNAS +Esegui come '%s ...' ('%s ?' mostra il modello degli argomenti):\n +; +; --- GUI (src/gui/main.c): account list column headers ----------------- +; +MSG_GUI_COL_ACCOUNT +Account +; +MSG_GUI_COL_CODE +Codice +; +MSG_GUI_COL_LEFT +Rimasto +; +; --- GUI: requester text ------------------------------------------------- +; +MSG_GUI_CANNOT_OPEN_VAULT +Impossibile aprire la cassaforte:\n%s +; +MSG_GUI_BAD_URI +Non è un URI otpauth:// valido. +; +MSG_GUI_BAD_SECRET +Non è né un URI otpauth:// né un secret Base32. +; +MSG_GUI_NO_QR +Nessun codice QR otpauth trovato in questa immagine. +; +MSG_GUI_SAVE_FAILED +Impossibile salvare la cassaforte. +; +MSG_GUI_REKEY_DONE +Cassaforte ricifrata per questo computer. +; +MSG_GUI_REKEY_FAILED +Ricifratura non riuscita; la cassaforte resta invariata. +; +; --- GUI: common requester button labels ---------------------------------- +; +MSG_GUI_OK +OK +; +MSG_GUI_CANCEL +Annulla +; +; --- CLI: vault_err()'s own strings, wrapped by MSG_CLI_ERR below --------- +; +MSG_ERR_IO +impossibile leggere/scrivere il file della cassaforte +; +MSG_ERR_FORMAT +file di cassaforte non valido +; +MSG_ERR_AUTH +passphrase errata o file manomesso +; +MSG_ERR_LOCKED +la cassaforte è bloccata +; +MSG_ERR_FULL +la cassaforte è piena +; +MSG_ERR_RANGE +account non trovato +; +MSG_ERR_UNKNOWN +errore +; +; --- CLI: generic error wrapper ("AmiAuth" + vault_err()/other message) -- +; +MSG_CLI_ERR +%s: %s\n +; +MSG_CLI_WARN_HOTP_PERSIST +%s: attenzione: impossibile salvare il contatore HOTP (%s)\n +; +; --- CLI: passphrase prompts ---------------------------------------------- +; +MSG_CLI_PROMPT_PASSPHRASE +Passphrase: +; +MSG_CLI_NO_TTY +%s: questa cassaforte è cifrata; eseguire da un terminale interattivo, oppure usare una cassaforte sempre sbloccata per gli script\n +; +MSG_CLI_NO_RNG_SAVE +%s: nessuna fonte casuale sicura per salvare una cassaforte cifrata (Fase 4)\n +; +; --- CLI: adaptive re-key (maybe_rekey) ----------------------------------- +; +; REVIEWER: the letter(s) in "(x)" and the word in '...' below are read back +; out of this translated string at runtime (locale/AmiAuth.cd's header has +; the full convention) - s/n/m and 'si' are just this draft's choice, freely +; change them, just keep exactly one "(x)" per choice in the same +; left-to-right order, with the accept and never-ask letters distinct. +MSG_CLI_REKEY_STRENGTHEN_PROMPT +Questo computer è molto più veloce di quello che ha protetto questa cassaforte.\nRafforzarla ora? [(s)ì/(n)o/(m)ai chiedere qui] +; +MSG_CLI_REKEY_SLOW_NOTICE +%s: sblocco durato ~%lus; questa cassaforte è stata calibrata per hardware più veloce.\n +; +MSG_CLI_REKEY_LOWER_PROMPT +Indebolire qui per sblocchi più rapidi? Questo RIDUCE la sicurezza. [(s)ì/(n)o] +; +MSG_CLI_CONFIRM_YES_PROMPT +Digita 'si' per confermare: +; +MSG_CLI_REKEYED +%s: ricifrata con %lu iterazioni\n +; +MSG_CLI_REKEY_FAILED +%s: ricifratura non riuscita; cassaforte invariata\n +; +; --- CLI: CODE --------------------------------------------------------- +; +MSG_CLI_BAD_SECRET +%s: secret Base32 non valido o vuoto\n +; +MSG_CLI_SECONDS_REMAINING +(%u secondi rimanenti)\n +; +; --- CLI: CLOCK/SYNC/OFFSET/NUDGE --------------------------------------- +; +MSG_CLI_CLOCK_SYNCED +sincronizzato (verde) +; +MSG_CLI_CLOCK_MANUAL +scarto applicato (ambra) +; +MSG_CLI_CLOCK_UNVERIFIED +non verificato (rosso) +; +MSG_CLI_CLOCK_OFFSET_LINE +scarto UTC : %+ld secondi (%+ld min)\n +; +MSG_CLI_CLOCK_STATUS_LINE +stato : %s\n +; +MSG_CLI_CLOCK_CORRECTED_LINE +corretto : %04d-%02d-%02d %02d:%02d:%02d UTC\n +; +MSG_CLI_QUERYING +Interrogazione di %s ...\n +; +MSG_CLI_SYNC_FAILED +%s: sincronizzazione SNTP non riuscita (nessuno stack TCP/IP, o nessuna risposta da %s)\n +; +MSG_CLI_OFFSET_SAVE_FAILED +%s: impossibile salvare lo scarto\n +; +; --- CLI: INIT ----------------------------------------------------------- +; +MSG_CLI_INIT_PASS_PROMPT +Nuova passphrase (vuota per una cassaforte sempre sbloccata): +; +MSG_CLI_INIT_NO_TTY +%s: INIT richiede un terminale interattivo (usare 'INIT --open' per una cassaforte sempre sbloccata non interattiva)\n +; +MSG_CONFIRM_PASSPHRASE +Conferma la passphrase: +; +MSG_CLI_PASS_MISMATCH +%s: le passphrase non corrispondono\n +; +MSG_CLI_INIT_NO_RNG +%s: nessuna fonte casuale sicura per una cassaforte cifrata (Fase 4); creare una cassaforte sempre sbloccata con 'INIT --open'\n +; +MSG_CLI_KDF_ITERATIONS +%s: iterazioni KDF = %lu\n +; +MSG_CLI_CREATED +Cassaforte (%s) creata in %s\n +; +MSG_CLI_ALWAYS_UNLOCKED +sempre sbloccata +; +MSG_CLI_ENCRYPTED +cifrata +; +; --- CLI: GUI forwarding (try_forward) ------------------------------------ +; +MSG_CLI_GUI_LOCKED +%s: la cassaforte dell'interfaccia grafica in esecuzione è bloccata; sbloccarla prima lì\n +; +MSG_CLI_NO_ACCOUNT +%s: nessun account corrispondente a '%s'\n +; +MSG_CLI_VAULT_FULL +%s: la cassaforte è piena (max 64 account)\n +; +MSG_CLI_BAD_OTPURI +%s: non è un URI otpauth:// valido\n +; +MSG_CLI_GUI_SAVEFAIL +%s: applicato nell'interfaccia grafica ma il nuovo salvataggio non è riuscito\n +; +MSG_CLI_URI_TOO_LONG +%s: l'URI di questo account è troppo lungo per essere codificato come codice QR\n +; +MSG_CLI_GUI_UNKNOWN +%s: l'interfaccia grafica in esecuzione non ha potuto gestirlo\n +; +; --- CLI: ADD -------------------------------------------------------------- +; +MSG_CLI_PARSE_FAILED +%s: impossibile analizzare l'URI otpauth://\n +; +MSG_CLI_ADDED_NOISSUER +%s aggiunto\n +; +MSG_CLI_BAD_BASE32 +%s: non sembra un secret Base32\n +; +; --- CLI: SHOW / usage ----------------------------------------------------- +; +MSG_CLI_NO_GUI +%s: nessuna interfaccia grafica in esecuzione da mostrare\n +; +MSG_CLI_TAGLINE +%s - autenticatore TOTP/HOTP per AmigaOS\n\n +; +MSG_CLI_ENCRYPTED_NOTE +Le casseforti cifrate richiedono la passphrase nel terminale.\n +; +; --- GUI: main menu (g_menu[]) --------------------------------------------- +; +MSG_GUI_MENU_PROJECT +Progetto +; +MSG_GUI_QUIT +Esci +; +MSG_GUI_MENU_ACCOUNT +Account +; +MSG_GUI_MENU_ADD_CLIP +Aggiungi dagli appunti +; +MSG_GUI_MENU_ADD_TYPE +Aggiungi (digita URI/secret)... +; +MSG_GUI_MENU_ADD_QR +Aggiungi da immagine QR... +; +MSG_GUI_MENU_EDIT +Modifica selezionato... +; +MSG_GUI_MENU_COPY +Copia codice +; +MSG_GUI_MENU_SHOW_QR +Mostra codice QR... +; +MSG_GUI_MENU_REMOVE +Rimuovi selezionato... +; +; --- GUI: toolbar button mnemonics (LBL_*, see the underscore note above) - +; REVIEWER: the '_' MUST stay the very first character of each string (the +; code reads a fixed index, not "wherever the underscore is") - so the +; mnemonic is always the first letter of whichever word you choose. The six +; letters below (A/M/R/C/D/P) are distinct from each other. +; +MSG_GUI_BTN_ADD +_Aggiungi +; +MSG_GUI_BTN_EDIT +_Modifica +; +MSG_GUI_BTN_REMOVE +_Rimuovi +; +MSG_GUI_BTN_COPY +_Copia +; +MSG_GUI_BTN_NUDGE_DOWN +_D -10s +; +MSG_GUI_BTN_NUDGE_UP +_P +10s +; +; --- GUI: copy-code button's transient states (no mnemonic) --------------- +; +MSG_GUI_COPY_PLAIN +Copia +; +MSG_GUI_COPIED +Copiato +; +; --- GUI: clock-status line -------------------------------------------- +; +MSG_GUI_CLOCK_SYNCED +sincronizzato +; +MSG_GUI_CLOCK_MANUAL +manuale +; +MSG_GUI_CLOCK_UNVERIFIED +non verificato +; +MSG_GUI_CLOCK_LINE_SHORT +Orologio: %s +; +MSG_GUI_CLOCK_LINE_FULL +Orologio: %s %s%lu:%02lu +; +; --- GUI: adaptive re-key (gui_maybe_rekey) ------------------------------- +; +MSG_GUI_REKEY_STRENGTHEN_BODY +Questo computer è molto più veloce di quello che ha protetto questa cassaforte.\nRafforzarla ora (ricifrare con più iterazioni PBKDF2)? +; +MSG_GUI_REKEY_STRENGTHEN_BTNS +Rafforza|Non ora|Mai qui +; +MSG_GUI_REKEY_SLOW_BODY +Lo sblocco è durato circa %lu secondi; questa cassaforte è stata calibrata per hardware più veloce.\nIndebolire qui per sblocchi più rapidi? Questo RIDUCE la sicurezza. +; +MSG_GUI_REKEY_LOWER_BTNS +Indebolisci|Annulla +; +MSG_GUI_REKEY_CONFIRM_BODY +Ridurre davvero la sicurezza di questa cassaforte? +; +MSG_GUI_REKEY_REDUCE_BTNS +Riduci|Annulla +; +; --- GUI: add account (uri/secret, shared gui_add_uri/gui_add_secret) ---- +; +MSG_GUI_VAULT_FULL +La cassaforte è piena (max 64 account). +; +; --- GUI: QR import (do_add_qr_impl) -------------------------------------- +; +MSG_GUI_IMG_TOOBIG +Questa immagine è troppo grande per essere decodificata. +; +MSG_GUI_QR_UNAVAIL +L'importazione QR richiede datatypes.library. +; +MSG_GUI_IMG_NOMEM +Memoria insufficiente per caricare questa immagine. +; +MSG_GUI_IMG_READ_FAILED +Impossibile leggere questa immagine (richiede picture.datatype). +; +MSG_GUI_TITLE_DECODING +%s - Decodifica immagine QR... +; +MSG_GUI_QR_FILE_TITLE +Seleziona un'immagine QR +; +; --- GUI: unlock prompt (passphrase_request title) ------------------------ +; +MSG_GUI_TITLE_UNLOCK +%s - Sblocca +; +; --- GUI: first-run vault creation ----------------------------------------- +; +MSG_GUI_CHOOSE_LOCATION +Scegli dove conservare la cassaforte +; +MSG_GUI_WELCOME +Benvenuto in %s!\n\nNon esiste ancora una cassaforte - verrà creata in\n%s +; +MSG_GUI_CREATE_VAULT_BTNS_DEFER +Crea una cassaforte...|Non ora +; +MSG_GUI_CREATE_VAULT_BTNS_QUIT +Crea una cassaforte...|Esci +; +MSG_GUI_NEW_PASS_PROMPT +Nuova passphrase (vuota = sempre sbloccata): +; +MSG_GUI_STORE_UNENCRYPTED_BODY +Salvare la cassaforte NON CIFRATA?\n\nSenza passphrase non c'è protezione a riposo:\nchiunque abbia accesso al file può leggere i tuoi secret.\n(Potrai aggiungere una passphrase più tardi.) +; +MSG_GUI_STORE_UNENCRYPTED_BTNS +Salva senza cifrare|Indietro +; +MSG_GUI_PASS_MISMATCH +Le passphrase non corrispondono. +; +MSG_GUI_TRY_AGAIN +Riprova +; +MSG_GUI_NO_RNG_BODY +Nessuna fonte casuale sicura è disponibile, quindi una\ncassaforte cifrata non può essere creata in modo sicuro. +; +MSG_GUI_CANNOT_WRITE_BODY +Impossibile scrivere la cassaforte in\n%s\n\nScegliere un'altra posizione? +; +MSG_GUI_CHOOSE_LOCATION_BTNS +Scegli posizione...|Esci +; +MSG_GUI_CREATE_FAILED +Creazione della cassaforte non riuscita. +; +; --- GUI: unlock loop (gui_open_vault) -------------------------------- +; +MSG_GUI_ENTER_PASS +Inserisci la passphrase: +; +MSG_GUI_WRONG_PASS +Passphrase errata - riprova: +; +MSG_GUI_CANNOT_OPEN_PRINTF +%s: impossibile aprire la cassaforte (%ld)\n +; +; --- GUI: add account form (uri_request) ------------------------------ +; +MSG_GUI_URI_PASTE_LABEL +Incolla o digita un URI otpauth:// o un secret Base32: +; +MSG_GUI_TITLE_ADD_ACCOUNT +%s - Aggiungi account +; +; --- GUI: edit account form (edit_request) ------------------------------ +; +MSG_GUI_LABEL_ISSUER +Emittente: +; +MSG_GUI_LABEL_LABEL +Etichetta: +; +MSG_GUI_LABEL_DIGITS +Cifre: +; +MSG_GUI_LABEL_PERIOD +Intervallo: +; +MSG_GUI_TITLE_EDIT_ACCOUNT +%s - Modifica account +; +MSG_GUI_LABEL_REQUIRED_STEAM +L'etichetta è obbligatoria. +; +MSG_GUI_LABEL_REQUIRED_FULL +L'etichetta è obbligatoria; le cifre devono essere 6-8, l'intervallo 1-86400. +; +; --- GUI: QR export display (qr_display) -------------------------------- +; +MSG_GUI_QR_BUILD_FAILED +Impossibile creare un codice QR per questo account. +; +MSG_GUI_QR_TOO_LONG +L'URI di questo account è troppo lungo per essere codificato come codice QR. +; +MSG_GUI_QR_DISPLAY_FAILED +Impossibile creare la visualizzazione QR. +; +MSG_GUI_QR_WINDOW_FAILED +Impossibile aprire la finestra QR. +; +MSG_GUI_TITLE_QR_ISSUER +%s - QR: %s:%s +; +MSG_GUI_TITLE_QR_NOISSUER +%s - QR: %s +; +; --- GUI: bare-secret add form (secret_meta_request) ---------------------- +; +MSG_GUI_SECRET_META_HINT +Un secret puro non porta alcun nome - per chi è questo account? +; +MSG_GUI_ADD_BTN +Aggiungi +; +MSG_GUI_ISSUER_LABEL_REQUIRED +Emittente ed etichetta sono entrambi obbligatori. +; +; --- GUI: commodity broker (Exchange) ------------------------------------- +; +MSG_GUI_CX_DESCR +Autenticatore TOTP/HOTP +; +; --- GUI: remove confirmation --------------------------------------------- +; +MSG_GUI_REMOVE_CONFIRM +Rimuovere %s? +; +MSG_GUI_REMOVE_BTNS +Rimuovi|Annulla +; +; --- GUI: startup failures (open_libs/main) ------------------------------- +; +MSG_GUI_NEEDS_INTUITION +richiede intuition.library / utility.library v37+ (OS 2.04) +; +MSG_GUI_NEEDS_REACTION +richiede le classi ReAction/ClassAct (window/layout/listbrowser/fuelgauge/button) +; +MSG_GUI_WINDOW_CREATE_FAILED +%s: impossibile creare la finestra\n +; +MSG_GUI_WINDOW_OPEN_FAILED +%s: impossibile aprire la finestra\n diff --git a/locale/drafts/polski.ct b/locale/drafts/polski.ct new file mode 100644 index 0000000..21ad7a1 --- /dev/null +++ b/locale/drafts/polski.ct @@ -0,0 +1,539 @@ +; polski.ct - DRAFT Polish translation of locale/AmiAuth.cd. +; +; MACHINE-GENERATED FIRST PASS, NOT REVIEWED BY A NATIVE SPEAKER. +; See locale/drafts/README.md before using or building this file - it is +; not wired into any build target and must not be installed as-is. +; +; REVIEWER, IMPORTANT: this draft deliberately writes Polish WITHOUT +; diacritics (a/c/e/l/n/o/s/z instead of the nine accented Polish letters) - +; "polski bez ogonkow", the traditional fallback on platforms whose charset +; can't represent them. This isn't a stylistic choice: FlexCat's own docs +; state the catalog ##codeset field "must be 0" (no Latin-2 support in this +; classic catalog format), and Latin-1/ISO-8859-1 - what codeset 0 actually +; is - only happens to contain one of the nine Polish diacritics (o-acute), +; not the rest. Adding proper diacritics back in would look right in this +; file's UTF-8 source but render as garbage (or fail to encode at all - see +; tools/check_catalog.py's Latin-1 check) once compiled for real AmigaOS. +; If your review confirms otherwise for a specific runtime/codeset, README.md +; here is the place to correct this note. +; +; Terminology used throughout, for reviewer consistency: +; vault -> sejf passphrase -> fraza haslo +; account -> konto secret -> secret (kept, common PL tech usage) +; issuer -> wystawca label -> etykieta +; re-key -> przeszyfrowac / przeszyfrowanie +; always-unlocked -> zawsze odblokowany +; +##version $VER: AmiAuth.catalog 1.0 (28.07.2026) +##language polski +##codeset 0 +; +; --- CLI (src/cli/main.c) --------------------------------------------- +; +MSG_CLI_ALREADY_EXISTS +%s: %s juz istnieje\n +; +MSG_CLI_ADDED +%s:%s dodano\n +; +MSG_CLI_REMOVED +usunieto '%s'\n +; +MSG_CLI_USAGE_RUNAS +Uruchom jako '%s ...' ('%s ?' pokazuje szablon argumentow):\n +; +; --- GUI (src/gui/main.c): account list column headers ----------------- +; +MSG_GUI_COL_ACCOUNT +Konto +; +MSG_GUI_COL_CODE +Kod +; +MSG_GUI_COL_LEFT +Pozostalo +; +; --- GUI: requester text ------------------------------------------------- +; +MSG_GUI_CANNOT_OPEN_VAULT +Nie mozna otworzyc sejfu:\n%s +; +MSG_GUI_BAD_URI +To nie jest poprawny URI otpauth://. +; +MSG_GUI_BAD_SECRET +To nie jest ani URI otpauth://, ani secret Base32. +; +MSG_GUI_NO_QR +Nie znaleziono kodu QR otpauth w tym obrazie. +; +MSG_GUI_SAVE_FAILED +Nie mozna zapisac sejfu. +; +MSG_GUI_REKEY_DONE +Sejf przeszyfrowano dla tego komputera. +; +MSG_GUI_REKEY_FAILED +Przeszyfrowanie nie powiodlo sie; sejf pozostaje bez zmian. +; +; --- GUI: common requester button labels ---------------------------------- +; +MSG_GUI_OK +OK +; +MSG_GUI_CANCEL +Anuluj +; +; --- CLI: vault_err()'s own strings, wrapped by MSG_CLI_ERR below --------- +; +MSG_ERR_IO +nie mozna odczytac/zapisac pliku sejfu +; +MSG_ERR_FORMAT +nieprawidlowy plik sejfu +; +MSG_ERR_AUTH +bledna fraza haslo lub plik zostal naruszony +; +MSG_ERR_LOCKED +sejf jest zablokowany +; +MSG_ERR_FULL +sejf jest pelny +; +MSG_ERR_RANGE +nie ma takiego konta +; +MSG_ERR_UNKNOWN +blad +; +; --- CLI: generic error wrapper ("AmiAuth" + vault_err()/other message) -- +; +MSG_CLI_ERR +%s: %s\n +; +MSG_CLI_WARN_HOTP_PERSIST +%s: uwaga: nie mozna zapisac licznika HOTP (%s)\n +; +; --- CLI: passphrase prompts ---------------------------------------------- +; +MSG_CLI_PROMPT_PASSPHRASE +Fraza haslo: +; +MSG_CLI_NO_TTY +%s: ten sejf jest zaszyfrowany; uruchom z terminala interaktywnego, albo uzyj zawsze odblokowanego sejfu do skryptow\n +; +MSG_CLI_NO_RNG_SAVE +%s: brak bezpiecznego zrodla losowosci, aby zapisac zaszyfrowany sejf (Faza 4)\n +; +; --- CLI: adaptive re-key (maybe_rekey) ----------------------------------- +; +; REVIEWER: the letter(s) in "(x)" and the word in '...' below are read back +; out of this translated string at runtime (locale/AmiAuth.cd's header has +; the full convention) - t/n/y and 'tak' are just this draft's choice, +; freely change them, just keep exactly one "(x)" per choice in the same +; left-to-right order, with the accept and never-ask letters distinct. The +; never-ask letter is marked mid-word ("nigd(y)") rather than on "nigdy"'s +; own first letter, purely to avoid visually duplicating "(n)ie"'s marker - +; the CLI convention (unlike the GUI's) allows marking any letter, not just +; the first. +MSG_CLI_REKEY_STRENGTHEN_PROMPT +Ten komputer jest znacznie szybszy niz ten, ktory zabezpieczyl ten sejf.\nWzmocnic teraz? [(t)ak/(n)ie/nigd(y) nie pytaj tutaj] +; +MSG_CLI_REKEY_SLOW_NOTICE +%s: odblokowanie trwalo ~%lus; ten sejf byl skalibrowany dla szybszego sprzetu.\n +; +MSG_CLI_REKEY_LOWER_PROMPT +Oslabic tutaj dla szybszego odblokowania? To ZMNIEJSZA bezpieczenstwo. [(t)ak/(n)ie] +; +MSG_CLI_CONFIRM_YES_PROMPT +Wpisz 'tak' aby potwierdzic: +; +MSG_CLI_REKEYED +%s: przeszyfrowano z %lu iteracjami\n +; +MSG_CLI_REKEY_FAILED +%s: przeszyfrowanie nie powiodlo sie; sejf bez zmian\n +; +; --- CLI: CODE --------------------------------------------------------- +; +MSG_CLI_BAD_SECRET +%s: nieprawidlowy lub pusty secret Base32\n +; +MSG_CLI_SECONDS_REMAINING +(pozostalo %u sekund)\n +; +; --- CLI: CLOCK/SYNC/OFFSET/NUDGE --------------------------------------- +; +MSG_CLI_CLOCK_SYNCED +zsynchronizowany (zielony) +; +MSG_CLI_CLOCK_MANUAL +przesuniecie zastosowane (bursztynowy) +; +MSG_CLI_CLOCK_UNVERIFIED +niezweryfikowany (czerwony) +; +MSG_CLI_CLOCK_OFFSET_LINE +przesuniecie UTC : %+ld sekund (%+ld min)\n +; +MSG_CLI_CLOCK_STATUS_LINE +status : %s\n +; +MSG_CLI_CLOCK_CORRECTED_LINE +skorygowano : %04d-%02d-%02d %02d:%02d:%02d UTC\n +; +MSG_CLI_QUERYING +Odpytywanie %s ...\n +; +MSG_CLI_SYNC_FAILED +%s: synchronizacja SNTP nie powiodla sie (brak stosu TCP/IP lub brak odpowiedzi od %s)\n +; +MSG_CLI_OFFSET_SAVE_FAILED +%s: nie mozna zapisac przesuniecia\n +; +; --- CLI: INIT ----------------------------------------------------------- +; +MSG_CLI_INIT_PASS_PROMPT +Nowa fraza haslo (pusta dla zawsze odblokowanego sejfu): +; +MSG_CLI_INIT_NO_TTY +%s: INIT wymaga terminala interaktywnego (uzyj 'INIT --open' dla nieinteraktywnego, zawsze odblokowanego sejfu)\n +; +MSG_CONFIRM_PASSPHRASE +Potwierdz fraze haslo: +; +MSG_CLI_PASS_MISMATCH +%s: frazy haslo nie sa zgodne\n +; +MSG_CLI_INIT_NO_RNG +%s: brak bezpiecznego zrodla losowosci dla zaszyfrowanego sejfu (Faza 4); utworz zawsze odblokowany sejf poleceniem 'INIT --open'\n +; +MSG_CLI_KDF_ITERATIONS +%s: iteracje KDF = %lu\n +; +MSG_CLI_CREATED +Utworzono sejf (%s) w %s\n +; +MSG_CLI_ALWAYS_UNLOCKED +zawsze odblokowany +; +MSG_CLI_ENCRYPTED +zaszyfrowany +; +; --- CLI: GUI forwarding (try_forward) ------------------------------------ +; +MSG_CLI_GUI_LOCKED +%s: sejf dzialajacego interfejsu graficznego jest zablokowany; odblokuj go najpierw tam\n +; +MSG_CLI_NO_ACCOUNT +%s: brak konta pasujacego do '%s'\n +; +MSG_CLI_VAULT_FULL +%s: sejf jest pelny (maks. 64 konta)\n +; +MSG_CLI_BAD_OTPURI +%s: to nie jest poprawny URI otpauth://\n +; +MSG_CLI_GUI_SAVEFAIL +%s: zastosowano w interfejsie graficznym, ale ponowny zapis nie powiodl sie\n +; +MSG_CLI_URI_TOO_LONG +%s: URI tego konta jest zbyt dlugi, aby zakodowac go jako kod QR\n +; +MSG_CLI_GUI_UNKNOWN +%s: dzialajacy interfejs graficzny nie mogl tego obsluzyc\n +; +; --- CLI: ADD -------------------------------------------------------------- +; +MSG_CLI_PARSE_FAILED +%s: nie mozna przetworzyc URI otpauth://\n +; +MSG_CLI_ADDED_NOISSUER +dodano %s\n +; +MSG_CLI_BAD_BASE32 +%s: to nie wyglada na secret Base32\n +; +; --- CLI: SHOW / usage ----------------------------------------------------- +; +MSG_CLI_NO_GUI +%s: brak dzialajacego interfejsu graficznego do pokazania\n +; +MSG_CLI_TAGLINE +%s - uwierzytelnianie TOTP/HOTP dla AmigaOS\n\n +; +MSG_CLI_ENCRYPTED_NOTE +Zaszyfrowane sejfy pytaja o fraze haslo w terminalu.\n +; +; --- GUI: main menu (g_menu[]) --------------------------------------------- +; +MSG_GUI_MENU_PROJECT +Projekt +; +MSG_GUI_QUIT +Zakoncz +; +MSG_GUI_MENU_ACCOUNT +Konto +; +MSG_GUI_MENU_ADD_CLIP +Dodaj ze schowka +; +MSG_GUI_MENU_ADD_TYPE +Dodaj (wpisz URI/secret)... +; +MSG_GUI_MENU_ADD_QR +Dodaj z obrazu QR... +; +MSG_GUI_MENU_EDIT +Edytuj zaznaczone... +; +MSG_GUI_MENU_COPY +Kopiuj kod +; +MSG_GUI_MENU_SHOW_QR +Pokaz kod QR... +; +MSG_GUI_MENU_REMOVE +Usun zaznaczone... +; +; --- GUI: toolbar button mnemonics (LBL_*, see the underscore note above) - +; REVIEWER: the '_' MUST stay the very first character of each string (the +; code reads a fixed index, not "wherever the underscore is") - so the +; mnemonic is always the first letter of whichever word you choose. The six +; letters below (D/E/U/K/M/P) are distinct from each other. +; +MSG_GUI_BTN_ADD +_Dodaj +; +MSG_GUI_BTN_EDIT +_Edytuj +; +MSG_GUI_BTN_REMOVE +_Usun +; +MSG_GUI_BTN_COPY +_Kopiuj +; +MSG_GUI_BTN_NUDGE_DOWN +_M -10s +; +MSG_GUI_BTN_NUDGE_UP +_P +10s +; +; --- GUI: copy-code button's transient states (no mnemonic) --------------- +; +MSG_GUI_COPY_PLAIN +Kopiuj +; +MSG_GUI_COPIED +Skopiowano +; +; --- GUI: clock-status line -------------------------------------------- +; +MSG_GUI_CLOCK_SYNCED +zsynchronizowany +; +MSG_GUI_CLOCK_MANUAL +reczny +; +MSG_GUI_CLOCK_UNVERIFIED +niezweryfikowany +; +MSG_GUI_CLOCK_LINE_SHORT +Zegar: %s +; +MSG_GUI_CLOCK_LINE_FULL +Zegar: %s %s%lu:%02lu +; +; --- GUI: adaptive re-key (gui_maybe_rekey) ------------------------------- +; +MSG_GUI_REKEY_STRENGTHEN_BODY +Ten komputer jest znacznie szybszy niz ten, ktory zabezpieczyl ten sejf.\nWzmocnic teraz (przeszyfrowac z wieksza liczba iteracji PBKDF2)? +; +MSG_GUI_REKEY_STRENGTHEN_BTNS +Wzmocnij|Nie teraz|Nigdy tutaj +; +MSG_GUI_REKEY_SLOW_BODY +Odblokowanie trwalo okolo %lu sekund; ten sejf byl skalibrowany dla szybszego sprzetu.\nOslabic tutaj dla szybszego odblokowania? To ZMNIEJSZA bezpieczenstwo. +; +MSG_GUI_REKEY_LOWER_BTNS +Oslab|Anuluj +; +MSG_GUI_REKEY_CONFIRM_BODY +Naprawde zmniejszyc bezpieczenstwo tego sejfu? +; +MSG_GUI_REKEY_REDUCE_BTNS +Zmniejsz|Anuluj +; +; --- GUI: add account (uri/secret, shared gui_add_uri/gui_add_secret) ---- +; +MSG_GUI_VAULT_FULL +Sejf jest pelny (maks. 64 konta). +; +; --- GUI: QR import (do_add_qr_impl) -------------------------------------- +; +MSG_GUI_IMG_TOOBIG +Ten obraz jest zbyt duzy, aby go zdekodowac. +; +MSG_GUI_QR_UNAVAIL +Import QR wymaga datatypes.library. +; +MSG_GUI_IMG_NOMEM +Za malo pamieci, aby zaladowac ten obraz. +; +MSG_GUI_IMG_READ_FAILED +Nie mozna odczytac tego obrazu (wymaga picture.datatype). +; +MSG_GUI_TITLE_DECODING +%s - Dekodowanie obrazu QR... +; +MSG_GUI_QR_FILE_TITLE +Wybierz obraz QR +; +; --- GUI: unlock prompt (passphrase_request title) ------------------------ +; +MSG_GUI_TITLE_UNLOCK +%s - Odblokuj +; +; --- GUI: first-run vault creation ----------------------------------------- +; +MSG_GUI_CHOOSE_LOCATION +Wybierz, gdzie przechowywac sejf +; +MSG_GUI_WELCOME +Witamy w %s!\n\nNie ma jeszcze sejfu - zostanie utworzony w\n%s +; +MSG_GUI_CREATE_VAULT_BTNS_DEFER +Utworz sejf...|Nie teraz +; +MSG_GUI_CREATE_VAULT_BTNS_QUIT +Utworz sejf...|Zakoncz +; +MSG_GUI_NEW_PASS_PROMPT +Nowa fraza haslo (pusta = zawsze odblokowany): +; +MSG_GUI_STORE_UNENCRYPTED_BODY +Zapisac sejf BEZ SZYFROWANIA?\n\nBez frazy haslo nie ma ochrony danych w spoczynku:\nkazdy z dostepem do pliku moze odczytac twoje secrety.\n(Fraze haslo mozna dodac pozniej.) +; +MSG_GUI_STORE_UNENCRYPTED_BTNS +Zapisz bez szyfrowania|Wroc +; +MSG_GUI_PASS_MISMATCH +Frazy haslo nie sa zgodne. +; +MSG_GUI_TRY_AGAIN +Sprobuj ponownie +; +MSG_GUI_NO_RNG_BODY +Brak bezpiecznego zrodla losowosci, wiec\nzaszyfrowanego sejfu nie mozna bezpiecznie utworzyc. +; +MSG_GUI_CANNOT_WRITE_BODY +Nie mozna zapisac sejfu do\n%s\n\nWybrac inna lokalizacje? +; +MSG_GUI_CHOOSE_LOCATION_BTNS +Wybierz lokalizacje...|Zakoncz +; +MSG_GUI_CREATE_FAILED +Tworzenie sejfu nie powiodlo sie. +; +; --- GUI: unlock loop (gui_open_vault) -------------------------------- +; +MSG_GUI_ENTER_PASS +Wprowadz fraze haslo: +; +MSG_GUI_WRONG_PASS +Bledna fraza haslo - sprobuj ponownie: +; +MSG_GUI_CANNOT_OPEN_PRINTF +%s: nie mozna otworzyc sejfu (%ld)\n +; +; --- GUI: add account form (uri_request) ------------------------------ +; +MSG_GUI_URI_PASTE_LABEL +Wklej lub wpisz URI otpauth:// albo secret Base32: +; +MSG_GUI_TITLE_ADD_ACCOUNT +%s - Dodaj konto +; +; --- GUI: edit account form (edit_request) ------------------------------ +; +MSG_GUI_LABEL_ISSUER +Wystawca: +; +MSG_GUI_LABEL_LABEL +Etykieta: +; +MSG_GUI_LABEL_DIGITS +Cyfry: +; +MSG_GUI_LABEL_PERIOD +Interwal: +; +MSG_GUI_TITLE_EDIT_ACCOUNT +%s - Edytuj konto +; +MSG_GUI_LABEL_REQUIRED_STEAM +Etykieta jest wymagana. +; +MSG_GUI_LABEL_REQUIRED_FULL +Etykieta jest wymagana; cyfry musza byc 6-8, interwal 1-86400. +; +; --- GUI: QR export display (qr_display) -------------------------------- +; +MSG_GUI_QR_BUILD_FAILED +Nie mozna utworzyc kodu QR dla tego konta. +; +MSG_GUI_QR_TOO_LONG +URI tego konta jest zbyt dlugi, aby zakodowac go jako kod QR. +; +MSG_GUI_QR_DISPLAY_FAILED +Nie mozna utworzyc widoku QR. +; +MSG_GUI_QR_WINDOW_FAILED +Nie mozna otworzyc okna QR. +; +MSG_GUI_TITLE_QR_ISSUER +%s - QR: %s:%s +; +MSG_GUI_TITLE_QR_NOISSUER +%s - QR: %s +; +; --- GUI: bare-secret add form (secret_meta_request) ---------------------- +; +MSG_GUI_SECRET_META_HINT +Sam secret nie ma nazwy - dla kogo jest to konto? +; +MSG_GUI_ADD_BTN +Dodaj +; +MSG_GUI_ISSUER_LABEL_REQUIRED +Wystawca i etykieta sa oba wymagane. +; +; --- GUI: commodity broker (Exchange) ------------------------------------- +; +MSG_GUI_CX_DESCR +Uwierzytelnianie TOTP/HOTP +; +; --- GUI: remove confirmation --------------------------------------------- +; +MSG_GUI_REMOVE_CONFIRM +Usunac %s? +; +MSG_GUI_REMOVE_BTNS +Usun|Anuluj +; +; --- GUI: startup failures (open_libs/main) ------------------------------- +; +MSG_GUI_NEEDS_INTUITION +wymaga intuition.library / utility.library v37+ (OS 2.04) +; +MSG_GUI_NEEDS_REACTION +wymaga klas ReAction/ClassAct (window/layout/listbrowser/fuelgauge/button) +; +MSG_GUI_WINDOW_CREATE_FAILED +%s: nie mozna utworzyc okna\n +; +MSG_GUI_WINDOW_OPEN_FAILED +%s: nie mozna otworzyc okna\n diff --git a/tools/check_catalog.py b/tools/check_catalog.py index 77a2224..b3bc7ce 100644 --- a/tools/check_catalog.py +++ b/tools/check_catalog.py @@ -2,7 +2,9 @@ """check_catalog.py - structural sanity checks for locale/AmiAuth.cd and any .ct translation, catching the class of bug a translator can introduce without ever reading src/cli/main.c or src/gui/main.c: a dropped/reordered printf -placeholder, a missing button mnemonic, or two shortcuts sharing one letter. +placeholder, a missing/misplaced button mnemonic, two shortcuts sharing one +letter, or a file accidentally saved as UTF-8 instead of the Latin-1 FlexCat +and AmigaOS expect. This is NOT a translation-quality check - wording/tone/register still needs a human fluent in the language. It only checks structure: the same mechanical @@ -12,12 +14,18 @@ Usage: tools/check_catalog.py locale/AmiAuth.cd [translation.ct ...] + tools/check_catalog.py --fix-encoding FILE [FILE ...] -The first argument is always the .cd (the English source of truth, and the -baseline every .ct is checked against); every following argument is a .ct -translation. Exits 1 if any FAIL-level issue is found in any file; WARN-level -issues (see MSG_GUI_BTN_*/MSG_CLI_REKEY_STRENGTHEN_PROMPT below) are printed -but don't fail the build - the code has a documented runtime fallback for them. +The first form's first argument is always the .cd (the English source of +truth, and the baseline every .ct is checked against); every following +argument is a .ct translation. Exits 1 if any FAIL-level issue is found in +any file; WARN-level issues (see MSG_GUI_BTN_*/MSG_CLI_REKEY_STRENGTHEN_PROMPT +below) are printed but don't fail the build - the code has a documented +runtime fallback for them. + +The second form re-saves the given file(s) as Latin-1 if check_encoding() +would flag them - use this instead of a hand-rolled one-liner (see +fix_encoding()'s docstring for why a naive one is dangerous). Parsing note: this assumes THIS repo's own .cd/.ct convention - one line of body text per entry, terminated by a lone ';' line (see locale/AmiAuth.cd) - @@ -70,6 +78,46 @@ def parse_entries(path): return entries +def check_encoding(label, path, fails): + """FlexCat and AmigaOS expect Latin-1 (ISO-8859-1), not UTF-8 - an editor + that defaults to UTF-8 will happily save e.g. 'ü' as the two bytes 0xC3 + 0xBC instead of the single Latin-1 byte 0xFC, which FlexCat either + rejects outright (for the .cd's built-in text) or silently mis-renders + (for a .ct translation - AmigaOS displays each byte as its own Latin-1 + character, turning one accented letter into two garbled ones). + + Heuristic: real Latin-1 prose with high-bit bytes essentially never also + decodes as valid UTF-8 (an accented letter is usually followed by an + ordinary ASCII letter, which breaks UTF-8's continuation-byte rules) - + so "the raw bytes contain a high bit AND decode cleanly as UTF-8" is a + reliable (not mathematically airtight, but reliable in practice for + natural-language text) signal that the file is accidentally UTF-8.""" + with open(path, 'rb') as f: + raw = f.read() + if not any(b >= 0x80 for b in raw): + return # pure ASCII: no Latin-1-vs-UTF-8 question to ask + try: + raw.decode('utf-8') + except UnicodeDecodeError: + return # doesn't parse as UTF-8 - consistent with real Latin-1 + fails.append(f"{label}: file has non-ASCII bytes that also decode as " + f"valid UTF-8 - it's almost certainly saved as UTF-8, not " + f"the Latin-1/ISO-8859-1 FlexCat and AmigaOS expect. Fix " + f"with: tools/check_catalog.py --fix-encoding {path}") + + +def fix_encoding(path): + """Re-save `path` as Latin-1 if it's currently (accidentally) UTF-8. + Reads the WHOLE file into memory before opening it for writing - opening + the same path in 'wb' mode truncates it immediately, so a naive + read-and-write-in-one-expression one-liner destroys the file before the + read ever happens. Don't inline this as a shell one-liner; call this.""" + with open(path, encoding='utf-8') as f: + text = f.read() + with open(path, 'wb') as f: + f.write(text.encode('latin-1')) + + def placeholders(text): return PLACEHOLDER_RE.findall(text) @@ -99,6 +147,17 @@ def check_mnemonics(label, entries, fails, warns): f"(need exactly 1): {text!r}") continue pos = marks[0] + # src/gui/main.c's WMHI_VANILLAKEY handler reads LBL_ADD[1] etc. - a + # HARDCODED index, not "find the underscore" - so the '_' MUST be + # the very first character (index 0) or the wrong letter (whatever + # happens to sit at index 1) gets matched instead, silently. + if pos != 0: + fails.append(f"{label}: {name}'s '_' is at position {pos}, not the " + f"start of the string - the code always reads index 1 " + f"as the shortcut letter regardless of where '_' " + f"actually is, so this silently marks the wrong " + f"letter (or none): {text!r}") + continue if pos + 1 >= len(text) or not text[pos + 1].isalpha(): fails.append(f"{label}: {name}'s '_' isn't followed by a letter: {text!r}") continue @@ -151,7 +210,16 @@ def check_placeholders(label, source, translation, fails): def main(argv): if len(argv) < 2: - sys.exit(f"usage: {argv[0]} locale/AmiAuth.cd [translation.ct ...]") + sys.exit(f"usage: {argv[0]} locale/AmiAuth.cd [translation.ct ...]\n" + f" {argv[0]} --fix-encoding FILE [FILE ...]") + + if argv[1] == '--fix-encoding': + if len(argv) < 3: + sys.exit(f"usage: {argv[0]} --fix-encoding FILE [FILE ...]") + for path in argv[2:]: + fix_encoding(path) + print(f"re-saved as Latin-1: {path}") + return 0 cd_path, ct_paths = argv[1], argv[2:] source = parse_entries(cd_path) @@ -159,10 +227,12 @@ def main(argv): sys.exit(f"{cd_path}: parsed zero entries - check the file/parser") fails, warns = [], [] + check_encoding(cd_path, cd_path, fails) check_mnemonics(cd_path, source, fails, warns) check_rekey_prompts(cd_path, source, fails, warns) for ct_path in ct_paths: + check_encoding(ct_path, ct_path, fails) entries = parse_entries(ct_path) check_mnemonics(ct_path, entries, fails, warns) check_rekey_prompts(ct_path, entries, fails, warns) diff --git a/userdocs/Localization.md b/userdocs/Localization.md index 2ba2235..1c1f91d 100644 --- a/userdocs/Localization.md +++ b/userdocs/Localization.md @@ -57,10 +57,16 @@ everywhere. **GUI button mnemonics:** the account-list toolbar's button labels (`_Add`, `_Edit`, `_Remove`, `_Copy`, `_D -10s`, `_U +10s`) carry a keyboard shortcut as a leading underscore before one letter — AmiAuth reads that letter back -out of the string at runtime. When translating these, keep exactly one `_` -immediately before an ASCII letter (ideally one that doesn't collide with -another button's shortcut); dropping it, or moving it in front of a digit or -symbol, silently breaks that button's keyboard shortcut. +out of the string at runtime, from a **fixed position**, not by searching +for the underscore. That means the `_` must be the very *first* character +of the string, immediately followed by an ASCII letter — you can pick +whichever translated word you like for the button (the shortcut is always +that word's first letter, not a letter of your choosing further in), but +you can't mark a letter in the middle of a word the way the CLI re-key +prompts below let you. Getting this wrong (underscore missing, doubled, or +not at the very start) silently breaks or mismarks that button's shortcut +rather than erroring — `tools/check_catalog.py` (`make check-catalog`) +checks for it. **CLI re-key prompts:** the three interactive re-key confirmations (offered after unlocking a vault on much faster/slower hardware than it was tuned @@ -78,6 +84,37 @@ do end up the same, AmiAuth degrades safely rather than misreading your answer: the never-ask shortcut (which writes a persisted preference) simply becomes unreachable, it never fires on a "yes" by mistake. +## Draft translations awaiting review + +[`locale/drafts/`](https://github.com/sidick/amiauth/tree/main/locale/drafts) +in the source repository holds **machine-generated first-pass translations** +— a starting point for a fluent speaker to review and correct, not something +to install as-is. They exist so contributing a translation can start from +"fix this" rather than "write 145 strings from a blank file." Currently +drafted: German +([`deutsch.ct`](https://github.com/sidick/amiauth/blob/main/locale/drafts/deutsch.ct)), +French +([`francais.ct`](https://github.com/sidick/amiauth/blob/main/locale/drafts/francais.ct)), +Italian +([`italiano.ct`](https://github.com/sidick/amiauth/blob/main/locale/drafts/italiano.ct)), +and Polish +([`polski.ct`](https://github.com/sidick/amiauth/blob/main/locale/drafts/polski.ct)). + +None of these are wired into any build — they can't end up in a release, or +get picked up by AmigaOS's catalog search, just by existing in the repo. +Every draft has passed `tools/check_catalog.py`'s structural checks +(placeholders, button mnemonics, re-key prompt markers, file encoding — see +above), so what's left to fix is wording, tone, and register, not mechanics. +Polish specifically drops its usual diacritics (`a`/`c`/`e`/`l`/`n`/`o`/`s`/`z` +instead of the accented forms) — not a stylistic choice, but because the +catalog format's `##codeset` field only supports the Latin-1 character set, +which contains just one of the nine Polish diacritics. + +To help: pick a draft, read it against `locale/AmiAuth.cd` (the English +source of truth), fix what needs fixing, build and test it locally (see +[Contributing a translation](#contributing-a-translation) above), then open +a pull request moving it out of `locale/drafts/`. + ## Baseline Locale.library catalogs require AmigaOS 2.1 (V38) — one step above AmiAuth's