From 9ca03493b249e65048ceb9e3ce8c010984409ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Bene=C5=A1?= Date: Sun, 18 Oct 2026 03:38:31 +0200 Subject: [PATCH] The app's own pages in the server's nine languages scripts/gen-i18n.py writes ui/i18n.js from the server's catalogues where the same sentence exists and from its own table otherwise; the pages pick the language from the browser and fall back to English. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 24 ++ scripts/gen-i18n.py | 621 ++++++++++++++++++++++++++++++++++++++++++++ ui/app.js | 30 ++- ui/i18n.js | 463 +++++++++++++++++++++++++++++++++ ui/index.html | 47 ++-- ui/titlebar.html | 16 +- ui/titlebar.js | 32 ++- 7 files changed, 1175 insertions(+), 58 deletions(-) create mode 100755 scripts/gen-i18n.py create mode 100644 ui/i18n.js diff --git a/CHANGELOG.md b/CHANGELOG.md index da2c427..e740a14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,30 @@ All notable changes to LLeMbas Desktop. The format follows ## [Unreleased] +### Changed + +- The app's own pages take their look from the server's `tokens.css`, copied by + `scripts/fetch-tokens.sh` from a server tag and pinned in `tokens.lock.json` (the script with + no argument verifies the copy). The hand copy had drifted: `--danger`, `--control-h` and + `--text-xl` were not the server's. The pages name no colour, size or space of their own, and + no font is bundled: the font rules are left out of the copy, so the title bar and the + first-run page are set in the system's monospace face. +- The window's frame is painted in the theme's border colour read out of `tokens.css` at build + time (`build.rs`), not a value written in Rust, and it changes with the theme. +- The first-run page and the title bar speak the system's language when it is one of the + server's nine (Czech, German, Spanish, French, Hungarian, Italian, Polish, Slovak; English + otherwise). `ui/i18n.js` is generated by `scripts/gen-i18n.py`: the sentences the server also + has are translated as the server translates them, the app's own are in the script. + +### Fixed + +- The title bar followed the desktop's light or dark setting, not the account's theme, so it + could be dark over the parchment (Shire) page. It now follows the server page: a script in the + content view reports the page's theme to the app, which paints the frame, the title bar and + the first-run page in it and remembers it for the next start. The server's pages still get no + IPC; the report is a navigation attempt the app reads and refuses. +- The close button's hover colour was a literal white; it is the theme's inverse ink. + ## [0.4.0] — 2026-10-14 Carries LLeMbas CLI 1.2.0, whose agent stays linked before any folder is allowed and follows diff --git a/scripts/gen-i18n.py b/scripts/gen-i18n.py new file mode 100755 index 0000000..705bb9c --- /dev/null +++ b/scripts/gen-i18n.py @@ -0,0 +1,621 @@ +#!/usr/bin/env python3 +"""Write ui/i18n.js: the app's own pages' words in the server's nine languages. + + scripts/gen-i18n.py [path/to/LLeMbas] (default: ../LLeMbas, or $LEMBAS_SERVER) + +The server's catalogues (src/lembas/web/i18n/.py, keyed by the English sentence) are +the first source: a sentence the server also has is translated as the server translates it, +so the title bar says "Zavrieť" where the server's own Close button does. The sentences only +this app has -- the first-run page, the agent's states -- are in OWN below, and nowhere else. +Keep them few: a new string on the page is a new row here, in all eight languages. + +The output is deterministic (languages and keys in a fixed order), so running this twice +writes the same file. ui/i18n.js is committed; this script runs when a string or a server +catalogue changes, not in CI, which has no server checkout. +""" + +from __future__ import annotations + +import importlib.util +import json +import os +import re +import sys +from pathlib import Path + +# The server checkout is read, not written: no __pycache__ beside its catalogues. +sys.dont_write_bytecode = True + +ROOT = Path(__file__).resolve().parent.parent +OUT = ROOT / "ui" / "i18n.js" +LANGS = ("cs", "de", "es", "fr", "hu", "it", "pl", "sk") # and "en", the keys themselves + +# Every sentence the pages use (ui/index.html, ui/app.js, ui/titlebar.html, ui/titlebar.js), +# as `t("…")` and `data-i18n` name it. `%(name)s` fields are filled in by `t`. +KEYS = ( + # index.html + "Server", + "Agent", + "Project folders", + "Address of your LLeMbas server", + "Connect", + "Sign in there as you would in a browser.", + "The agent lets agent chats on your server work in this computer's projects. It is the " + "LLeMbas CLI, built in, running in the background while the app runs.", + "Link this computer", + "Start the agent", + "Open the server", + "Agent chats from your server can work in these folders and anything inside them — " + "nowhere else on this computer. Start one in the server's Agents tab, on “Local”.", + "Add folder…", + "The most an agent chat may do here", + "Manual — asks first", + "Edit — asks before commands", + "Auto — asks nothing", + "Plan — changes nothing", + "A chat asking for more is lowered to this.", + "Terminal", + "Let the server open a terminal here", + "A shell as you, in one of these folders.", + # app.js + "The agent is off.", + "This computer is not linked to the server yet.", + "The agent is starting…", + "The agent is running: agent chats can use this computer.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.", + "The agent stopped: %(why)s. Trying again shortly.", + "The agent is connected, but may open no folder yet: add one below.", + "Link again", + "Stop the agent", + "No folder yet: the agent can open nothing. Add the folder of a project.", + "Remove", + "Remove %(dir)s", + "Looking for a LLeMbas server there…", + # titlebar.html, titlebar.js + "agent off", + "not linked", + "agent starting…", + "agent running", + "agent running (service)", + "agent stopped", + "agent: no folders", + "Agent stopped: %(why)s", + "update", + "update %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)", + "Menu", + "Minimise", + "Maximise", + "Restore", + "Close", + "Close (LLeMbas stays in the tray)", +) + +# The app's own sentences. The mode names (Manual, Edit, Auto, Plan) stay English, as the +# server's catalogues keep them ("Režim Edit", "Modus Edit"); "Local" is the server's word for +# the device, as the server's catalogue has it. +OWN: dict[str, dict[str, str]] = { + "cs": { + "Server": "Server", + "Project folders": "Složky projektů", + "Address of your LLeMbas server": "Adresa vašeho serveru LLeMbas", + "Connect": "Připojit", + "Sign in there as you would in a browser.": "Přihlaste se tam jako v prohlížeči.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "Agent umožňuje agentním konverzacím na vašem serveru pracovat v projektech na tomto počítači. Je to vestavěné LLeMbas CLI, které běží na pozadí, dokud běží aplikace.", + "Link this computer": "Propojit tento počítač", + "Start the agent": "Spustit agenta", + "Open the server": "Otevřít server", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Agentní konverzace z vašeho serveru mohou pracovat v těchto složkách a ve všem uvnitř nich — nikde jinde na tomto počítači. Spusťte ji na serveru na kartě Agenti, na zařízení „Místní“.", + "Add folder…": "Přidat složku…", + "The most an agent chat may do here": "Nejvíc, co tu agentní konverzace smí", + "Manual — asks first": "Manual — nejdřív se zeptá", + "Edit — asks before commands": "Edit — ptá se před příkazy", + "Auto — asks nothing": "Auto — na nic se neptá", + "Plan — changes nothing": "Plan — nic nemění", + "A chat asking for more is lowered to this.": "Konverzace, která chce víc, je snížena na tohle.", + "Let the server open a terminal here": "Dovolit serveru otevřít zde terminál", + "A shell as you, in one of these folders.": "Shell pod vaším účtem, v jedné z těchto složek.", + "The agent is off.": "Agent je vypnutý.", + "This computer is not linked to the server yet.": "Tento počítač ještě není propojený se serverem.", + "The agent is starting…": "Agent se spouští…", + "The agent is running: agent chats can use this computer.": "Agent běží: agentní konverzace mohou používat tento počítač.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Služba LLeMbas CLI už tu běží (pid %(pid)s); je agentem tohoto počítače.", + "The agent stopped: %(why)s. Trying again shortly.": "Agent se zastavil: %(why)s. Za chvíli to zkusí znovu.", + "The agent is connected, but may open no folder yet: add one below.": + "Agent je připojený, ale zatím nesmí otevřít žádnou složku: přidejte ji níže.", + "Link again": "Propojit znovu", + "Stop the agent": "Zastavit agenta", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Zatím žádná složka: agent nemůže nic otevřít. Přidejte složku projektu.", + "Remove %(dir)s": "Odebrat %(dir)s", + "Looking for a LLeMbas server there…": "Hledám tam server LLeMbas…", + "agent off": "agent vypnutý", + "not linked": "nepropojeno", + "agent starting…": "agent se spouští…", + "agent running": "agent běží", + "agent running (service)": "agent běží (služba)", + "agent stopped": "agent zastaven", + "agent: no folders": "agent: žádné složky", + "Agent stopped: %(why)s": "Agent se zastavil: %(why)s", + "update": "aktualizace", + "update %(version)s": "aktualizace %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "Je k dispozici LLeMbas %(version)s (tohle je %(current)s)", + "Menu": "Nabídka", + "Minimise": "Minimalizovat", + "Maximise": "Maximalizovat", + "Close (LLeMbas stays in the tray)": "Zavřít (LLeMbas zůstane v oznamovací oblasti)", + }, + "de": { + "Server": "Server", + "Project folders": "Projektordner", + "Address of your LLeMbas server": "Adresse Ihres LLeMbas-Servers", + "Connect": "Verbinden", + "Sign in there as you would in a browser.": "Melden Sie sich dort wie im Browser an.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "Der Agent lässt Agent-Chats auf Ihrem Server in den Projekten dieses Computers arbeiten. Er ist das eingebaute LLeMbas CLI und läuft im Hintergrund, solange die App läuft.", + "Link this computer": "Diesen Computer verbinden", + "Start the agent": "Agent starten", + "Open the server": "Server öffnen", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Agent-Chats von Ihrem Server dürfen in diesen Ordnern und allem darin arbeiten — nirgends sonst auf diesem Computer. Starten Sie einen im Reiter Agenten des Servers, auf „Lokal“.", + "Add folder…": "Ordner hinzufügen…", + "The most an agent chat may do here": "Das Meiste, was ein Agent-Chat hier darf", + "Manual — asks first": "Manual — fragt zuerst", + "Edit — asks before commands": "Edit — fragt vor Befehlen", + "Auto — asks nothing": "Auto — fragt nichts", + "Plan — changes nothing": "Plan — ändert nichts", + "A chat asking for more is lowered to this.": "Ein Chat, der mehr verlangt, wird hierauf begrenzt.", + "Let the server open a terminal here": "Dem Server erlauben, hier ein Terminal zu öffnen", + "A shell as you, in one of these folders.": "Eine Shell als Sie, in einem dieser Ordner.", + "The agent is off.": "Der Agent ist aus.", + "This computer is not linked to the server yet.": "Dieser Computer ist noch nicht mit dem Server verbunden.", + "The agent is starting…": "Der Agent startet…", + "The agent is running: agent chats can use this computer.": "Der Agent läuft: Agent-Chats können diesen Computer nutzen.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Ein LLeMbas-CLI-Dienst läuft hier bereits (PID %(pid)s); er ist der Agent dieses Computers.", + "The agent stopped: %(why)s. Trying again shortly.": "Der Agent hat angehalten: %(why)s. Gleich wird es erneut versucht.", + "The agent is connected, but may open no folder yet: add one below.": + "Der Agent ist verbunden, darf aber noch keinen Ordner öffnen: Fügen Sie unten einen hinzu.", + "Link again": "Erneut verbinden", + "Stop the agent": "Agent anhalten", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Noch kein Ordner: Der Agent kann nichts öffnen. Fügen Sie den Ordner eines Projekts hinzu.", + "Remove %(dir)s": "%(dir)s entfernen", + "Looking for a LLeMbas server there…": "Suche dort nach einem LLeMbas-Server…", + "agent off": "Agent aus", + "not linked": "nicht verbunden", + "agent starting…": "Agent startet…", + "agent running": "Agent läuft", + "agent running (service)": "Agent läuft (Dienst)", + "agent stopped": "Agent angehalten", + "agent: no folders": "Agent: keine Ordner", + "Agent stopped: %(why)s": "Agent angehalten: %(why)s", + "update": "Update", + "update %(version)s": "Update %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s ist verfügbar (dies ist %(current)s)", + "Menu": "Menü", + "Minimise": "Minimieren", + "Maximise": "Maximieren", + "Close (LLeMbas stays in the tray)": "Schließen (LLeMbas bleibt im Benachrichtigungsbereich)", + }, + "es": { + "Server": "Servidor", + "Project folders": "Carpetas de proyectos", + "Address of your LLeMbas server": "Dirección de su servidor LLeMbas", + "Connect": "Conectar", + "Sign in there as you would in a browser.": "Inicie sesión allí como lo haría en un navegador.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "El agente permite que los chats de agente de su servidor trabajen en los proyectos de este ordenador. Es el LLeMbas CLI, integrado, que se ejecuta en segundo plano mientras la aplicación está abierta.", + "Link this computer": "Vincular este ordenador", + "Start the agent": "Iniciar el agente", + "Open the server": "Abrir el servidor", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Los chats de agente de su servidor pueden trabajar en estas carpetas y en todo lo que contienen — en ningún otro lugar de este ordenador. Inicie uno en la pestaña Agentes del servidor, en «Local».", + "Add folder…": "Añadir carpeta…", + "The most an agent chat may do here": "Lo máximo que un chat de agente puede hacer aquí", + "Manual — asks first": "Manual — pregunta primero", + "Edit — asks before commands": "Edit — pregunta antes de los comandos", + "Auto — asks nothing": "Auto — no pregunta nada", + "Plan — changes nothing": "Plan — no cambia nada", + "A chat asking for more is lowered to this.": "Un chat que pida más se limita a esto.", + "Let the server open a terminal here": "Permitir que el servidor abra un terminal aquí", + "A shell as you, in one of these folders.": "Un shell con su usuario, en una de estas carpetas.", + "The agent is off.": "El agente está apagado.", + "This computer is not linked to the server yet.": "Este ordenador aún no está vinculado al servidor.", + "The agent is starting…": "El agente se está iniciando…", + "The agent is running: agent chats can use this computer.": "El agente está en marcha: los chats de agente pueden usar este ordenador.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Ya se está ejecutando aquí un servicio de LLeMbas CLI (pid %(pid)s); es el agente de este ordenador.", + "The agent stopped: %(why)s. Trying again shortly.": "El agente se detuvo: %(why)s. Se volverá a intentar en breve.", + "The agent is connected, but may open no folder yet: add one below.": + "El agente está conectado, pero aún no puede abrir ninguna carpeta: añada una abajo.", + "Link again": "Vincular de nuevo", + "Stop the agent": "Detener el agente", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Aún no hay ninguna carpeta: el agente no puede abrir nada. Añada la carpeta de un proyecto.", + "Remove %(dir)s": "Quitar %(dir)s", + "Looking for a LLeMbas server there…": "Buscando un servidor LLeMbas allí…", + "agent off": "agente apagado", + "not linked": "no vinculado", + "agent starting…": "agente iniciando…", + "agent running": "agente en marcha", + "agent running (service)": "agente en marcha (servicio)", + "agent stopped": "agente detenido", + "agent: no folders": "agente: sin carpetas", + "Agent stopped: %(why)s": "El agente se detuvo: %(why)s", + "update": "actualizar", + "update %(version)s": "actualizar a %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s está disponible (esta es la %(current)s)", + "Menu": "Menú", + "Minimise": "Minimizar", + "Maximise": "Maximizar", + "Close (LLeMbas stays in the tray)": "Cerrar (LLeMbas sigue en la bandeja del sistema)", + }, + "fr": { + "Server": "Serveur", + "Project folders": "Dossiers de projets", + "Address of your LLeMbas server": "Adresse de votre serveur LLeMbas", + "Connect": "Se connecter", + "Sign in there as you would in a browser.": "Connectez-vous là-bas comme dans un navigateur.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "L'agent permet aux conversations d'agent de votre serveur de travailler dans les projets de cet ordinateur. C'est le LLeMbas CLI, intégré, qui tourne en arrière-plan tant que l'application est ouverte.", + "Link this computer": "Relier cet ordinateur", + "Start the agent": "Démarrer l'agent", + "Open the server": "Ouvrir le serveur", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Les conversations d'agent de votre serveur peuvent travailler dans ces dossiers et tout ce qu'ils contiennent — nulle part ailleurs sur cet ordinateur. Démarrez-en une dans l'onglet Agents du serveur, sur « Local ».", + "Add folder…": "Ajouter un dossier…", + "The most an agent chat may do here": "Le maximum qu'une conversation d'agent peut faire ici", + "Manual — asks first": "Manual — demande d'abord", + "Edit — asks before commands": "Edit — demande avant les commandes", + "Auto — asks nothing": "Auto — ne demande rien", + "Plan — changes nothing": "Plan — ne change rien", + "A chat asking for more is lowered to this.": "Une conversation qui en demande plus est ramenée à ceci.", + "Let the server open a terminal here": "Laisser le serveur ouvrir un terminal ici", + "A shell as you, in one of these folders.": "Un shell en votre nom, dans l'un de ces dossiers.", + "The agent is off.": "L'agent est arrêté.", + "This computer is not linked to the server yet.": "Cet ordinateur n'est pas encore relié au serveur.", + "The agent is starting…": "L'agent démarre…", + "The agent is running: agent chats can use this computer.": "L'agent tourne : les conversations d'agent peuvent utiliser cet ordinateur.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Un service LLeMbas CLI tourne déjà ici (pid %(pid)s) ; c'est l'agent de cet ordinateur.", + "The agent stopped: %(why)s. Trying again shortly.": "L'agent s'est arrêté : %(why)s. Nouvel essai sous peu.", + "The agent is connected, but may open no folder yet: add one below.": + "L'agent est connecté, mais ne peut encore ouvrir aucun dossier : ajoutez-en un ci-dessous.", + "Link again": "Relier à nouveau", + "Stop the agent": "Arrêter l'agent", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Aucun dossier pour l'instant : l'agent ne peut rien ouvrir. Ajoutez le dossier d'un projet.", + "Remove %(dir)s": "Retirer %(dir)s", + "Looking for a LLeMbas server there…": "Recherche d'un serveur LLeMbas à cette adresse…", + "agent off": "agent arrêté", + "not linked": "non relié", + "agent starting…": "agent en démarrage…", + "agent running": "agent actif", + "agent running (service)": "agent actif (service)", + "agent stopped": "agent interrompu", + "agent: no folders": "agent : aucun dossier", + "Agent stopped: %(why)s": "L'agent s'est arrêté : %(why)s", + "update": "mise à jour", + "update %(version)s": "mise à jour %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s est disponible (ceci est la %(current)s)", + "Menu": "Menu", + "Minimise": "Réduire", + "Maximise": "Agrandir", + "Close (LLeMbas stays in the tray)": "Fermer (LLeMbas reste dans la zone de notification)", + }, + "hu": { + "Server": "Kiszolgáló", + "Project folders": "Projektmappák", + "Address of your LLeMbas server": "A LLeMbas-kiszolgáló címe", + "Connect": "Csatlakozás", + "Sign in there as you would in a browser.": "Jelentkezzen be ott úgy, mint egy böngészőben.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "Az ügynök lehetővé teszi, hogy a kiszolgálón futó ügynök-csevegések ennek a számítógépnek a projektjeiben dolgozzanak. Ez a beépített LLeMbas CLI, amely a háttérben fut, amíg az alkalmazás fut.", + "Link this computer": "A számítógép összekapcsolása", + "Start the agent": "Ügynök indítása", + "Open the server": "Kiszolgáló megnyitása", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "A kiszolgáló ügynök-csevegései ezekben a mappákban és a bennük lévőkben dolgozhatnak — a számítógépen máshol nem. Indítson egyet a kiszolgáló Ügynökök lapján, a „Helyi” eszközön.", + "Add folder…": "Mappa hozzáadása…", + "The most an agent chat may do here": "A legtöbb, amit egy ügynök-csevegés itt tehet", + "Manual — asks first": "Manual — előbb kérdez", + "Edit — asks before commands": "Edit — parancsok előtt kérdez", + "Auto — asks nothing": "Auto — semmit nem kérdez", + "Plan — changes nothing": "Plan — semmit nem változtat", + "A chat asking for more is lowered to this.": "Az ennél többet kérő csevegés erre korlátozódik.", + "Let the server open a terminal here": "A kiszolgáló nyithasson itt terminált", + "A shell as you, in one of these folders.": "Egy shell az Ön nevében, ezek egyik mappájában.", + "The agent is off.": "Az ügynök ki van kapcsolva.", + "This computer is not linked to the server yet.": "Ez a számítógép még nincs összekapcsolva a kiszolgálóval.", + "The agent is starting…": "Az ügynök indul…", + "The agent is running: agent chats can use this computer.": "Az ügynök fut: az ügynök-csevegések használhatják ezt a számítógépet.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Itt már fut egy LLeMbas CLI szolgáltatás (pid %(pid)s); ez a számítógép ügynöke.", + "The agent stopped: %(why)s. Trying again shortly.": "Az ügynök leállt: %(why)s. Hamarosan újra próbálja.", + "The agent is connected, but may open no folder yet: add one below.": + "Az ügynök csatlakozott, de még egy mappát sem nyithat meg: adjon hozzá egyet lent.", + "Link again": "Újra összekapcsolás", + "Stop the agent": "Ügynök leállítása", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Még nincs mappa: az ügynök semmit nem nyithat meg. Adja hozzá egy projekt mappáját.", + "Remove %(dir)s": "%(dir)s eltávolítása", + "Looking for a LLeMbas server there…": "LLeMbas-kiszolgáló keresése ott…", + "agent off": "ügynök kikapcsolva", + "not linked": "nincs összekapcsolva", + "agent starting…": "ügynök indul…", + "agent running": "ügynök fut", + "agent running (service)": "ügynök fut (szolgáltatás)", + "agent stopped": "ügynök leállt", + "agent: no folders": "ügynök: nincs mappa", + "Agent stopped: %(why)s": "Az ügynök leállt: %(why)s", + "update": "frissítés", + "update %(version)s": "frissítés: %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "Elérhető a LLeMbas %(version)s (ez a %(current)s)", + "Menu": "Menü", + "Minimise": "Kis méret", + "Maximise": "Teljes méret", + "Close (LLeMbas stays in the tray)": "Bezárás (a LLeMbas a tálcán marad)", + }, + "it": { + "Server": "Server", + "Project folders": "Cartelle dei progetti", + "Address of your LLeMbas server": "Indirizzo del tuo server LLeMbas", + "Connect": "Connetti", + "Sign in there as you would in a browser.": "Accedi lì come faresti in un browser.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "L'agente permette alle chat agente del tuo server di lavorare nei progetti di questo computer. È il LLeMbas CLI, integrato, in esecuzione in background finché l'app è aperta.", + "Link this computer": "Collega questo computer", + "Start the agent": "Avvia l'agente", + "Open the server": "Apri il server", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Le chat agente del tuo server possono lavorare in queste cartelle e in tutto ciò che contengono — in nessun altro posto di questo computer. Avviane una nella scheda Agenti del server, su “Locale”.", + "Add folder…": "Aggiungi cartella…", + "The most an agent chat may do here": "Il massimo che una chat agente può fare qui", + "Manual — asks first": "Manual — chiede prima", + "Edit — asks before commands": "Edit — chiede prima dei comandi", + "Auto — asks nothing": "Auto — non chiede nulla", + "Plan — changes nothing": "Plan — non cambia nulla", + "A chat asking for more is lowered to this.": "Una chat che chiede di più viene limitata a questo.", + "Let the server open a terminal here": "Lascia che il server apra un terminale qui", + "A shell as you, in one of these folders.": "Una shell con il tuo utente, in una di queste cartelle.", + "The agent is off.": "L'agente è spento.", + "This computer is not linked to the server yet.": "Questo computer non è ancora collegato al server.", + "The agent is starting…": "L'agente si sta avviando…", + "The agent is running: agent chats can use this computer.": "L'agente è in esecuzione: le chat agente possono usare questo computer.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Un servizio LLeMbas CLI è già in esecuzione qui (pid %(pid)s); è l'agente di questo computer.", + "The agent stopped: %(why)s. Trying again shortly.": "L'agente si è fermato: %(why)s. Tra poco riprova.", + "The agent is connected, but may open no folder yet: add one below.": + "L'agente è connesso, ma non può ancora aprire nessuna cartella: aggiungine una qui sotto.", + "Link again": "Collega di nuovo", + "Stop the agent": "Ferma l'agente", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Ancora nessuna cartella: l'agente non può aprire nulla. Aggiungi la cartella di un progetto.", + "Remove %(dir)s": "Rimuovi %(dir)s", + "Looking for a LLeMbas server there…": "Cerco un server LLeMbas lì…", + "agent off": "agente spento", + "not linked": "non collegato", + "agent starting…": "agente in avvio…", + "agent running": "agente attivo", + "agent running (service)": "agente attivo (servizio)", + "agent stopped": "agente fermato", + "agent: no folders": "agente: nessuna cartella", + "Agent stopped: %(why)s": "L'agente si è fermato: %(why)s", + "update": "aggiorna", + "update %(version)s": "aggiorna a %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "È disponibile LLeMbas %(version)s (questa è la %(current)s)", + "Menu": "Menu", + "Minimise": "Riduci a icona", + "Maximise": "Ingrandisci", + "Close (LLeMbas stays in the tray)": "Chiudi (LLeMbas resta nell'area di notifica)", + }, + "pl": { + "Server": "Serwer", + "Project folders": "Foldery projektów", + "Address of your LLeMbas server": "Adres Twojego serwera LLeMbas", + "Connect": "Połącz", + "Sign in there as you would in a browser.": "Zaloguj się tam tak jak w przeglądarce.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "Agent pozwala czatom agenta z Twojego serwera pracować w projektach na tym komputerze. To wbudowane LLeMbas CLI, działające w tle, dopóki działa aplikacja.", + "Link this computer": "Połącz ten komputer", + "Start the agent": "Uruchom agenta", + "Open the server": "Otwórz serwer", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Czaty agenta z Twojego serwera mogą pracować w tych folderach i we wszystkim, co zawierają — nigdzie indziej na tym komputerze. Uruchom czat na karcie Agenci na serwerze, na urządzeniu „Lokalne”.", + "Add folder…": "Dodaj folder…", + "The most an agent chat may do here": "Najwięcej, co czat agenta może tu zrobić", + "Manual — asks first": "Manual — najpierw pyta", + "Edit — asks before commands": "Edit — pyta przed poleceniami", + "Auto — asks nothing": "Auto — o nic nie pyta", + "Plan — changes nothing": "Plan — niczego nie zmienia", + "A chat asking for more is lowered to this.": "Czat, który prosi o więcej, zostaje ograniczony do tego.", + "Let the server open a terminal here": "Pozwól serwerowi otworzyć tu terminal", + "A shell as you, in one of these folders.": "Powłoka na Twoim koncie, w jednym z tych folderów.", + "The agent is off.": "Agent jest wyłączony.", + "This computer is not linked to the server yet.": "Ten komputer nie jest jeszcze połączony z serwerem.", + "The agent is starting…": "Agent się uruchamia…", + "The agent is running: agent chats can use this computer.": "Agent działa: czaty agenta mogą używać tego komputera.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Usługa LLeMbas CLI już tu działa (pid %(pid)s); jest agentem tego komputera.", + "The agent stopped: %(why)s. Trying again shortly.": "Agent się zatrzymał: %(why)s. Za chwilę spróbuje ponownie.", + "The agent is connected, but may open no folder yet: add one below.": + "Agent jest połączony, ale nie może jeszcze otworzyć żadnego folderu: dodaj go poniżej.", + "Link again": "Połącz ponownie", + "Stop the agent": "Zatrzymaj agenta", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Nie ma jeszcze folderu: agent nie może nic otworzyć. Dodaj folder projektu.", + "Remove %(dir)s": "Usuń %(dir)s", + "Looking for a LLeMbas server there…": "Szukam tam serwera LLeMbas…", + "agent off": "agent wyłączony", + "not linked": "niepołączony", + "agent starting…": "agent się uruchamia…", + "agent running": "agent działa", + "agent running (service)": "agent działa (usługa)", + "agent stopped": "agent zatrzymany", + "agent: no folders": "agent: brak folderów", + "Agent stopped: %(why)s": "Agent się zatrzymał: %(why)s", + "update": "aktualizacja", + "update %(version)s": "aktualizacja %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "Dostępny jest LLeMbas %(version)s (to jest %(current)s)", + "Menu": "Menu", + "Minimise": "Minimalizuj", + "Maximise": "Maksymalizuj", + "Close (LLeMbas stays in the tray)": "Zamknij (LLeMbas zostaje w zasobniku systemowym)", + }, + "sk": { + "Server": "Server", + "Project folders": "Priečinky projektov", + "Address of your LLeMbas server": "Adresa vášho servera LLeMbas", + "Connect": "Pripojiť", + "Sign in there as you would in a browser.": "Prihláste sa tam ako v prehliadači.", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": + "Agent umožňuje agentským konverzáciám na vašom serveri pracovať v projektoch na tomto počítači. Je to vstavané LLeMbas CLI, ktoré beží na pozadí, kým beží aplikácia.", + "Link this computer": "Prepojiť tento počítač", + "Start the agent": "Spustiť agenta", + "Open the server": "Otvoriť server", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": + "Agentské konverzácie z vášho servera môžu pracovať v týchto priečinkoch a vo všetkom, čo obsahujú — nikde inde na tomto počítači. Spustite ju na serveri na karte Agenti, na zariadení „Lokálny“.", + "Add folder…": "Pridať priečinok…", + "The most an agent chat may do here": "Najviac, čo tu agentská konverzácia smie", + "Manual — asks first": "Manual — najprv sa opýta", + "Edit — asks before commands": "Edit — pýta sa pred príkazmi", + "Auto — asks nothing": "Auto — na nič sa nepýta", + "Plan — changes nothing": "Plan — nič nemení", + "A chat asking for more is lowered to this.": "Konverzácia, ktorá chce viac, je znížená na toto.", + "Let the server open a terminal here": "Dovoliť serveru otvoriť tu terminál", + "A shell as you, in one of these folders.": "Shell pod vaším účtom, v jednom z týchto priečinkov.", + "The agent is off.": "Agent je vypnutý.", + "This computer is not linked to the server yet.": "Tento počítač ešte nie je prepojený so serverom.", + "The agent is starting…": "Agent sa spúšťa…", + "The agent is running: agent chats can use this computer.": "Agent beží: agentské konverzácie môžu používať tento počítač.", + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": + "Služba LLeMbas CLI tu už beží (pid %(pid)s); je agentom tohto počítača.", + "The agent stopped: %(why)s. Trying again shortly.": "Agent sa zastavil: %(why)s. O chvíľu to skúsi znova.", + "The agent is connected, but may open no folder yet: add one below.": + "Agent je pripojený, ale zatiaľ nesmie otvoriť žiadny priečinok: pridajte ho nižšie.", + "Link again": "Prepojiť znova", + "Stop the agent": "Zastaviť agenta", + "No folder yet: the agent can open nothing. Add the folder of a project.": + "Zatiaľ žiadny priečinok: agent nemôže nič otvoriť. Pridajte priečinok projektu.", + "Remove %(dir)s": "Odstrániť %(dir)s", + "Looking for a LLeMbas server there…": "Hľadám tam server LLeMbas…", + "agent off": "agent vypnutý", + "not linked": "neprepojené", + "agent starting…": "agent sa spúšťa…", + "agent running": "agent beží", + "agent running (service)": "agent beží (služba)", + "agent stopped": "agent zastavený", + "agent: no folders": "agent: žiadne priečinky", + "Agent stopped: %(why)s": "Agent sa zastavil: %(why)s", + "update": "aktualizácia", + "update %(version)s": "aktualizácia %(version)s", + "LLeMbas %(version)s is available (this is %(current)s)": "Je k dispozícii LLeMbas %(version)s (toto je %(current)s)", + "Menu": "Ponuka", + "Minimise": "Minimalizovať", + "Maximise": "Maximalizovať", + "Close (LLeMbas stays in the tray)": "Zavrieť (LLeMbas zostane v oznamovacej oblasti)", + }, +} + +FIELD = re.compile(r"%\((\w+)\)s") + + +def server_catalogue(server: Path, lang: str) -> dict[str, str]: + """The server's MESSAGES for `lang`: the module loaded from its file, nothing imported.""" + path = server / "src" / "lembas" / "web" / "i18n" / f"{lang}.py" + if not path.is_file(): + sys.exit(f"gen-i18n: {path} is missing: is {server} the LLeMbas server checkout?") + spec = importlib.util.spec_from_file_location(f"lembas_i18n_{lang}", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + messages = getattr(module, "MESSAGES", None) + if not isinstance(messages, dict): + sys.exit(f"gen-i18n: {path} has no MESSAGES") + return messages + + +def main() -> None: + server = Path(sys.argv[1] if len(sys.argv) > 1 else os.environ.get("LEMBAS_SERVER", ROOT.parent / "LLeMbas")) + catalogue: dict[str, dict[str, str]] = {} + problems = 0 + for lang in LANGS: + theirs = server_catalogue(server, lang) + ours = OWN.get(lang, {}) + table: dict[str, str] = {} + from_server = 0 + for key in KEYS: + text = theirs.get(key) + if text: + from_server += 1 + else: + text = ours.get(key) + if not text: + print(f"gen-i18n: {lang}: no translation of {key!r} (English will show)", file=sys.stderr) + problems += 1 + continue + if set(FIELD.findall(text)) != set(FIELD.findall(key)): + print(f"gen-i18n: {lang}: the fields of {text!r} do not match {key!r}", file=sys.stderr) + problems += 1 + continue + table[key] = text + for key in ours: + if key not in KEYS: + print(f"gen-i18n: {lang}: OWN has {key!r}, which no page uses", file=sys.stderr) + problems += 1 + catalogue[lang] = table + print(f"gen-i18n: {lang}: {len(table)} of {len(KEYS)} ({from_server} from the server)") + + body = json.dumps(catalogue, ensure_ascii=False, indent=2, sort_keys=True) + out = ( + "// The app's own pages' words in the server's languages. GENERATED by scripts/gen-i18n.py:\n" + "// the sentences the server also has come from its web/i18n catalogues, the rest from the\n" + "// script's own table. Do not edit; run the script.\n" + "//\n" + "// `t(text, fields)` is the page's English with `%(name)s` fields, as on the server; a\n" + "// missing entry is the English. The language is the system's (navigator.language),\n" + "// English unless it is one of these. Elements marked `data-i18n` (their text) and\n" + "// `data-i18n-title`, `data-i18n-aria-label`, `data-i18n-placeholder` (that attribute)\n" + "// are translated once the page is parsed; this script is deferred, before the page's.\n" + '"use strict"\n' + "\n" + f"const LEMBAS_I18N = {body}\n" + "\n" + "const LEMBAS_LANG = (() => {\n" + ' const code = String(navigator.language || "").slice(0, 2).toLowerCase()\n' + ' return Object.prototype.hasOwnProperty.call(LEMBAS_I18N, code) ? code : "en"\n' + "})()\n" + "document.documentElement.lang = LEMBAS_LANG\n" + "\n" + "function t(text, fields) {\n" + ' const key = String(text).replace(/\\s+/g, " ").trim()\n' + " const table = LEMBAS_I18N[LEMBAS_LANG] || {}\n" + " const out = Object.prototype.hasOwnProperty.call(table, key) ? table[key] : key\n" + " if (!fields) return out\n" + " return out.replace(/%\\((\\w+)\\)s/g, (all, name) =>\n" + " Object.prototype.hasOwnProperty.call(fields, name) ? String(fields[name]) : all)\n" + "}\n" + "\n" + "function translatePage() {\n" + ' for (const el of document.querySelectorAll("[data-i18n]")) el.textContent = t(el.textContent)\n' + ' for (const attr of ["title", "aria-label", "placeholder"]) {\n' + " for (const el of document.querySelectorAll(`[data-i18n-${attr}]`)) {\n" + " el.setAttribute(attr, t(el.getAttribute(attr)))\n" + " }\n" + " }\n" + "}\n" + 'if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", translatePage)\n' + "else translatePage()\n" + ) + OUT.write_text(out, encoding="utf-8") + print(f"gen-i18n: wrote {OUT.relative_to(ROOT)} ({len(LANGS)} languages, {len(KEYS)} sentences)") + if problems: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/ui/app.js b/ui/app.js index d3cc397..1f849b5 100644 --- a/ui/app.js +++ b/ui/app.js @@ -1,5 +1,6 @@ -// The first-run page. It talks to the app over Tauri's IPC — four commands, see src-tauri/src/lib.rs. +// The first-run page. It talks to the app over Tauri's IPC — the commands in src-tauri/src/lib.rs. // The server's own pages never get this: they are remote content with no capability. +// Every word shown goes through `t` (i18n.js, loaded before this), in the system's language. "use strict" const { invoke } = window.__TAURI__.core @@ -8,28 +9,29 @@ const { listen } = window.__TAURI__.event const $ = (id) => document.getElementById(id) const AGENT = { - off: () => ["The agent is off.", ""], - not_linked: () => ["This computer is not linked to the server yet.", ""], - starting: () => ["The agent is starting…", ""], - running: () => ["The agent is running: agent chats can use this computer.", "is-ok"], - elsewhere: (pid) => [`A LLeMbas CLI service already runs here (pid ${pid}); it is this computer's agent.`, "is-ok"], - failed: (why) => [`The agent stopped: ${why}. Trying again shortly.`, "is-error"], + off: () => [t("The agent is off."), ""], + not_linked: () => [t("This computer is not linked to the server yet."), ""], + starting: () => [t("The agent is starting…"), ""], + running: () => [t("The agent is running: agent chats can use this computer."), "is-ok"], + elsewhere: (pid) => [t("A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.", { pid }), "is-ok"], + failed: (why) => [t("The agent stopped: %(why)s. Trying again shortly.", { why }), "is-error"], } function render(s) { + window.lembasTheme.apply(s.theme) $("version").textContent = s.version if (s.server && !$("address").value) $("address").value = s.server $("agent-section").hidden = !s.server const noFolders = s.remote && !(s.remote.enabled && s.remote.roots.length) const [text, cls] = s.agent.state === "running" && noFolders - ? ["The agent is connected, but may open no folder yet: add one below.", "is-error"] + ? [t("The agent is connected, but may open no folder yet: add one below."), "is-error"] : (AGENT[s.agent.state] ?? AGENT.off)(s.agent.detail) const status = $("agent-status") status.textContent = text status.className = `status ${cls}` - $("link").textContent = s.linked ? "Link again" : "Link this computer" - $("toggle").textContent = s.agent_wanted ? "Stop the agent" : "Start the agent" + $("link").textContent = s.linked ? t("Link again") : t("Link this computer") + $("toggle").textContent = s.agent_wanted ? t("Stop the agent") : t("Start the agent") $("toggle").disabled = !s.linked && !s.agent_wanted renderFolders(s) } @@ -43,7 +45,7 @@ function renderFolders(s) { if (!r.enabled || !r.roots.length) { const li = document.createElement("li") li.className = "empty" - li.textContent = "No folder yet: the agent can open nothing. Add the folder of a project." + li.textContent = t("No folder yet: the agent can open nothing. Add the folder of a project.") list.append(li) } for (const dir of r.enabled ? r.roots : []) { @@ -57,8 +59,8 @@ function renderFolders(s) { const remove = document.createElement("button") remove.type = "button" remove.className = "btn" - remove.textContent = "Remove" - remove.setAttribute("aria-label", `Remove ${dir}`) + remove.textContent = t("Remove") + remove.setAttribute("aria-label", t("Remove %(dir)s", { dir })) remove.addEventListener("click", () => invoke("remote_remove", { dir })) li.append(path, remove) list.append(li) @@ -79,7 +81,7 @@ $("server-form").addEventListener("submit", async (e) => { const button = e.target.querySelector("button") button.disabled = true hint.className = "hint" - hint.textContent = "Looking for a LLeMbas server there…" + hint.textContent = t("Looking for a LLeMbas server there…") try { await invoke("set_server", { address: $("address").value }) // The window now shows the server. diff --git a/ui/i18n.js b/ui/i18n.js new file mode 100644 index 0000000..7ddfcca --- /dev/null +++ b/ui/i18n.js @@ -0,0 +1,463 @@ +// The app's own pages' words in the server's languages. GENERATED by scripts/gen-i18n.py: +// the sentences the server also has come from its web/i18n catalogues, the rest from the +// script's own table. Do not edit; run the script. +// +// `t(text, fields)` is the page's English with `%(name)s` fields, as on the server; a +// missing entry is the English. The language is the system's (navigator.language), +// English unless it is one of these. Elements marked `data-i18n` (their text) and +// `data-i18n-title`, `data-i18n-aria-label`, `data-i18n-placeholder` (that attribute) +// are translated once the page is parsed; this script is deferred, before the page's. +"use strict" + +const LEMBAS_I18N = { + "cs": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Služba LLeMbas CLI už tu běží (pid %(pid)s); je agentem tohoto počítače.", + "A chat asking for more is lowered to this.": "Konverzace, která chce víc, je snížena na tohle.", + "A shell as you, in one of these folders.": "Shell pod vaším účtem, v jedné z těchto složek.", + "Add folder…": "Přidat složku…", + "Address of your LLeMbas server": "Adresa vašeho serveru LLeMbas", + "Agent": "Agent", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Agentní konverzace z vašeho serveru mohou pracovat v těchto složkách a ve všem uvnitř nich — nikde jinde na tomto počítači. Spusťte ji na serveru na kartě Agenti, na zařízení „Místní“.", + "Agent stopped: %(why)s": "Agent se zastavil: %(why)s", + "Auto — asks nothing": "Auto — na nic se neptá", + "Close": "Zavřít", + "Close (LLeMbas stays in the tray)": "Zavřít (LLeMbas zůstane v oznamovací oblasti)", + "Connect": "Připojit", + "Edit — asks before commands": "Edit — ptá se před příkazy", + "LLeMbas %(version)s is available (this is %(current)s)": "Je k dispozici LLeMbas %(version)s (tohle je %(current)s)", + "Let the server open a terminal here": "Dovolit serveru otevřít zde terminál", + "Link again": "Propojit znovu", + "Link this computer": "Propojit tento počítač", + "Looking for a LLeMbas server there…": "Hledám tam server LLeMbas…", + "Manual — asks first": "Manual — nejdřív se zeptá", + "Maximise": "Maximalizovat", + "Menu": "Nabídka", + "Minimise": "Minimalizovat", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Zatím žádná složka: agent nemůže nic otevřít. Přidejte složku projektu.", + "Open the server": "Otevřít server", + "Plan — changes nothing": "Plan — nic nemění", + "Project folders": "Složky projektů", + "Remove": "Odebrat", + "Remove %(dir)s": "Odebrat %(dir)s", + "Restore": "Obnovit", + "Server": "Server", + "Sign in there as you would in a browser.": "Přihlaste se tam jako v prohlížeči.", + "Start the agent": "Spustit agenta", + "Stop the agent": "Zastavit agenta", + "Terminal": "Terminál", + "The agent is connected, but may open no folder yet: add one below.": "Agent je připojený, ale zatím nesmí otevřít žádnou složku: přidejte ji níže.", + "The agent is off.": "Agent je vypnutý.", + "The agent is running: agent chats can use this computer.": "Agent běží: agentní konverzace mohou používat tento počítač.", + "The agent is starting…": "Agent se spouští…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "Agent umožňuje agentním konverzacím na vašem serveru pracovat v projektech na tomto počítači. Je to vestavěné LLeMbas CLI, které běží na pozadí, dokud běží aplikace.", + "The agent stopped: %(why)s. Trying again shortly.": "Agent se zastavil: %(why)s. Za chvíli to zkusí znovu.", + "The most an agent chat may do here": "Nejvíc, co tu agentní konverzace smí", + "This computer is not linked to the server yet.": "Tento počítač ještě není propojený se serverem.", + "agent off": "agent vypnutý", + "agent running": "agent běží", + "agent running (service)": "agent běží (služba)", + "agent starting…": "agent se spouští…", + "agent stopped": "agent zastaven", + "agent: no folders": "agent: žádné složky", + "not linked": "nepropojeno", + "update": "aktualizace", + "update %(version)s": "aktualizace %(version)s" + }, + "de": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Ein LLeMbas-CLI-Dienst läuft hier bereits (PID %(pid)s); er ist der Agent dieses Computers.", + "A chat asking for more is lowered to this.": "Ein Chat, der mehr verlangt, wird hierauf begrenzt.", + "A shell as you, in one of these folders.": "Eine Shell als Sie, in einem dieser Ordner.", + "Add folder…": "Ordner hinzufügen…", + "Address of your LLeMbas server": "Adresse Ihres LLeMbas-Servers", + "Agent": "Agent", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Agent-Chats von Ihrem Server dürfen in diesen Ordnern und allem darin arbeiten — nirgends sonst auf diesem Computer. Starten Sie einen im Reiter Agenten des Servers, auf „Lokal“.", + "Agent stopped: %(why)s": "Agent angehalten: %(why)s", + "Auto — asks nothing": "Auto — fragt nichts", + "Close": "Schließen", + "Close (LLeMbas stays in the tray)": "Schließen (LLeMbas bleibt im Benachrichtigungsbereich)", + "Connect": "Verbinden", + "Edit — asks before commands": "Edit — fragt vor Befehlen", + "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s ist verfügbar (dies ist %(current)s)", + "Let the server open a terminal here": "Dem Server erlauben, hier ein Terminal zu öffnen", + "Link again": "Erneut verbinden", + "Link this computer": "Diesen Computer verbinden", + "Looking for a LLeMbas server there…": "Suche dort nach einem LLeMbas-Server…", + "Manual — asks first": "Manual — fragt zuerst", + "Maximise": "Maximieren", + "Menu": "Menü", + "Minimise": "Minimieren", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Noch kein Ordner: Der Agent kann nichts öffnen. Fügen Sie den Ordner eines Projekts hinzu.", + "Open the server": "Server öffnen", + "Plan — changes nothing": "Plan — ändert nichts", + "Project folders": "Projektordner", + "Remove": "Entfernen", + "Remove %(dir)s": "%(dir)s entfernen", + "Restore": "Wiederherstellen", + "Server": "Server", + "Sign in there as you would in a browser.": "Melden Sie sich dort wie im Browser an.", + "Start the agent": "Agent starten", + "Stop the agent": "Agent anhalten", + "Terminal": "Terminal", + "The agent is connected, but may open no folder yet: add one below.": "Der Agent ist verbunden, darf aber noch keinen Ordner öffnen: Fügen Sie unten einen hinzu.", + "The agent is off.": "Der Agent ist aus.", + "The agent is running: agent chats can use this computer.": "Der Agent läuft: Agent-Chats können diesen Computer nutzen.", + "The agent is starting…": "Der Agent startet…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "Der Agent lässt Agent-Chats auf Ihrem Server in den Projekten dieses Computers arbeiten. Er ist das eingebaute LLeMbas CLI und läuft im Hintergrund, solange die App läuft.", + "The agent stopped: %(why)s. Trying again shortly.": "Der Agent hat angehalten: %(why)s. Gleich wird es erneut versucht.", + "The most an agent chat may do here": "Das Meiste, was ein Agent-Chat hier darf", + "This computer is not linked to the server yet.": "Dieser Computer ist noch nicht mit dem Server verbunden.", + "agent off": "Agent aus", + "agent running": "Agent läuft", + "agent running (service)": "Agent läuft (Dienst)", + "agent starting…": "Agent startet…", + "agent stopped": "Agent angehalten", + "agent: no folders": "Agent: keine Ordner", + "not linked": "nicht verbunden", + "update": "Update", + "update %(version)s": "Update %(version)s" + }, + "es": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Ya se está ejecutando aquí un servicio de LLeMbas CLI (pid %(pid)s); es el agente de este ordenador.", + "A chat asking for more is lowered to this.": "Un chat que pida más se limita a esto.", + "A shell as you, in one of these folders.": "Un shell con su usuario, en una de estas carpetas.", + "Add folder…": "Añadir carpeta…", + "Address of your LLeMbas server": "Dirección de su servidor LLeMbas", + "Agent": "Agente", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Los chats de agente de su servidor pueden trabajar en estas carpetas y en todo lo que contienen — en ningún otro lugar de este ordenador. Inicie uno en la pestaña Agentes del servidor, en «Local».", + "Agent stopped: %(why)s": "El agente se detuvo: %(why)s", + "Auto — asks nothing": "Auto — no pregunta nada", + "Close": "Cerrar", + "Close (LLeMbas stays in the tray)": "Cerrar (LLeMbas sigue en la bandeja del sistema)", + "Connect": "Conectar", + "Edit — asks before commands": "Edit — pregunta antes de los comandos", + "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s está disponible (esta es la %(current)s)", + "Let the server open a terminal here": "Permitir que el servidor abra un terminal aquí", + "Link again": "Vincular de nuevo", + "Link this computer": "Vincular este ordenador", + "Looking for a LLeMbas server there…": "Buscando un servidor LLeMbas allí…", + "Manual — asks first": "Manual — pregunta primero", + "Maximise": "Maximizar", + "Menu": "Menú", + "Minimise": "Minimizar", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Aún no hay ninguna carpeta: el agente no puede abrir nada. Añada la carpeta de un proyecto.", + "Open the server": "Abrir el servidor", + "Plan — changes nothing": "Plan — no cambia nada", + "Project folders": "Carpetas de proyectos", + "Remove": "Quitar", + "Remove %(dir)s": "Quitar %(dir)s", + "Restore": "Restaurar", + "Server": "Servidor", + "Sign in there as you would in a browser.": "Inicie sesión allí como lo haría en un navegador.", + "Start the agent": "Iniciar el agente", + "Stop the agent": "Detener el agente", + "Terminal": "Terminal", + "The agent is connected, but may open no folder yet: add one below.": "El agente está conectado, pero aún no puede abrir ninguna carpeta: añada una abajo.", + "The agent is off.": "El agente está apagado.", + "The agent is running: agent chats can use this computer.": "El agente está en marcha: los chats de agente pueden usar este ordenador.", + "The agent is starting…": "El agente se está iniciando…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "El agente permite que los chats de agente de su servidor trabajen en los proyectos de este ordenador. Es el LLeMbas CLI, integrado, que se ejecuta en segundo plano mientras la aplicación está abierta.", + "The agent stopped: %(why)s. Trying again shortly.": "El agente se detuvo: %(why)s. Se volverá a intentar en breve.", + "The most an agent chat may do here": "Lo máximo que un chat de agente puede hacer aquí", + "This computer is not linked to the server yet.": "Este ordenador aún no está vinculado al servidor.", + "agent off": "agente apagado", + "agent running": "agente en marcha", + "agent running (service)": "agente en marcha (servicio)", + "agent starting…": "agente iniciando…", + "agent stopped": "agente detenido", + "agent: no folders": "agente: sin carpetas", + "not linked": "no vinculado", + "update": "actualizar", + "update %(version)s": "actualizar a %(version)s" + }, + "fr": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Un service LLeMbas CLI tourne déjà ici (pid %(pid)s) ; c'est l'agent de cet ordinateur.", + "A chat asking for more is lowered to this.": "Une conversation qui en demande plus est ramenée à ceci.", + "A shell as you, in one of these folders.": "Un shell en votre nom, dans l'un de ces dossiers.", + "Add folder…": "Ajouter un dossier…", + "Address of your LLeMbas server": "Adresse de votre serveur LLeMbas", + "Agent": "Agent", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Les conversations d'agent de votre serveur peuvent travailler dans ces dossiers et tout ce qu'ils contiennent — nulle part ailleurs sur cet ordinateur. Démarrez-en une dans l'onglet Agents du serveur, sur « Local ».", + "Agent stopped: %(why)s": "L'agent s'est arrêté : %(why)s", + "Auto — asks nothing": "Auto — ne demande rien", + "Close": "Fermer", + "Close (LLeMbas stays in the tray)": "Fermer (LLeMbas reste dans la zone de notification)", + "Connect": "Se connecter", + "Edit — asks before commands": "Edit — demande avant les commandes", + "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s est disponible (ceci est la %(current)s)", + "Let the server open a terminal here": "Laisser le serveur ouvrir un terminal ici", + "Link again": "Relier à nouveau", + "Link this computer": "Relier cet ordinateur", + "Looking for a LLeMbas server there…": "Recherche d'un serveur LLeMbas à cette adresse…", + "Manual — asks first": "Manual — demande d'abord", + "Maximise": "Agrandir", + "Menu": "Menu", + "Minimise": "Réduire", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Aucun dossier pour l'instant : l'agent ne peut rien ouvrir. Ajoutez le dossier d'un projet.", + "Open the server": "Ouvrir le serveur", + "Plan — changes nothing": "Plan — ne change rien", + "Project folders": "Dossiers de projets", + "Remove": "Retirer", + "Remove %(dir)s": "Retirer %(dir)s", + "Restore": "Restaurer", + "Server": "Serveur", + "Sign in there as you would in a browser.": "Connectez-vous là-bas comme dans un navigateur.", + "Start the agent": "Démarrer l'agent", + "Stop the agent": "Arrêter l'agent", + "Terminal": "Terminal", + "The agent is connected, but may open no folder yet: add one below.": "L'agent est connecté, mais ne peut encore ouvrir aucun dossier : ajoutez-en un ci-dessous.", + "The agent is off.": "L'agent est arrêté.", + "The agent is running: agent chats can use this computer.": "L'agent tourne : les conversations d'agent peuvent utiliser cet ordinateur.", + "The agent is starting…": "L'agent démarre…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "L'agent permet aux conversations d'agent de votre serveur de travailler dans les projets de cet ordinateur. C'est le LLeMbas CLI, intégré, qui tourne en arrière-plan tant que l'application est ouverte.", + "The agent stopped: %(why)s. Trying again shortly.": "L'agent s'est arrêté : %(why)s. Nouvel essai sous peu.", + "The most an agent chat may do here": "Le maximum qu'une conversation d'agent peut faire ici", + "This computer is not linked to the server yet.": "Cet ordinateur n'est pas encore relié au serveur.", + "agent off": "agent arrêté", + "agent running": "agent actif", + "agent running (service)": "agent actif (service)", + "agent starting…": "agent en démarrage…", + "agent stopped": "agent interrompu", + "agent: no folders": "agent : aucun dossier", + "not linked": "non relié", + "update": "mise à jour", + "update %(version)s": "mise à jour %(version)s" + }, + "hu": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Itt már fut egy LLeMbas CLI szolgáltatás (pid %(pid)s); ez a számítógép ügynöke.", + "A chat asking for more is lowered to this.": "Az ennél többet kérő csevegés erre korlátozódik.", + "A shell as you, in one of these folders.": "Egy shell az Ön nevében, ezek egyik mappájában.", + "Add folder…": "Mappa hozzáadása…", + "Address of your LLeMbas server": "A LLeMbas-kiszolgáló címe", + "Agent": "Ügynök", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "A kiszolgáló ügynök-csevegései ezekben a mappákban és a bennük lévőkben dolgozhatnak — a számítógépen máshol nem. Indítson egyet a kiszolgáló Ügynökök lapján, a „Helyi” eszközön.", + "Agent stopped: %(why)s": "Az ügynök leállt: %(why)s", + "Auto — asks nothing": "Auto — semmit nem kérdez", + "Close": "Bezárás", + "Close (LLeMbas stays in the tray)": "Bezárás (a LLeMbas a tálcán marad)", + "Connect": "Csatlakozás", + "Edit — asks before commands": "Edit — parancsok előtt kérdez", + "LLeMbas %(version)s is available (this is %(current)s)": "Elérhető a LLeMbas %(version)s (ez a %(current)s)", + "Let the server open a terminal here": "A kiszolgáló nyithasson itt terminált", + "Link again": "Újra összekapcsolás", + "Link this computer": "A számítógép összekapcsolása", + "Looking for a LLeMbas server there…": "LLeMbas-kiszolgáló keresése ott…", + "Manual — asks first": "Manual — előbb kérdez", + "Maximise": "Teljes méret", + "Menu": "Menü", + "Minimise": "Kis méret", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Még nincs mappa: az ügynök semmit nem nyithat meg. Adja hozzá egy projekt mappáját.", + "Open the server": "Kiszolgáló megnyitása", + "Plan — changes nothing": "Plan — semmit nem változtat", + "Project folders": "Projektmappák", + "Remove": "Eltávolítás", + "Remove %(dir)s": "%(dir)s eltávolítása", + "Restore": "Visszaállítás", + "Server": "Kiszolgáló", + "Sign in there as you would in a browser.": "Jelentkezzen be ott úgy, mint egy böngészőben.", + "Start the agent": "Ügynök indítása", + "Stop the agent": "Ügynök leállítása", + "Terminal": "Terminál", + "The agent is connected, but may open no folder yet: add one below.": "Az ügynök csatlakozott, de még egy mappát sem nyithat meg: adjon hozzá egyet lent.", + "The agent is off.": "Az ügynök ki van kapcsolva.", + "The agent is running: agent chats can use this computer.": "Az ügynök fut: az ügynök-csevegések használhatják ezt a számítógépet.", + "The agent is starting…": "Az ügynök indul…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "Az ügynök lehetővé teszi, hogy a kiszolgálón futó ügynök-csevegések ennek a számítógépnek a projektjeiben dolgozzanak. Ez a beépített LLeMbas CLI, amely a háttérben fut, amíg az alkalmazás fut.", + "The agent stopped: %(why)s. Trying again shortly.": "Az ügynök leállt: %(why)s. Hamarosan újra próbálja.", + "The most an agent chat may do here": "A legtöbb, amit egy ügynök-csevegés itt tehet", + "This computer is not linked to the server yet.": "Ez a számítógép még nincs összekapcsolva a kiszolgálóval.", + "agent off": "ügynök kikapcsolva", + "agent running": "ügynök fut", + "agent running (service)": "ügynök fut (szolgáltatás)", + "agent starting…": "ügynök indul…", + "agent stopped": "ügynök leállt", + "agent: no folders": "ügynök: nincs mappa", + "not linked": "nincs összekapcsolva", + "update": "frissítés", + "update %(version)s": "frissítés: %(version)s" + }, + "it": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Un servizio LLeMbas CLI è già in esecuzione qui (pid %(pid)s); è l'agente di questo computer.", + "A chat asking for more is lowered to this.": "Una chat che chiede di più viene limitata a questo.", + "A shell as you, in one of these folders.": "Una shell con il tuo utente, in una di queste cartelle.", + "Add folder…": "Aggiungi cartella…", + "Address of your LLeMbas server": "Indirizzo del tuo server LLeMbas", + "Agent": "Agente", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Le chat agente del tuo server possono lavorare in queste cartelle e in tutto ciò che contengono — in nessun altro posto di questo computer. Avviane una nella scheda Agenti del server, su “Locale”.", + "Agent stopped: %(why)s": "L'agente si è fermato: %(why)s", + "Auto — asks nothing": "Auto — non chiede nulla", + "Close": "Chiudi", + "Close (LLeMbas stays in the tray)": "Chiudi (LLeMbas resta nell'area di notifica)", + "Connect": "Connetti", + "Edit — asks before commands": "Edit — chiede prima dei comandi", + "LLeMbas %(version)s is available (this is %(current)s)": "È disponibile LLeMbas %(version)s (questa è la %(current)s)", + "Let the server open a terminal here": "Lascia che il server apra un terminale qui", + "Link again": "Collega di nuovo", + "Link this computer": "Collega questo computer", + "Looking for a LLeMbas server there…": "Cerco un server LLeMbas lì…", + "Manual — asks first": "Manual — chiede prima", + "Maximise": "Ingrandisci", + "Menu": "Menu", + "Minimise": "Riduci a icona", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Ancora nessuna cartella: l'agente non può aprire nulla. Aggiungi la cartella di un progetto.", + "Open the server": "Apri il server", + "Plan — changes nothing": "Plan — non cambia nulla", + "Project folders": "Cartelle dei progetti", + "Remove": "Rimuovi", + "Remove %(dir)s": "Rimuovi %(dir)s", + "Restore": "Ripristina", + "Server": "Server", + "Sign in there as you would in a browser.": "Accedi lì come faresti in un browser.", + "Start the agent": "Avvia l'agente", + "Stop the agent": "Ferma l'agente", + "Terminal": "Terminale", + "The agent is connected, but may open no folder yet: add one below.": "L'agente è connesso, ma non può ancora aprire nessuna cartella: aggiungine una qui sotto.", + "The agent is off.": "L'agente è spento.", + "The agent is running: agent chats can use this computer.": "L'agente è in esecuzione: le chat agente possono usare questo computer.", + "The agent is starting…": "L'agente si sta avviando…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "L'agente permette alle chat agente del tuo server di lavorare nei progetti di questo computer. È il LLeMbas CLI, integrato, in esecuzione in background finché l'app è aperta.", + "The agent stopped: %(why)s. Trying again shortly.": "L'agente si è fermato: %(why)s. Tra poco riprova.", + "The most an agent chat may do here": "Il massimo che una chat agente può fare qui", + "This computer is not linked to the server yet.": "Questo computer non è ancora collegato al server.", + "agent off": "agente spento", + "agent running": "agente attivo", + "agent running (service)": "agente attivo (servizio)", + "agent starting…": "agente in avvio…", + "agent stopped": "agente fermato", + "agent: no folders": "agente: nessuna cartella", + "not linked": "non collegato", + "update": "aggiorna", + "update %(version)s": "aggiorna a %(version)s" + }, + "pl": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Usługa LLeMbas CLI już tu działa (pid %(pid)s); jest agentem tego komputera.", + "A chat asking for more is lowered to this.": "Czat, który prosi o więcej, zostaje ograniczony do tego.", + "A shell as you, in one of these folders.": "Powłoka na Twoim koncie, w jednym z tych folderów.", + "Add folder…": "Dodaj folder…", + "Address of your LLeMbas server": "Adres Twojego serwera LLeMbas", + "Agent": "Agent", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Czaty agenta z Twojego serwera mogą pracować w tych folderach i we wszystkim, co zawierają — nigdzie indziej na tym komputerze. Uruchom czat na karcie Agenci na serwerze, na urządzeniu „Lokalne”.", + "Agent stopped: %(why)s": "Agent się zatrzymał: %(why)s", + "Auto — asks nothing": "Auto — o nic nie pyta", + "Close": "Zamknij", + "Close (LLeMbas stays in the tray)": "Zamknij (LLeMbas zostaje w zasobniku systemowym)", + "Connect": "Połącz", + "Edit — asks before commands": "Edit — pyta przed poleceniami", + "LLeMbas %(version)s is available (this is %(current)s)": "Dostępny jest LLeMbas %(version)s (to jest %(current)s)", + "Let the server open a terminal here": "Pozwól serwerowi otworzyć tu terminal", + "Link again": "Połącz ponownie", + "Link this computer": "Połącz ten komputer", + "Looking for a LLeMbas server there…": "Szukam tam serwera LLeMbas…", + "Manual — asks first": "Manual — najpierw pyta", + "Maximise": "Maksymalizuj", + "Menu": "Menu", + "Minimise": "Minimalizuj", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Nie ma jeszcze folderu: agent nie może nic otworzyć. Dodaj folder projektu.", + "Open the server": "Otwórz serwer", + "Plan — changes nothing": "Plan — niczego nie zmienia", + "Project folders": "Foldery projektów", + "Remove": "Usuń", + "Remove %(dir)s": "Usuń %(dir)s", + "Restore": "Przywróć", + "Server": "Serwer", + "Sign in there as you would in a browser.": "Zaloguj się tam tak jak w przeglądarce.", + "Start the agent": "Uruchom agenta", + "Stop the agent": "Zatrzymaj agenta", + "Terminal": "Terminal", + "The agent is connected, but may open no folder yet: add one below.": "Agent jest połączony, ale nie może jeszcze otworzyć żadnego folderu: dodaj go poniżej.", + "The agent is off.": "Agent jest wyłączony.", + "The agent is running: agent chats can use this computer.": "Agent działa: czaty agenta mogą używać tego komputera.", + "The agent is starting…": "Agent się uruchamia…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "Agent pozwala czatom agenta z Twojego serwera pracować w projektach na tym komputerze. To wbudowane LLeMbas CLI, działające w tle, dopóki działa aplikacja.", + "The agent stopped: %(why)s. Trying again shortly.": "Agent się zatrzymał: %(why)s. Za chwilę spróbuje ponownie.", + "The most an agent chat may do here": "Najwięcej, co czat agenta może tu zrobić", + "This computer is not linked to the server yet.": "Ten komputer nie jest jeszcze połączony z serwerem.", + "agent off": "agent wyłączony", + "agent running": "agent działa", + "agent running (service)": "agent działa (usługa)", + "agent starting…": "agent się uruchamia…", + "agent stopped": "agent zatrzymany", + "agent: no folders": "agent: brak folderów", + "not linked": "niepołączony", + "update": "aktualizacja", + "update %(version)s": "aktualizacja %(version)s" + }, + "sk": { + "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Služba LLeMbas CLI tu už beží (pid %(pid)s); je agentom tohto počítača.", + "A chat asking for more is lowered to this.": "Konverzácia, ktorá chce viac, je znížená na toto.", + "A shell as you, in one of these folders.": "Shell pod vaším účtom, v jednom z týchto priečinkov.", + "Add folder…": "Pridať priečinok…", + "Address of your LLeMbas server": "Adresa vášho servera LLeMbas", + "Agent": "Agent", + "Agent chats from your server can work in these folders and anything inside them — nowhere else on this computer. Start one in the server's Agents tab, on “Local”.": "Agentské konverzácie z vášho servera môžu pracovať v týchto priečinkoch a vo všetkom, čo obsahujú — nikde inde na tomto počítači. Spustite ju na serveri na karte Agenti, na zariadení „Lokálny“.", + "Agent stopped: %(why)s": "Agent sa zastavil: %(why)s", + "Auto — asks nothing": "Auto — na nič sa nepýta", + "Close": "Zavrieť", + "Close (LLeMbas stays in the tray)": "Zavrieť (LLeMbas zostane v oznamovacej oblasti)", + "Connect": "Pripojiť", + "Edit — asks before commands": "Edit — pýta sa pred príkazmi", + "LLeMbas %(version)s is available (this is %(current)s)": "Je k dispozícii LLeMbas %(version)s (toto je %(current)s)", + "Let the server open a terminal here": "Dovoliť serveru otvoriť tu terminál", + "Link again": "Prepojiť znova", + "Link this computer": "Prepojiť tento počítač", + "Looking for a LLeMbas server there…": "Hľadám tam server LLeMbas…", + "Manual — asks first": "Manual — najprv sa opýta", + "Maximise": "Maximalizovať", + "Menu": "Ponuka", + "Minimise": "Minimalizovať", + "No folder yet: the agent can open nothing. Add the folder of a project.": "Zatiaľ žiadny priečinok: agent nemôže nič otvoriť. Pridajte priečinok projektu.", + "Open the server": "Otvoriť server", + "Plan — changes nothing": "Plan — nič nemení", + "Project folders": "Priečinky projektov", + "Remove": "Odstrániť", + "Remove %(dir)s": "Odstrániť %(dir)s", + "Restore": "Obnoviť", + "Server": "Server", + "Sign in there as you would in a browser.": "Prihláste sa tam ako v prehliadači.", + "Start the agent": "Spustiť agenta", + "Stop the agent": "Zastaviť agenta", + "Terminal": "Terminál", + "The agent is connected, but may open no folder yet: add one below.": "Agent je pripojený, ale zatiaľ nesmie otvoriť žiadny priečinok: pridajte ho nižšie.", + "The agent is off.": "Agent je vypnutý.", + "The agent is running: agent chats can use this computer.": "Agent beží: agentské konverzácie môžu používať tento počítač.", + "The agent is starting…": "Agent sa spúšťa…", + "The agent lets agent chats on your server work in this computer's projects. It is the LLeMbas CLI, built in, running in the background while the app runs.": "Agent umožňuje agentským konverzáciám na vašom serveri pracovať v projektoch na tomto počítači. Je to vstavané LLeMbas CLI, ktoré beží na pozadí, kým beží aplikácia.", + "The agent stopped: %(why)s. Trying again shortly.": "Agent sa zastavil: %(why)s. O chvíľu to skúsi znova.", + "The most an agent chat may do here": "Najviac, čo tu agentská konverzácia smie", + "This computer is not linked to the server yet.": "Tento počítač ešte nie je prepojený so serverom.", + "agent off": "agent vypnutý", + "agent running": "agent beží", + "agent running (service)": "agent beží (služba)", + "agent starting…": "agent sa spúšťa…", + "agent stopped": "agent zastavený", + "agent: no folders": "agent: žiadne priečinky", + "not linked": "neprepojené", + "update": "aktualizácia", + "update %(version)s": "aktualizácia %(version)s" + } +} + +const LEMBAS_LANG = (() => { + const code = String(navigator.language || "").slice(0, 2).toLowerCase() + return Object.prototype.hasOwnProperty.call(LEMBAS_I18N, code) ? code : "en" +})() +document.documentElement.lang = LEMBAS_LANG + +function t(text, fields) { + const key = String(text).replace(/\s+/g, " ").trim() + const table = LEMBAS_I18N[LEMBAS_LANG] || {} + const out = Object.prototype.hasOwnProperty.call(table, key) ? table[key] : key + if (!fields) return out + return out.replace(/%\((\w+)\)s/g, (all, name) => + Object.prototype.hasOwnProperty.call(fields, name) ? String(fields[name]) : all) +} + +function translatePage() { + for (const el of document.querySelectorAll("[data-i18n]")) el.textContent = t(el.textContent) + for (const attr of ["title", "aria-label", "placeholder"]) { + for (const el of document.querySelectorAll(`[data-i18n-${attr}]`)) { + el.setAttribute(attr, t(el.getAttribute(attr))) + } + } +} +if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", translatePage) +else translatePage() diff --git a/ui/index.html b/ui/index.html index 235b87b..e3c5f8b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -7,6 +7,8 @@ LLeMbas + + @@ -20,55 +22,54 @@
-

[ SERVER ]

+

Server

- +
- +
-

Sign in there as you would in a browser.

+

Sign in there as you would in a browser.

diff --git a/ui/titlebar.html b/ui/titlebar.html index 233d482..956b3e3 100644 --- a/ui/titlebar.html +++ b/ui/titlebar.html @@ -6,6 +6,8 @@ LLeMbas + + @@ -14,22 +16,22 @@ LLeMbas - - + - - - - diff --git a/ui/titlebar.js b/ui/titlebar.js index e6de386..159c0b8 100644 --- a/ui/titlebar.js +++ b/ui/titlebar.js @@ -1,6 +1,7 @@ // The title bar. Moving and double-click maximising come from `data-tauri-drag-region`; the // buttons call the window directly; the menu and the agent pill open the app's native menu (the -// tray's items), which can reach past this 36-pixel-high page. +// tray's items), which can reach past this 36-pixel-high page. The bar is painted in the theme +// the app reports in `status` (the server page's); every word goes through `t` (i18n.js). "use strict" const { invoke } = window.__TAURI__.core @@ -10,16 +11,17 @@ const win = window.__TAURI__.window.getCurrentWindow() const $ = (id) => document.getElementById(id) const WORDS = { - off: "agent off", - not_linked: "not linked", - starting: "agent starting…", - running: "agent running", - elsewhere: "agent running (service)", - failed: "agent stopped", - no_folders: "agent: no folders", + off: () => t("agent off"), + not_linked: () => t("not linked"), + starting: () => t("agent starting…"), + running: () => t("agent running"), + elsewhere: () => t("agent running (service)"), + failed: () => t("agent stopped"), + no_folders: () => t("agent: no folders"), } function render(s) { + window.lembasTheme.apply(s.theme) let host = "" try { host = s.server ? new URL(s.server).host : "" @@ -30,18 +32,20 @@ function render(s) { const noFolders = s.remote && !(s.remote.enabled && s.remote.roots.length) const state = s.agent.state === "running" && noFolders ? "no_folders" : s.agent.state $("agent").dataset.state = state - $("agent-text").textContent = WORDS[state] ?? state - $("agent").title = state === "failed" ? `Agent stopped: ${s.agent.detail}` : "Agent" + $("agent-text").textContent = WORDS[state] ? WORDS[state]() : state + $("agent").title = state === "failed" ? t("Agent stopped: %(why)s", { why: s.agent.detail }) : t("Agent") $("update").hidden = !s.update - $("update").textContent = s.update ? `update ${s.update}` : "update" - $("update").title = s.update ? `LLeMbas ${s.update} is available (this is ${s.version})` : "" + $("update").textContent = s.update ? t("update %(version)s", { version: s.update }) : t("update") + $("update").title = s.update + ? t("LLeMbas %(version)s is available (this is %(current)s)", { version: s.update, current: s.version }) + : "" } function maximized(on) { const b = $("maximize") b.toggleAttribute("data-maximized", on) - b.setAttribute("aria-label", on ? "Restore" : "Maximise") - b.title = on ? "Restore" : "Maximise" + b.setAttribute("aria-label", on ? t("Restore") : t("Maximise")) + b.title = on ? t("Restore") : t("Maximise") } $("minimize").addEventListener("click", () => win.minimize())