From d786424353bb1bb14232294a85d00fd4d7e03a10 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 16 Jan 2026 17:03:34 -0700 Subject: [PATCH] translate backend strings --- core/Cargo.lock | 65 +- core/locales/i18n.yaml | 4096 ++++++++++++++++++++++ core/src/action.rs | 6 +- core/src/auth.rs | 35 +- core/src/backup/backup_bulk.rs | 19 +- core/src/backup/mod.rs | 6 +- core/src/backup/restore.rs | 4 +- core/src/backup/target/cifs.rs | 14 +- core/src/backup/target/mod.rs | 10 +- core/src/bins/container_cli.rs | 1 + core/src/bins/deprecated.rs | 9 +- core/src/bins/mod.rs | 86 +- core/src/bins/registry.rs | 4 +- core/src/bins/start_cli.rs | 1 + core/src/bins/start_init.rs | 41 +- core/src/bins/startd.rs | 7 +- core/src/bins/tunnel.rs | 8 +- core/src/context/cli.rs | 8 +- core/src/context/diagnostic.rs | 2 +- core/src/context/rpc.rs | 24 +- core/src/context/setup.rs | 12 +- core/src/db/mod.rs | 8 +- core/src/diagnostic.rs | 21 +- core/src/disk/fsck/ext4.rs | 17 +- core/src/disk/main.rs | 5 +- core/src/disk/mod.rs | 4 +- core/src/disk/mount/util.rs | 5 +- core/src/disk/util.rs | 29 +- core/src/error.rs | 169 +- core/src/init.rs | 71 +- core/src/lib.rs | 170 +- core/src/logs.rs | 4 +- core/src/lxc/mod.rs | 48 +- core/src/middleware/auth/local.rs | 2 +- core/src/middleware/auth/session.rs | 8 +- core/src/middleware/auth/signature.rs | 10 +- core/src/middleware/db.rs | 3 +- core/src/net/acme.rs | 4 +- core/src/net/dns.rs | 29 +- core/src/net/forward.rs | 8 +- core/src/net/gateway.rs | 41 +- core/src/net/host/address.rs | 14 +- core/src/net/host/binding.rs | 4 +- core/src/net/host/mod.rs | 2 +- core/src/net/mod.rs | 17 +- core/src/net/ssl.rs | 2 +- core/src/net/tor/arti.rs | 76 +- core/src/net/tor/ctor.rs | 16 +- core/src/net/tunnel.rs | 4 +- core/src/net/wifi.rs | 100 +- core/src/notifications.rs | 16 +- core/src/prelude.rs | 1 + core/src/registry/admin.rs | 21 +- core/src/registry/asset.rs | 6 +- core/src/registry/context.rs | 6 +- core/src/registry/db.rs | 4 +- core/src/registry/info.rs | 6 +- core/src/registry/mod.rs | 10 +- core/src/registry/os/asset/add.rs | 10 +- core/src/registry/os/asset/get.rs | 6 +- core/src/registry/os/asset/mod.rs | 6 +- core/src/registry/os/asset/sign.rs | 6 +- core/src/registry/os/mod.rs | 6 +- core/src/registry/os/version/mod.rs | 8 +- core/src/registry/os/version/signer.rs | 8 +- core/src/registry/package/add.rs | 16 +- core/src/registry/package/category.rs | 8 +- core/src/registry/package/get.rs | 10 +- core/src/registry/package/mod.rs | 30 +- core/src/registry/package/signer.rs | 8 +- core/src/registry/signer.rs | 2 +- core/src/s9pk/rpc.rs | 20 +- core/src/service/action.rs | 6 +- core/src/service/effects/action.rs | 9 +- core/src/service/effects/dependency.rs | 5 +- core/src/service/mod.rs | 23 +- core/src/service/persistent_container.rs | 12 +- core/src/service/service_actor.rs | 4 +- core/src/service/transition/backup.rs | 2 +- core/src/service/uninstall.rs | 2 +- core/src/setup.rs | 22 +- core/src/shutdown.rs | 11 +- core/src/ssh.rs | 11 +- core/src/system.rs | 138 +- core/src/tunnel/api.rs | 24 +- core/src/tunnel/auth.rs | 10 +- core/src/tunnel/db.rs | 4 +- core/src/tunnel/web.rs | 20 +- core/src/tunnel/wg.rs | 1 + core/src/update/mod.rs | 24 +- core/src/util/cpupower.rs | 8 +- core/src/util/mod.rs | 21 + core/src/util/net.rs | 2 +- core/src/util/rpc.rs | 4 +- core/src/util/serde.rs | 14 +- core/src/util/tui.rs | 12 +- 96 files changed, 5177 insertions(+), 775 deletions(-) create mode 100644 core/locales/i18n.yaml diff --git a/core/Cargo.lock b/core/Cargo.lock index de3ff8c11..22baac8e9 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -1177,9 +1177,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "jobserver", @@ -2726,9 +2726,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "fixed-capacity-vec" @@ -4186,9 +4186,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -6587,7 +6587,7 @@ dependencies = [ [[package]] name = "rpc-toolkit" version = "0.3.2" -source = "git+https://github.com/Start9Labs/rpc-toolkit.git#406ee9e88bf20e3155f150eb755b5b9c2aefd167" +source = "git+https://github.com/Start9Labs/rpc-toolkit.git#39e547ff99d997c19f9b6483b28a4394ca5a07bc" dependencies = [ "async-stream", "async-trait", @@ -6727,9 +6727,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" @@ -6806,7 +6806,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] @@ -6834,9 +6834,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -6856,7 +6856,7 @@ dependencies = [ "rustls 0.23.36", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.9", "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", @@ -6882,9 +6882,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "aws-lc-rs", "ring", @@ -9556,7 +9556,7 @@ dependencies = [ "paste", "pin-project", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.9", "thiserror 2.0.17", "tokio", "tokio-util", @@ -10201,9 +10201,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] @@ -10225,9 +10225,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -10238,11 +10238,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -10251,9 +10252,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10261,9 +10262,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -10274,9 +10275,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -10375,9 +10376,9 @@ checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -10974,9 +10975,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" diff --git a/core/locales/i18n.yaml b/core/locales/i18n.yaml new file mode 100644 index 000000000..eed8a2701 --- /dev/null +++ b/core/locales/i18n.yaml @@ -0,0 +1,4096 @@ +_version: 2 + +bins.deprecated.renamed: + en_US: "%{old} has been renamed to %{new}" + de_DE: "%{old} wurde in %{new} umbenannt" + es_ES: "%{old} ha sido renombrado a %{new}" + fr_FR: "%{old} a été renommé en %{new}" + pl_PL: "%{old} zostało zmienione na %{new}" + +bins.deprecated.removed: + en_US: "%{name} has been removed" + de_DE: "%{name} wurde entfernt" + es_ES: "%{name} ha sido eliminado" + fr_FR: "%{name} a été supprimé" + pl_PL: "%{name} zostało usunięte" + +bins.mod.unknown-executable: + en_US: "unknown executable: %{name}" + de_DE: "Unbekannte ausführbare Datei: %{name}" + es_ES: "ejecutable desconocido: %{name}" + fr_FR: "exécutable inconnu : %{name}" + pl_PL: "nieznany plik wykonywalny: %{name}" + +bins.mod.does-not-exist: + en_US: "%{name} does not exist in MultiExecutable" + de_DE: "%{name} existiert nicht in MultiExecutable" + es_ES: "%{name} no existe en MultiExecutable" + fr_FR: "%{name} n'existe pas dans MultiExecutable" + pl_PL: "%{name} nie istnieje w MultiExecutable" + +bins.start-init.updating-firmware: + en_US: "Updating Firmware" + de_DE: "Firmware wird aktualisiert" + es_ES: "Actualizando firmware" + fr_FR: "Mise à jour du firmware" + pl_PL: "Aktualizacja oprogramowania" + +bins.start-init.rebooting: + en_US: "Rebooting" + de_DE: "Neustart" + es_ES: "Reiniciando" + fr_FR: "Redémarrage" + pl_PL: "Ponowne uruchamianie" + +bins.start-init.opening-data-drive: + en_US: "Opening data drive" + de_DE: "Datenlaufwerk wird geöffnet" + es_ES: "Abriendo unidad de datos" + fr_FR: "Ouverture du disque de données" + pl_PL: "Otwieranie dysku danych" + +bins.start-init.setup-mode-exited: + en_US: "Setup mode exited before setup completed" + de_DE: "Der Einrichtungsmodus wurde beendet, bevor die Einrichtung abgeschlossen war" + es_ES: "El modo de configuración se cerró antes de completar la configuración" + fr_FR: "Le mode configuration s'est terminé avant la fin de la configuration" + pl_PL: "Tryb konfiguracji zakończył się przed ukończeniem konfiguracji" + +bins.start-init.loaded-disk: + en_US: "Loaded Disk" + de_DE: "Festplatte geladen" + es_ES: "Disco cargado" + fr_FR: "Disque chargé" + pl_PL: "Dysk załadowany" + +bins.start-init.error-checking-firmware: + en_US: "Error checking for firmware update: %{error}" + de_DE: "Fehler bei der Suche nach Firmware-Update: %{error}" + es_ES: "Error al buscar actualización de firmware: %{error}" + fr_FR: "Erreur lors de la vérification de la mise à jour du firmware : %{error}" + pl_PL: "Błąd podczas sprawdzania aktualizacji oprogramowania: %{error}" + +bins.start-init.error-firmware-update: + en_US: "Error performing firmware update: %{error}" + de_DE: "Fehler beim Durchführen des Firmware-Updates: %{error}" + es_ES: "Error al realizar la actualización de firmware: %{error}" + fr_FR: "Erreur lors de la mise à jour du firmware : %{error}" + pl_PL: "Błąd podczas wykonywania aktualizacji oprogramowania: %{error}" + +bins.start-init.failed-to-kill-kiosk: + en_US: "Failed to kill kiosk: %{error}" + de_DE: "Kiosk konnte nicht beendet werden: %{error}" + es_ES: "Error al cerrar el kiosco: %{error}" + fr_FR: "Échec de la fermeture du kiosque : %{error}" + pl_PL: "Nie udało się zamknąć kiosku: %{error}" + +bins.startd.metrics-daemon-panicked: + en_US: "Metrics daemon panicked!" + de_DE: "Metriken-Daemon ist abgestürzt!" + es_ES: "El demonio de métricas entró en pánico!" + fr_FR: "Le démon de métriques a paniqué !" + pl_PL: "Demon metryk uległ panice!" + +bins.startd.metrics-daemon-shutdown: + en_US: "Metrics daemon Shutdown" + de_DE: "Metriken-Daemon heruntergefahren" + es_ES: "Demonio de métricas apagado" + fr_FR: "Démon de métriques arrêté" + pl_PL: "Demon metryk zamknięty" + +bins.startd.failed-to-initialize-runtime: + en_US: "failed to initialize runtime" + de_DE: "Laufzeit konnte nicht initialisiert werden" + es_ES: "error al inicializar el entorno de ejecución" + fr_FR: "échec de l'initialisation du runtime" + pl_PL: "nie udało się zainicjować środowiska wykonawczego" + +bins.registry.failed-to-initialize-runtime: + en_US: "failed to initialize runtime" + de_DE: "Laufzeit konnte nicht initialisiert werden" + es_ES: "error al inicializar el entorno de ejecución" + fr_FR: "échec de l'initialisation du runtime" + pl_PL: "nie udało się zainicjować środowiska wykonawczego" + +bins.tunnel.failed-to-initialize-runtime: + en_US: "failed to initialize runtime" + de_DE: "Laufzeit konnte nicht initialisiert werden" + es_ES: "error al inicializar el entorno de ejecución" + fr_FR: "échec de l'initialisation du runtime" + pl_PL: "nie udało się zainicjować środowiska wykonawczego" + +bins.tunnel.error-adding-ssl-listener: + en_US: "error adding ssl listener: %{error}" + de_DE: "Fehler beim Hinzufügen des SSL-Listeners: %{error}" + es_ES: "error al agregar el listener SSL: %{error}" + fr_FR: "erreur lors de l'ajout de l'écouteur SSL : %{error}" + pl_PL: "błąd podczas dodawania nasłuchiwacza SSL: %{error}" + +bins.tunnel.error-updating-webserver-bind: + en_US: "error updating webserver bind: %{error}" + de_DE: "Fehler beim Aktualisieren der Webserver-Bindung: %{error}" + es_ES: "error al actualizar el enlace del servidor web: %{error}" + fr_FR: "erreur lors de la mise à jour de la liaison du serveur web : %{error}" + pl_PL: "błąd podczas aktualizacji powiązania serwera WWW: %{error}" + +# setup.rs +setup.opening-data-drive: + en_US: "Opening data drive" + de_DE: "Datenlaufwerk wird geöffnet" + es_ES: "Abriendo unidad de datos" + fr_FR: "Ouverture du disque de données" + pl_PL: "Otwieranie dysku danych" + +setup.couldnt-decode-password: + en_US: "Couldn't decode password" + de_DE: "Passwort konnte nicht dekodiert werden" + es_ES: "No se pudo decodificar la contraseña" + fr_FR: "Impossible de décoder le mot de passe" + pl_PL: "Nie można zdekodować hasła" + +setup.disk-errors-corrected-restart-required: + en_US: "Errors were corrected with your disk, but the server must be restarted in order to proceed" + de_DE: "Fehler auf Ihrer Festplatte wurden korrigiert, aber der Server muss neu gestartet werden, um fortzufahren" + es_ES: "Se corrigieron errores en su disco, pero el servidor debe reiniciarse para continuar" + fr_FR: "Des erreurs ont été corrigées sur votre disque, mais le serveur doit être redémarré pour continuer" + pl_PL: "Błędy na dysku zostały naprawione, ale serwer musi zostać uruchomiony ponownie, aby kontynuować" + +setup.no-backup-found: + en_US: "No Backup Found" + de_DE: "Keine Sicherung gefunden" + es_ES: "No se encontró copia de seguridad" + fr_FR: "Aucune sauvegarde trouvée" + pl_PL: "Nie znaleziono kopii zapasowej" + +setup.couldnt-decode-startos-password: + en_US: "Couldn't decode startOsPassword" + de_DE: "startOsPassword konnte nicht dekodiert werden" + es_ES: "No se pudo decodificar startOsPassword" + fr_FR: "Impossible de décoder startOsPassword" + pl_PL: "Nie można zdekodować startOsPassword" + +setup.couldnt-decode-recovery-password: + en_US: "Couldn't decode recoveryPassword" + de_DE: "recoveryPassword konnte nicht dekodiert werden" + es_ES: "No se pudo decodificar recoveryPassword" + fr_FR: "Impossible de décoder recoveryPassword" + pl_PL: "Nie można zdekodować recoveryPassword" + +setup.execute-not-completed: + en_US: "setup.execute has not completed successfully" + de_DE: "setup.execute wurde nicht erfolgreich abgeschlossen" + es_ES: "setup.execute no se completó correctamente" + fr_FR: "setup.execute ne s'est pas terminé avec succès" + pl_PL: "setup.execute nie zakończyło się pomyślnie" + +setup.formatting-data-drive: + en_US: "Formatting data drive" + de_DE: "Datenlaufwerk wird formatiert" + es_ES: "Formateando unidad de datos" + fr_FR: "Formatage du disque de données" + pl_PL: "Formatowanie dysku danych" + +setup.restoring-backup: + en_US: "Restoring backup" + de_DE: "Sicherung wird wiederhergestellt" + es_ES: "Restaurando copia de seguridad" + fr_FR: "Restauration de la sauvegarde" + pl_PL: "Przywracanie kopii zapasowej" + +setup.transferring-data: + en_US: "Transferring data" + de_DE: "Daten werden übertragen" + es_ES: "Transfiriendo datos" + fr_FR: "Transfert de données" + pl_PL: "Przesyłanie danych" + +# system.rs +system.governor-not-available: + en_US: "Governor %{governor} not available" + de_DE: "Governor %{governor} nicht verfügbar" + es_ES: "El governor %{governor} no está disponible" + fr_FR: "Le gouverneur %{governor} n'est pas disponible" + pl_PL: "Governor %{governor} nie jest dostępny" + +system.could-not-get-initial-temperature: + en_US: "Could not get initial temperature: %{error}" + de_DE: "Anfangstemperatur konnte nicht abgerufen werden: %{error}" + es_ES: "No se pudo obtener la temperatura inicial: %{error}" + fr_FR: "Impossible d'obtenir la température initiale : %{error}" + pl_PL: "Nie można uzyskać początkowej temperatury: %{error}" + +system.could-not-get-initial-cpu-info: + en_US: "Could not get initial cpu info: %{error}" + de_DE: "Anfangs-CPU-Informationen konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudo obtener la información inicial de la CPU: %{error}" + fr_FR: "Impossible d'obtenir les informations CPU initiales : %{error}" + pl_PL: "Nie można uzyskać początkowych informacji o CPU: %{error}" + +system.could-not-get-initial-proc-stat: + en_US: "Could not get initial proc stat: %{error}" + de_DE: "Anfangs-Prozessstatistiken konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudo obtener el estado inicial del proceso: %{error}" + fr_FR: "Impossible d'obtenir les statistiques de processus initiales : %{error}" + pl_PL: "Nie można uzyskać początkowych statystyk procesów: %{error}" + +system.could-not-get-initial-mem-info: + en_US: "Could not get initial mem info: %{error}" + de_DE: "Anfangs-Speicherinformationen konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudo obtener la información inicial de memoria: %{error}" + fr_FR: "Impossible d'obtenir les informations mémoire initiales : %{error}" + pl_PL: "Nie można uzyskać początkowych informacji o pamięci: %{error}" + +system.could-not-get-initial-disk-info: + en_US: "Could not get initial disk info: %{error}" + de_DE: "Anfangs-Festplatteninformationen konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudo obtener la información inicial del disco: %{error}" + fr_FR: "Impossible d'obtenir les informations disque initiales : %{error}" + pl_PL: "Nie można uzyskać początkowych informacji o dysku: %{error}" + +system.could-not-get-new-temperature: + en_US: "Could not get new temperature: %{error}" + de_DE: "Neue Temperatur konnte nicht abgerufen werden: %{error}" + es_ES: "No se pudo obtener la nueva temperatura: %{error}" + fr_FR: "Impossible d'obtenir la nouvelle température : %{error}" + pl_PL: "Nie można uzyskać nowej temperatury: %{error}" + +system.could-not-get-new-cpu-metrics: + en_US: "Could not get new CPU Metrics: %{error}" + de_DE: "Neue CPU-Metriken konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudieron obtener las nuevas métricas de CPU: %{error}" + fr_FR: "Impossible d'obtenir les nouvelles métriques CPU : %{error}" + pl_PL: "Nie można uzyskać nowych metryk CPU: %{error}" + +system.could-not-get-new-memory-metrics: + en_US: "Could not get new Memory Metrics: %{error}" + de_DE: "Neue Speichermetriken konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudieron obtener las nuevas métricas de memoria: %{error}" + fr_FR: "Impossible d'obtenir les nouvelles métriques mémoire : %{error}" + pl_PL: "Nie można uzyskać nowych metryk pamięci: %{error}" + +system.could-not-get-new-disk-metrics: + en_US: "Could not get new Disk Metrics: %{error}" + de_DE: "Neue Festplattenmetriken konnten nicht abgerufen werden: %{error}" + es_ES: "No se pudieron obtener las nuevas métricas de disco: %{error}" + fr_FR: "Impossible d'obtenir les nouvelles métriques disque : %{error}" + pl_PL: "Nie można uzyskać nowych metryk dysku: %{error}" + +system.no-temperatures-available: + en_US: "No temperatures available" + de_DE: "Keine Temperaturen verfügbar" + es_ES: "No hay temperaturas disponibles" + fr_FR: "Aucune température disponible" + pl_PL: "Brak dostępnych temperatur" + +system.invalid-proc-stat-column: + en_US: "Invalid /proc/stat column value: %{error}" + de_DE: "Ungültiger /proc/stat-Spaltenwert: %{error}" + es_ES: "Valor de columna /proc/stat inválido: %{error}" + fr_FR: "Valeur de colonne /proc/stat invalide : %{error}" + pl_PL: "Nieprawidłowa wartość kolumny /proc/stat: %{error}" + +system.columns-missing-from-proc-stat: + en_US: "Columns missing from /proc/stat. Need 10, found %{count}" + de_DE: "Spalten fehlen in /proc/stat. Benötigt 10, gefunden %{count}" + es_ES: "Faltan columnas en /proc/stat. Se necesitan 10, se encontraron %{count}" + fr_FR: "Colonnes manquantes dans /proc/stat. 10 requises, %{count} trouvées" + pl_PL: "Brakujące kolumny w /proc/stat. Wymagane 10, znaleziono %{count}" + +system.invalid-meminfo-line: + en_US: "Invalid meminfo line: %{line}" + de_DE: "Ungültige meminfo-Zeile: %{line}" + es_ES: "Línea de meminfo inválida: %{line}" + fr_FR: "Ligne meminfo invalide : %{line}" + pl_PL: "Nieprawidłowa linia meminfo: %{line}" + +system.field-missing-from-meminfo: + en_US: "%{field} missing from /proc/meminfo" + de_DE: "%{field} fehlt in /proc/meminfo" + es_ES: "%{field} falta en /proc/meminfo" + fr_FR: "%{field} manquant dans /proc/meminfo" + pl_PL: "%{field} brakuje w /proc/meminfo" + +# error.rs - ErrorKind strings +error.unknown: + en_US: "Unknown Error" + de_DE: "Unbekannter Fehler" + es_ES: "Error Desconocido" + fr_FR: "Erreur Inconnue" + pl_PL: "Nieznany Błąd" + +error.filesystem: + en_US: "Filesystem I/O Error" + de_DE: "Dateisystem-E/A-Fehler" + es_ES: "Error de E/S del Sistema de Archivos" + fr_FR: "Erreur E/S du Système de Fichiers" + pl_PL: "Błąd Wejścia/Wyjścia Systemu Plików" + +error.docker: + en_US: "Docker Error" + de_DE: "Docker-Fehler" + es_ES: "Error de Docker" + fr_FR: "Erreur Docker" + pl_PL: "Błąd Docker" + +error.config-spec-violation: + en_US: "Config Spec Violation" + de_DE: "Verletzung der Konfigurationsspezifikation" + es_ES: "Violación de Especificación de Configuración" + fr_FR: "Violation de Spécification de Configuration" + pl_PL: "Naruszenie Specyfikacji Konfiguracji" + +error.config-rules-violation: + en_US: "Config Rules Violation" + de_DE: "Verletzung der Konfigurationsregeln" + es_ES: "Violación de Reglas de Configuración" + fr_FR: "Violation des Règles de Configuration" + pl_PL: "Naruszenie Reguł Konfiguracji" + +error.not-found: + en_US: "Not Found" + de_DE: "Nicht Gefunden" + es_ES: "No Encontrado" + fr_FR: "Non Trouvé" + pl_PL: "Nie Znaleziono" + +error.incorrect-password: + en_US: "Incorrect Password" + de_DE: "Falsches Passwort" + es_ES: "Contraseña Incorrecta" + fr_FR: "Mot de Passe Incorrect" + pl_PL: "Nieprawidłowe Hasło" + +error.version-incompatible: + en_US: "Version Incompatible" + de_DE: "Version Inkompatibel" + es_ES: "Versión Incompatible" + fr_FR: "Version Incompatible" + pl_PL: "Wersja Niekompatybilna" + +error.network: + en_US: "Network Error" + de_DE: "Netzwerkfehler" + es_ES: "Error de Red" + fr_FR: "Erreur Réseau" + pl_PL: "Błąd Sieci" + +error.registry: + en_US: "Registry Error" + de_DE: "Registrierungsfehler" + es_ES: "Error de Registro" + fr_FR: "Erreur de Registre" + pl_PL: "Błąd Rejestru" + +error.serialization: + en_US: "Serialization Error" + de_DE: "Serialisierungsfehler" + es_ES: "Error de Serialización" + fr_FR: "Erreur de Sérialisation" + pl_PL: "Błąd Serializacji" + +error.deserialization: + en_US: "Deserialization Error" + de_DE: "Deserialisierungsfehler" + es_ES: "Error de Deserialización" + fr_FR: "Erreur de Désérialisation" + pl_PL: "Błąd Deserializacji" + +error.utf8: + en_US: "UTF-8 Parse Error" + de_DE: "UTF-8-Analysefehler" + es_ES: "Error de Análisis UTF-8" + fr_FR: "Erreur d'Analyse UTF-8" + pl_PL: "Błąd Parsowania UTF-8" + +error.parse-version: + en_US: "Version Parsing Error" + de_DE: "Fehler beim Analysieren der Version" + es_ES: "Error al Analizar la Versión" + fr_FR: "Erreur d'Analyse de Version" + pl_PL: "Błąd Parsowania Wersji" + +error.incorrect-disk: + en_US: "Incorrect Disk" + de_DE: "Falsche Festplatte" + es_ES: "Disco Incorrecto" + fr_FR: "Disque Incorrect" + pl_PL: "Nieprawidłowy Dysk" + +error.dependency: + en_US: "Dependency Error" + de_DE: "Abhängigkeitsfehler" + es_ES: "Error de Dependencia" + fr_FR: "Erreur de Dépendance" + pl_PL: "Błąd Zależności" + +error.parse-s9pk: + en_US: "S9PK Parsing Error" + de_DE: "S9PK-Analysefehler" + es_ES: "Error al Analizar S9PK" + fr_FR: "Erreur d'Analyse S9PK" + pl_PL: "Błąd Parsowania S9PK" + +error.parse-url: + en_US: "URL Parsing Error" + de_DE: "URL-Analysefehler" + es_ES: "Error al Analizar URL" + fr_FR: "Erreur d'Analyse d'URL" + pl_PL: "Błąd Parsowania URL" + +error.disk-not-available: + en_US: "Disk Not Available" + de_DE: "Festplatte Nicht Verfügbar" + es_ES: "Disco No Disponible" + fr_FR: "Disque Non Disponible" + pl_PL: "Dysk Niedostępny" + +error.block-device: + en_US: "Block Device Error" + de_DE: "Blockgerätefehler" + es_ES: "Error de Dispositivo de Bloque" + fr_FR: "Erreur de Périphérique Bloc" + pl_PL: "Błąd Urządzenia Blokowego" + +error.invalid-onion-address: + en_US: "Invalid Onion Address" + de_DE: "Ungültige Onion-Adresse" + es_ES: "Dirección Onion Inválida" + fr_FR: "Adresse Onion Invalide" + pl_PL: "Nieprawidłowy Adres Onion" + +error.pack: + en_US: "Pack Error" + de_DE: "Paketfehler" + es_ES: "Error de Empaquetado" + fr_FR: "Erreur de Paquet" + pl_PL: "Błąd Pakietu" + +error.validate-s9pk: + en_US: "S9PK Validation Error" + de_DE: "S9PK-Validierungsfehler" + es_ES: "Error de Validación S9PK" + fr_FR: "Erreur de Validation S9PK" + pl_PL: "Błąd Walidacji S9PK" + +error.disk-corrupted: + en_US: "Disk Corrupted" + de_DE: "Festplatte Beschädigt" + es_ES: "Disco Dañado" + fr_FR: "Disque Corrompu" + pl_PL: "Dysk Uszkodzony" + +error.tor: + en_US: "Tor Daemon Error" + de_DE: "Tor-Daemon-Fehler" + es_ES: "Error del Daemon Tor" + fr_FR: "Erreur du Démon Tor" + pl_PL: "Błąd Demona Tor" + +error.config-gen: + en_US: "Config Generation Error" + de_DE: "Fehler bei der Konfigurationsgenerierung" + es_ES: "Error de Generación de Configuración" + fr_FR: "Erreur de Génération de Configuration" + pl_PL: "Błąd Generowania Konfiguracji" + +error.parse-number: + en_US: "Number Parsing Error" + de_DE: "Fehler beim Analysieren der Zahl" + es_ES: "Error al Analizar Número" + fr_FR: "Erreur d'Analyse de Nombre" + pl_PL: "Błąd Parsowania Liczby" + +error.database: + en_US: "Database Error" + de_DE: "Datenbankfehler" + es_ES: "Error de Base de Datos" + fr_FR: "Erreur de Base de Données" + pl_PL: "Błąd Bazy Danych" + +error.invalid-id: + en_US: "Invalid ID" + de_DE: "Ungültige ID" + es_ES: "ID Inválido" + fr_FR: "ID Invalide" + pl_PL: "Nieprawidłowe ID" + +error.invalid-signature: + en_US: "Invalid Signature" + de_DE: "Ungültige Signatur" + es_ES: "Firma Inválida" + fr_FR: "Signature Invalide" + pl_PL: "Nieprawidłowy Podpis" + +error.backup: + en_US: "Backup Error" + de_DE: "Sicherungsfehler" + es_ES: "Error de Copia de Seguridad" + fr_FR: "Erreur de Sauvegarde" + pl_PL: "Błąd Kopii Zapasowej" + +error.restore: + en_US: "Restore Error" + de_DE: "Wiederherstellungsfehler" + es_ES: "Error de Restauración" + fr_FR: "Erreur de Restauration" + pl_PL: "Błąd Przywracania" + +error.authorization: + en_US: "Unauthorized" + de_DE: "Nicht Autorisiert" + es_ES: "No Autorizado" + fr_FR: "Non Autorisé" + pl_PL: "Brak Autoryzacji" + +error.auto-configure: + en_US: "Auto-Configure Error" + de_DE: "Fehler bei der Automatischen Konfiguration" + es_ES: "Error de Configuración Automática" + fr_FR: "Erreur de Configuration Automatique" + pl_PL: "Błąd Automatycznej Konfiguracji" + +error.action: + en_US: "Action Failed" + de_DE: "Aktion Fehlgeschlagen" + es_ES: "Acción Fallida" + fr_FR: "Action Échouée" + pl_PL: "Akcja Nie Powiodła Się" + +error.rate-limited: + en_US: "Rate Limited" + de_DE: "Ratenlimit Erreicht" + es_ES: "Límite de Velocidad Alcanzado" + fr_FR: "Limite de Débit Atteinte" + pl_PL: "Przekroczono Limit Częstotliwości" + +error.invalid-request: + en_US: "Invalid Request" + de_DE: "Ungültige Anfrage" + es_ES: "Solicitud Inválida" + fr_FR: "Requête Invalide" + pl_PL: "Nieprawidłowe Żądanie" + +error.migration-failed: + en_US: "Migration Failed" + de_DE: "Migration Fehlgeschlagen" + es_ES: "Migración Fallida" + fr_FR: "Migration Échouée" + pl_PL: "Migracja Nie Powiodła Się" + +error.uninitialized: + en_US: "Uninitialized" + de_DE: "Nicht Initialisiert" + es_ES: "No Inicializado" + fr_FR: "Non Initialisé" + pl_PL: "Niezainicjalizowany" + +error.parse-net-address: + en_US: "Net Address Parsing Error" + de_DE: "Fehler beim Analysieren der Netzwerkadresse" + es_ES: "Error al Analizar Dirección de Red" + fr_FR: "Erreur d'Analyse d'Adresse Réseau" + pl_PL: "Błąd Parsowania Adresu Sieciowego" + +error.parse-ssh-key: + en_US: "SSH Key Parsing Error" + de_DE: "Fehler beim Analysieren des SSH-Schlüssels" + es_ES: "Error al Analizar Clave SSH" + fr_FR: "Erreur d'Analyse de Clé SSH" + pl_PL: "Błąd Parsowania Klucza SSH" + +error.sound-error: + en_US: "Sound Interface Error" + de_DE: "Schnittstellen-Soundfehler" + es_ES: "Error de Interfaz de Sonido" + fr_FR: "Erreur d'Interface Audio" + pl_PL: "Błąd Interfejsu Dźwiękowego" + +error.parse-timestamp: + en_US: "Timestamp Parsing Error" + de_DE: "Fehler beim Analysieren des Zeitstempels" + es_ES: "Error al Analizar Marca de Tiempo" + fr_FR: "Erreur d'Analyse d'Horodatage" + pl_PL: "Błąd Parsowania Znacznika Czasu" + +error.parse-sys-info: + en_US: "System Info Parsing Error" + de_DE: "Fehler beim Analysieren der Systeminfo" + es_ES: "Error al Analizar Información del Sistema" + fr_FR: "Erreur d'Analyse des Infos Système" + pl_PL: "Błąd Parsowania Informacji o Systemie" + +error.wifi: + en_US: "WiFi Internal Error" + de_DE: "Interner WiFi-Fehler" + es_ES: "Error Interno de WiFi" + fr_FR: "Erreur Interne WiFi" + pl_PL: "Wewnętrzny Błąd WiFi" + +error.journald: + en_US: "Journald Error" + de_DE: "Journald-Fehler" + es_ES: "Error de Journald" + fr_FR: "Erreur Journald" + pl_PL: "Błąd Journald" + +error.disk-management: + en_US: "Disk Management Error" + de_DE: "Festplattenverwaltungsfehler" + es_ES: "Error de Gestión de Disco" + fr_FR: "Erreur de Gestion de Disque" + pl_PL: "Błąd Zarządzania Dyskiem" + +error.openssl: + en_US: "OpenSSL Internal Error" + de_DE: "Interner OpenSSL-Fehler" + es_ES: "Error Interno de OpenSSL" + fr_FR: "Erreur Interne OpenSSL" + pl_PL: "Wewnętrzny Błąd OpenSSL" + +error.password-hash-generation: + en_US: "Password Hash Generation Error" + de_DE: "Fehler bei der Passwort-Hash-Generierung" + es_ES: "Error de Generación de Hash de Contraseña" + fr_FR: "Erreur de Génération de Hash de Mot de Passe" + pl_PL: "Błąd Generowania Skrótu Hasła" + +error.diagnostic-mode: + en_US: "Server is in Diagnostic Mode" + de_DE: "Server Befindet Sich im Diagnosemodus" + es_ES: "El Servidor Está en Modo de Diagnóstico" + fr_FR: "Le Serveur Est en Mode Diagnostic" + pl_PL: "Serwer Jest w Trybie Diagnostycznym" + +error.parse-db-field: + en_US: "Database Field Parse Error" + de_DE: "Fehler beim Analysieren des Datenbankfelds" + es_ES: "Error al Analizar Campo de Base de Datos" + fr_FR: "Erreur d'Analyse de Champ de Base de Données" + pl_PL: "Błąd Parsowania Pola Bazy Danych" + +error.duplicate: + en_US: "Duplication Error" + de_DE: "Duplikatfehler" + es_ES: "Error de Duplicación" + fr_FR: "Erreur de Duplication" + pl_PL: "Błąd Duplikacji" + +error.multiple-errors: + en_US: "Multiple Errors" + de_DE: "Mehrere Fehler" + es_ES: "Múltiples Errores" + fr_FR: "Erreurs Multiples" + pl_PL: "Wiele Błędów" + +error.incoherent: + en_US: "Incoherent" + de_DE: "Inkohärent" + es_ES: "Incoherente" + fr_FR: "Incohérent" + pl_PL: "Niespójny" + +error.invalid-backup-target-id: + en_US: "Invalid Backup Target ID" + de_DE: "Ungültige Sicherungsziel-ID" + es_ES: "ID de Destino de Copia de Seguridad Inválido" + fr_FR: "ID de Cible de Sauvegarde Invalide" + pl_PL: "Nieprawidłowe ID Celu Kopii Zapasowej" + +error.product-key-mismatch: + en_US: "Incompatible Product Keys" + de_DE: "Inkompatible Produktschlüssel" + es_ES: "Claves de Producto Incompatibles" + fr_FR: "Clés de Produit Incompatibles" + pl_PL: "Niekompatybilne Klucze Produktu" + +error.lan-port-conflict: + en_US: "Incompatible LAN Port Configuration" + de_DE: "Inkompatible LAN-Port-Konfiguration" + es_ES: "Configuración de Puerto LAN Incompatible" + fr_FR: "Configuration de Port LAN Incompatible" + pl_PL: "Niekompatybilna Konfiguracja Portu LAN" + +error.javascript: + en_US: "Javascript Engine Error" + de_DE: "JavaScript-Engine-Fehler" + es_ES: "Error del Motor JavaScript" + fr_FR: "Erreur du Moteur JavaScript" + pl_PL: "Błąd Silnika JavaScript" + +error.pem: + en_US: "PEM Encoding Error" + de_DE: "PEM-Kodierungsfehler" + es_ES: "Error de Codificación PEM" + fr_FR: "Erreur d'Encodage PEM" + pl_PL: "Błąd Kodowania PEM" + +error.tls-init: + en_US: "TLS Backend Initialization Error" + de_DE: "TLS-Backend-Initialisierungsfehler" + es_ES: "Error de Inicialización del Backend TLS" + fr_FR: "Erreur d'Initialisation du Backend TLS" + pl_PL: "Błąd Inicjalizacji Backendu TLS" + +error.ascii: + en_US: "ASCII Parse Error" + de_DE: "ASCII-Analysefehler" + es_ES: "Error de Análisis ASCII" + fr_FR: "Erreur d'Analyse ASCII" + pl_PL: "Błąd Parsowania ASCII" + +error.missing-header: + en_US: "Missing Header" + de_DE: "Fehlender Header" + es_ES: "Encabezado Faltante" + fr_FR: "En-Tête Manquant" + pl_PL: "Brakujący Nagłówek" + +error.grub: + en_US: "Grub Error" + de_DE: "Grub-Fehler" + es_ES: "Error de Grub" + fr_FR: "Erreur Grub" + pl_PL: "Błąd Grub" + +error.systemd: + en_US: "Systemd Error" + de_DE: "Systemd-Fehler" + es_ES: "Error de Systemd" + fr_FR: "Erreur Systemd" + pl_PL: "Błąd Systemd" + +error.openssh: + en_US: "OpenSSH Error" + de_DE: "OpenSSH-Fehler" + es_ES: "Error de OpenSSH" + fr_FR: "Erreur OpenSSH" + pl_PL: "Błąd OpenSSH" + +error.zram: + en_US: "Zram Error" + de_DE: "Zram-Fehler" + es_ES: "Error de Zram" + fr_FR: "Erreur Zram" + pl_PL: "Błąd Zram" + +error.lshw: + en_US: "LSHW Error" + de_DE: "LSHW-Fehler" + es_ES: "Error de LSHW" + fr_FR: "Erreur LSHW" + pl_PL: "Błąd LSHW" + +error.cpu-settings: + en_US: "CPU Settings Error" + de_DE: "CPU-Einstellungsfehler" + es_ES: "Error de Configuración de CPU" + fr_FR: "Erreur de Paramètres CPU" + pl_PL: "Błąd Ustawień CPU" + +error.firmware: + en_US: "Firmware Error" + de_DE: "Firmware-Fehler" + es_ES: "Error de Firmware" + fr_FR: "Erreur de Firmware" + pl_PL: "Błąd Firmware" + +error.timeout: + en_US: "Timeout Error" + de_DE: "Zeitüberschreitungsfehler" + es_ES: "Error de Tiempo de Espera" + fr_FR: "Erreur de Délai d'Attente" + pl_PL: "Błąd Przekroczenia Czasu" + +error.lxc: + en_US: "LXC Error" + de_DE: "LXC-Fehler" + es_ES: "Error de LXC" + fr_FR: "Erreur LXC" + pl_PL: "Błąd LXC" + +error.cancelled: + en_US: "Cancelled" + de_DE: "Abgebrochen" + es_ES: "Cancelado" + fr_FR: "Annulé" + pl_PL: "Anulowano" + +error.git: + en_US: "Git Error" + de_DE: "Git-Fehler" + es_ES: "Error de Git" + fr_FR: "Erreur Git" + pl_PL: "Błąd Git" + +error.dbus: + en_US: "DBus Error" + de_DE: "DBus-Fehler" + es_ES: "Error de DBus" + fr_FR: "Erreur DBus" + pl_PL: "Błąd DBus" + +error.install-failed: + en_US: "Install Failed" + de_DE: "Installation Fehlgeschlagen" + es_ES: "Instalación Fallida" + fr_FR: "Installation Échouée" + pl_PL: "Instalacja Nie Powiodła Się" + +error.update-failed: + en_US: "Update Failed" + de_DE: "Aktualisierung Fehlgeschlagen" + es_ES: "Actualización Fallida" + fr_FR: "Mise à Jour Échouée" + pl_PL: "Aktualizacja Nie Powiodła Się" + +error.smtp: + en_US: "SMTP Error" + de_DE: "SMTP-Fehler" + es_ES: "Error de SMTP" + fr_FR: "Erreur SMTP" + pl_PL: "Błąd SMTP" + +error.set-sys-info: + en_US: "Error Setting System Info" + de_DE: "Fehler beim Setzen der Systeminfo" + es_ES: "Error al Establecer Información del Sistema" + fr_FR: "Erreur de Définition des Infos Système" + pl_PL: "Błąd Ustawiania Informacji o Systemie" + +# disk/main.rs +disk.main.disk-not-found: + en_US: "StartOS disk not found." + de_DE: "StartOS-Festplatte nicht gefunden." + es_ES: "Disco StartOS no encontrado." + fr_FR: "Disque StartOS non trouvé." + pl_PL: "Nie znaleziono dysku StartOS." + +disk.main.incorrect-disk: + en_US: "A StartOS disk was found, but it is not the correct disk for this device." + de_DE: "Eine StartOS-Festplatte wurde gefunden, aber es ist nicht die richtige Festplatte für dieses Gerät." + es_ES: "Se encontró un disco StartOS, pero no es el disco correcto para este dispositivo." + fr_FR: "Un disque StartOS a été trouvé, mais ce n'est pas le bon disque pour cet appareil." + pl_PL: "Znaleziono dysk StartOS, ale nie jest to właściwy dysk dla tego urządzenia." + +# disk/util.rs +disk.util.not-canonical-block-device: + en_US: "not a canonical block device" + de_DE: "kein kanonisches Blockgerät" + es_ES: "no es un dispositivo de bloque canónico" + fr_FR: "pas un périphérique bloc canonique" + pl_PL: "nie jest kanonicznym urządzeniem blokowym" + +disk.util.could-not-get-partition-table: + en_US: "Could not get partition table of %{disk}: %{error}" + de_DE: "Konnte Partitionstabelle von %{disk} nicht abrufen: %{error}" + es_ES: "No se pudo obtener la tabla de particiones de %{disk}: %{error}" + fr_FR: "Impossible d'obtenir la table de partitions de %{disk} : %{error}" + pl_PL: "Nie można pobrać tablicy partycji %{disk}: %{error}" + +disk.util.could-not-get-vendor: + en_US: "Could not get vendor of %{disk}: %{error}" + de_DE: "Konnte Hersteller von %{disk} nicht abrufen: %{error}" + es_ES: "No se pudo obtener el fabricante de %{disk}: %{error}" + fr_FR: "Impossible d'obtenir le fabricant de %{disk} : %{error}" + pl_PL: "Nie można pobrać producenta %{disk}: %{error}" + +disk.util.could-not-get-model: + en_US: "Could not get model of %{disk}: %{error}" + de_DE: "Konnte Modell von %{disk} nicht abrufen: %{error}" + es_ES: "No se pudo obtener el modelo de %{disk}: %{error}" + fr_FR: "Impossible d'obtenir le modèle de %{disk} : %{error}" + pl_PL: "Nie można pobrać modelu %{disk}: %{error}" + +disk.util.could-not-get-capacity: + en_US: "Could not get capacity of %{disk}: %{error}" + de_DE: "Konnte Kapazität von %{disk} nicht abrufen: %{error}" + es_ES: "No se pudo obtener la capacidad de %{disk}: %{error}" + fr_FR: "Impossible d'obtenir la capacité de %{disk} : %{error}" + pl_PL: "Nie można pobrać pojemności %{disk}: %{error}" + +disk.util.could-not-get-label: + en_US: "Could not get label of %{part}: %{error}" + de_DE: "Konnte Bezeichnung von %{part} nicht abrufen: %{error}" + es_ES: "No se pudo obtener la etiqueta de %{part}: %{error}" + fr_FR: "Impossible d'obtenir l'étiquette de %{part} : %{error}" + pl_PL: "Nie można pobrać etykiety %{part}: %{error}" + +disk.util.could-not-get-capacity-part: + en_US: "Could not get capacity of %{part}: %{error}" + de_DE: "Konnte Kapazität von %{part} nicht abrufen: %{error}" + es_ES: "No se pudo obtener la capacidad de %{part}: %{error}" + fr_FR: "Impossible d'obtenir la capacité de %{part} : %{error}" + pl_PL: "Nie można pobrać pojemności %{part}: %{error}" + +disk.util.could-not-collect-usage-info: + en_US: "Could not collect usage information: %{error}" + de_DE: "Konnte Nutzungsinformationen nicht sammeln: %{error}" + es_ES: "No se pudo recopilar información de uso: %{error}" + fr_FR: "Impossible de collecter les informations d'utilisation : %{error}" + pl_PL: "Nie można zebrać informacji o użyciu: %{error}" + +disk.util.could-not-get-usage: + en_US: "Could not get usage of %{part}: %{error}" + de_DE: "Konnte Nutzung von %{part} nicht abrufen: %{error}" + es_ES: "No se pudo obtener el uso de %{part}: %{error}" + fr_FR: "Impossible d'obtenir l'utilisation de %{part} : %{error}" + pl_PL: "Nie można pobrać użycia %{part}: %{error}" + +disk.util.error-fetching-backup-metadata: + en_US: "Error fetching unencrypted backup metadata: %{error}" + de_DE: "Fehler beim Abrufen der unverschlüsselten Sicherungsmetadaten: %{error}" + es_ES: "Error al obtener metadatos de copia de seguridad sin cifrar: %{error}" + fr_FR: "Erreur lors de la récupération des métadonnées de sauvegarde non chiffrées : %{error}" + pl_PL: "Błąd pobierania niezaszyfrowanych metadanych kopii zapasowej: %{error}" + +disk.util.error-unmounting-partition: + en_US: "Error unmounting partition %{part}: %{error}" + de_DE: "Fehler beim Aushängen der Partition %{part}: %{error}" + es_ES: "Error al desmontar la partición %{part}: %{error}" + fr_FR: "Erreur lors du démontage de la partition %{part} : %{error}" + pl_PL: "Błąd odmontowywania partycji %{part}: %{error}" + +disk.util.failed-to-parse-pvscan: + en_US: "Failed to parse pvscan output line: %{line}" + de_DE: "Fehler beim Analysieren der pvscan-Ausgabezeile: %{line}" + es_ES: "Error al analizar la línea de salida de pvscan: %{line}" + fr_FR: "Échec de l'analyse de la ligne de sortie pvscan : %{line}" + pl_PL: "Nie udało się przeanalizować linii wyjścia pvscan: %{line}" + +# disk/fsck/ext4.rs +disk.fsck.process-terminated-by-signal: + en_US: "e2fsck: process terminated by signal" + de_DE: "e2fsck: Prozess durch Signal beendet" + es_ES: "e2fsck: proceso terminado por señal" + fr_FR: "e2fsck : processus terminé par un signal" + pl_PL: "e2fsck: proces zakończony sygnałem" + +disk.fsck.errors-not-corrected: + en_US: "some filesystem errors NOT corrected on %{device}:\n%{stderr}" + de_DE: "einige Dateisystemfehler wurden NICHT auf %{device} korrigiert:\n%{stderr}" + es_ES: "algunos errores del sistema de archivos NO se corrigieron en %{device}:\n%{stderr}" + fr_FR: "certaines erreurs du système de fichiers NON corrigées sur %{device} :\n%{stderr}" + pl_PL: "niektóre błędy systemu plików NIE zostały naprawione na %{device}:\n%{stderr}" + +disk.fsck.errors-corrected: + en_US: "filesystem errors corrected on %{device}:\n%{stderr}" + de_DE: "Dateisystemfehler auf %{device} korrigiert:\n%{stderr}" + es_ES: "errores del sistema de archivos corregidos en %{device}:\n%{stderr}" + fr_FR: "erreurs du système de fichiers corrigées sur %{device} :\n%{stderr}" + pl_PL: "błędy systemu plików naprawione na %{device}:\n%{stderr}" + +disk.fsck.reboot-required: + en_US: "reboot required" + de_DE: "Neustart erforderlich" + es_ES: "reinicio requerido" + fr_FR: "redémarrage requis" + pl_PL: "wymagany restart" + +disk.fsck.e2fsck-error: + en_US: "e2fsck: %{stderr}" + de_DE: "e2fsck: %{stderr}" + es_ES: "e2fsck: %{stderr}" + fr_FR: "e2fsck : %{stderr}" + pl_PL: "e2fsck: %{stderr}" + +# disk/mount/util.rs +disk.mount.binding: + en_US: "Binding %{src} to %{dst}" + de_DE: "Binde %{src} an %{dst}" + es_ES: "Vinculando %{src} a %{dst}" + fr_FR: "Liaison de %{src} à %{dst}" + pl_PL: "Wiązanie %{src} do %{dst}" + +# init.rs +init.running-preinit: + en_US: "Running preinit.sh" + de_DE: "Führe preinit.sh aus" + es_ES: "Ejecutando preinit.sh" + fr_FR: "Exécution de preinit.sh" + pl_PL: "Uruchamianie preinit.sh" + +init.enabling-local-auth: + en_US: "Enabling local authentication" + de_DE: "Aktiviere lokale Authentifizierung" + es_ES: "Habilitando autenticación local" + fr_FR: "Activation de l'authentification locale" + pl_PL: "Włączanie lokalnego uwierzytelniania" + +init.loading-database: + en_US: "Loading database" + de_DE: "Lade Datenbank" + es_ES: "Cargando base de datos" + fr_FR: "Chargement de la base de données" + pl_PL: "Ładowanie bazy danych" + +init.loading-ssh-keys: + en_US: "Loading SSH Keys" + de_DE: "Lade SSH-Schlüssel" + es_ES: "Cargando claves SSH" + fr_FR: "Chargement des clés SSH" + pl_PL: "Ładowanie kluczy SSH" + +init.starting-network-controller: + en_US: "Starting network controller" + de_DE: "Starte Netzwerkcontroller" + es_ES: "Iniciando controlador de red" + fr_FR: "Démarrage du contrôleur réseau" + pl_PL: "Uruchamianie kontrolera sieci" + +init.switching-logs-to-data-drive: + en_US: "Switching logs to write to data drive" + de_DE: "Wechsle Protokolle auf Datenlaufwerk" + es_ES: "Cambiando registros para escribir en unidad de datos" + fr_FR: "Basculement des journaux vers le disque de données" + pl_PL: "Przełączanie logów na dysk danych" + +init.loading-ca-certificate: + en_US: "Loading CA certificate" + de_DE: "Lade CA-Zertifikat" + es_ES: "Cargando certificado CA" + fr_FR: "Chargement du certificat CA" + pl_PL: "Ładowanie certyfikatu CA" + +init.loading-wifi-configuration: + en_US: "Loading WiFi configuration" + de_DE: "Lade WiFi-Konfiguration" + es_ES: "Cargando configuración WiFi" + fr_FR: "Chargement de la configuration WiFi" + pl_PL: "Ładowanie konfiguracji WiFi" + +init.initializing-temporary-files: + en_US: "Initializing temporary files" + de_DE: "Initialisiere temporäre Dateien" + es_ES: "Inicializando archivos temporales" + fr_FR: "Initialisation des fichiers temporaires" + pl_PL: "Inicjalizowanie plików tymczasowych" + +init.setting-cpu-performance-profile: + en_US: "Setting CPU performance profile" + de_DE: "Setze CPU-Leistungsprofil" + es_ES: "Configurando perfil de rendimiento de CPU" + fr_FR: "Configuration du profil de performance CPU" + pl_PL: "Ustawianie profilu wydajności CPU" + +init.synchronizing-system-clock: + en_US: "Synchronizing system clock" + de_DE: "Synchronisiere Systemuhr" + es_ES: "Sincronizando reloj del sistema" + fr_FR: "Synchronisation de l'horloge système" + pl_PL: "Synchronizowanie zegara systemowego" + +init.enabling-zram: + en_US: "Enabling ZRAM" + de_DE: "Aktiviere ZRAM" + es_ES: "Habilitando ZRAM" + fr_FR: "Activation de ZRAM" + pl_PL: "Włączanie ZRAM" + +init.updating-server-info: + en_US: "Updating server info" + de_DE: "Aktualisiere Serverinformationen" + es_ES: "Actualizando información del servidor" + fr_FR: "Mise à jour des informations du serveur" + pl_PL: "Aktualizowanie informacji o serwerze" + +init.launching-service-intranet: + en_US: "Launching service intranet" + de_DE: "Starte Service-Intranet" + es_ES: "Iniciando intranet de servicios" + fr_FR: "Lancement de l'intranet de services" + pl_PL: "Uruchamianie intranetu usług" + +init.validating-database: + en_US: "Validating database" + de_DE: "Validiere Datenbank" + es_ES: "Validando base de datos" + fr_FR: "Validation de la base de données" + pl_PL: "Walidowanie bazy danych" + +init.running-postinit: + en_US: "Running postinit.sh" + de_DE: "Führe postinit.sh aus" + es_ES: "Ejecutando postinit.sh" + fr_FR: "Exécution de postinit.sh" + pl_PL: "Uruchamianie postinit.sh" + +init.error-running-script: + en_US: "Error Running %{script}: %{error}" + de_DE: "Fehler beim Ausführen von %{script}: %{error}" + es_ES: "Error al ejecutar %{script}: %{error}" + fr_FR: "Erreur lors de l'exécution de %{script} : %{error}" + pl_PL: "Błąd uruchamiania %{script}: %{error}" + +init.cpu-governor-not-available: + en_US: "CPU Governor \"%{governor}\" Not Available" + de_DE: "CPU-Governor \"%{governor}\" nicht verfügbar" + es_ES: "CPU Governor \"%{governor}\" no disponible" + fr_FR: "Gouverneur CPU \"%{governor}\" non disponible" + pl_PL: "Governor CPU \"%{governor}\" niedostępny" + +init.setting-cpu-governor: + en_US: "Setting CPU Governor to \"%{governor}\"" + de_DE: "Setze CPU-Governor auf \"%{governor}\"" + es_ES: "Configurando CPU Governor a \"%{governor}\"" + fr_FR: "Configuration du gouverneur CPU à \"%{governor}\"" + pl_PL: "Ustawianie governora CPU na \"%{governor}\"" + +init.clock-sync-timeout: + en_US: "Timed out waiting for system time to synchronize" + de_DE: "Zeitüberschreitung beim Warten auf Systemzeitsynchronisierung" + es_ES: "Tiempo de espera agotado esperando sincronización de hora del sistema" + fr_FR: "Délai d'attente dépassé pour la synchronisation de l'heure système" + pl_PL: "Przekroczono limit czasu oczekiwania na synchronizację czasu systemowego" + +init.enabled-zram: + en_US: "Enabled ZRAM" + de_DE: "ZRAM aktiviert" + es_ES: "ZRAM habilitado" + fr_FR: "ZRAM activé" + pl_PL: "Włączono ZRAM" + +init.system-initialized: + en_US: "System initialized." + de_DE: "System initialisiert." + es_ES: "Sistema inicializado." + fr_FR: "Système initialisé." + pl_PL: "System zainicjalizowany." + +init.error-closing-websocket: + en_US: "error closing init progress websocket: %{error}" + de_DE: "Fehler beim Schließen des Init-Fortschritts-WebSockets: %{error}" + es_ES: "error al cerrar websocket de progreso de inicio: %{error}" + fr_FR: "erreur lors de la fermeture du websocket de progression d'init : %{error}" + pl_PL: "błąd zamykania websocketu postępu inicjalizacji: %{error}" + +init.initializing: + en_US: "Initializing..." + de_DE: "Initialisiere..." + es_ES: "Inicializando..." + fr_FR: "Initialisation..." + pl_PL: "Inicjalizowanie..." + +# backup/backup_bulk.rs +backup.bulk.complete-title: + en_US: "Backup Complete" + de_DE: "Sicherung abgeschlossen" + es_ES: "Copia de seguridad completada" + fr_FR: "Sauvegarde terminée" + pl_PL: "Kopia zapasowa zakończona" + +backup.bulk.complete-message: + en_US: "Your backup has completed" + de_DE: "Ihre Sicherung wurde abgeschlossen" + es_ES: "Su copia de seguridad se ha completado" + fr_FR: "Votre sauvegarde est terminée" + pl_PL: "Twoja kopia zapasowa została zakończona" + +backup.bulk.complete-with-failures: + en_US: "Your backup has completed, but some package(s) failed to backup" + de_DE: "Ihre Sicherung wurde abgeschlossen, aber einige Pakete konnten nicht gesichert werden" + es_ES: "Su copia de seguridad se completó, pero algunos paquetes fallaron" + fr_FR: "Votre sauvegarde est terminée, mais certains paquets n'ont pas pu être sauvegardés" + pl_PL: "Twoja kopia zapasowa została zakończona, ale niektóre pakiety nie zostały zapisane" + +backup.bulk.failed-error: + en_US: "Backup Failed: %{error}" + de_DE: "Sicherung fehlgeschlagen: %{error}" + es_ES: "Copia de seguridad fallida: %{error}" + fr_FR: "Échec de la sauvegarde : %{error}" + pl_PL: "Kopia zapasowa nie powiodła się: %{error}" + +backup.bulk.failed-title: + en_US: "Backup Failed" + de_DE: "Sicherung fehlgeschlagen" + es_ES: "Copia de seguridad fallida" + fr_FR: "Échec de la sauvegarde" + pl_PL: "Kopia zapasowa nie powiodła się" + +backup.bulk.failed-message: + en_US: "Your backup failed to complete." + de_DE: "Ihre Sicherung konnte nicht abgeschlossen werden." + es_ES: "Su copia de seguridad no se pudo completar." + fr_FR: "Votre sauvegarde n'a pas pu être terminée." + pl_PL: "Twoja kopia zapasowa nie została ukończona." + +backup.bulk.already-backing-up: + en_US: "Server is already backing up!" + de_DE: "Server führt bereits eine Sicherung durch!" + es_ES: "¡El servidor ya está realizando una copia de seguridad!" + fr_FR: "Le serveur est déjà en cours de sauvegarde !" + pl_PL: "Serwer już wykonuje kopię zapasową!" + +backup.bulk.leaked-reference: + en_US: "leaked reference to BackupMountGuard" + de_DE: "Leckreferenz auf BackupMountGuard" + es_ES: "referencia filtrada a BackupMountGuard" + fr_FR: "référence fuitée vers BackupMountGuard" + pl_PL: "wyciekła referencja do BackupMountGuard" + +# backup/restore.rs +backup.restore.package-error: + en_US: "Error restoring package %{id}: %{error}" + de_DE: "Fehler beim Wiederherstellen des Pakets %{id}: %{error}" + es_ES: "Error al restaurar el paquete %{id}: %{error}" + fr_FR: "Erreur lors de la restauration du paquet %{id} : %{error}" + pl_PL: "Błąd przywracania pakietu %{id}: %{error}" + +# backup/target/cifs.rs +backup.target.cifs.target-not-found: + en_US: "Backup Target ID %{id} Not Found" + de_DE: "Sicherungsziel-ID %{id} nicht gefunden" + es_ES: "ID de destino de copia de seguridad %{id} no encontrado" + fr_FR: "ID de cible de sauvegarde %{id} non trouvé" + pl_PL: "Nie znaleziono ID celu kopii zapasowej %{id}" + +backup.target.cifs.target-not-found-id: + en_US: "Backup Target ID %{id} Not Found" + de_DE: "Sicherungsziel-ID %{id} nicht gefunden" + es_ES: "ID de destino de copia de seguridad %{id} no encontrado" + fr_FR: "ID de cible de sauvegarde %{id} non trouvé" + pl_PL: "Nie znaleziono ID celu kopii zapasowej %{id}" + +# net/ssl.rs +net.ssl.unreachable: + en_US: "unreachable" + de_DE: "unerreichbar" + es_ES: "inalcanzable" + fr_FR: "inaccessible" + pl_PL: "nieosiągalny" + +# net/tor/arti.rs +net.tor.invalid-ed25519-key: + en_US: "invalid ed25519 expanded secret key" + de_DE: "ungültiger erweiterter ed25519-Geheimschlüssel" + es_ES: "clave secreta ed25519 expandida inválida" + fr_FR: "clé secrète ed25519 étendue invalide" + pl_PL: "nieprawidłowy rozszerzony klucz tajny ed25519" + +net.tor.bootstrap-no-progress: + en_US: "Bootstrap has not made progress for %{duration}" + de_DE: "Bootstrap hat seit %{duration} keinen Fortschritt gemacht" + es_ES: "Bootstrap no ha progresado durante %{duration}" + fr_FR: "Bootstrap n'a pas progressé depuis %{duration}" + pl_PL: "Bootstrap nie poczynił postępów przez %{duration}" + +net.tor.bootstrap-error: + en_US: "Tor Bootstrap Error: %{error}" + de_DE: "Tor-Bootstrap-Fehler: %{error}" + es_ES: "Error de Bootstrap de Tor: %{error}" + fr_FR: "Erreur de Bootstrap Tor : %{error}" + pl_PL: "Błąd Bootstrap Tor: %{error}" + +net.tor.health-error: + en_US: "Tor Health Error: %{error}" + de_DE: "Tor-Gesundheitsfehler: %{error}" + es_ES: "Error de salud de Tor: %{error}" + fr_FR: "Erreur de santé Tor : %{error}" + pl_PL: "Błąd kondycji Tor: %{error}" + +net.tor.status-stream-ended: + en_US: "status event stream ended" + de_DE: "Status-Ereignisstrom beendet" + es_ES: "flujo de eventos de estado terminado" + fr_FR: "flux d'événements de statut terminé" + pl_PL: "strumień zdarzeń statusu zakończony" + +net.tor.client-health-error: + en_US: "Tor Client Health Error: %{error}" + de_DE: "Tor-Client-Gesundheitsfehler: %{error}" + es_ES: "Error de salud del cliente Tor: %{error}" + fr_FR: "Erreur de santé du client Tor : %{error}" + pl_PL: "Błąd kondycji klienta Tor: %{error}" + +net.tor.health-check-failed-recycling: + en_US: "Client failed health check %{count} times, recycling" + de_DE: "Client hat Gesundheitsprüfung %{count} Mal nicht bestanden, wird recycelt" + es_ES: "El cliente falló la verificación de salud %{count} veces, reciclando" + fr_FR: "Le client a échoué au bilan de santé %{count} fois, recyclage" + pl_PL: "Klient nie przeszedł sprawdzenia kondycji %{count} razy, ponowne uruchamianie" + +net.tor.bootstrapper-error: + en_US: "Tor Bootstrapper Error: %{error}" + de_DE: "Tor-Bootstrapper-Fehler: %{error}" + es_ES: "Error del Bootstrapper de Tor: %{error}" + fr_FR: "Erreur du Bootstrapper Tor : %{error}" + pl_PL: "Błąd Bootstrappera Tor: %{error}" + +net.tor.client-creation-error: + en_US: "Tor Client Creation Error: %{error}" + de_DE: "Fehler bei der Erstellung des Tor-Clients: %{error}" + es_ES: "Error de creación del cliente Tor: %{error}" + fr_FR: "Erreur de création du client Tor : %{error}" + pl_PL: "Błąd tworzenia klienta Tor: %{error}" + +net.tor.failed-to-set-tcp-keepalive: + en_US: "Failed to set tcp keepalive: %{error}" + de_DE: "Fehler beim Setzen von TCP-Keepalive: %{error}" + es_ES: "Error al configurar tcp keepalive: %{error}" + fr_FR: "Échec de la configuration de tcp keepalive : %{error}" + pl_PL: "Nie udało się ustawić tcp keepalive: %{error}" + +net.tor.client-error: + en_US: "Tor Client Error: %{error}" + de_DE: "Tor-Client-Fehler: %{error}" + es_ES: "Error del cliente Tor: %{error}" + fr_FR: "Erreur du client Tor : %{error}" + pl_PL: "Błąd klienta Tor: %{error}" + +# net/wifi.rs +net.wifi.ssid-no-special-characters: + en_US: "SSID may not have special characters" + de_DE: "SSID darf keine Sonderzeichen enthalten" + es_ES: "El SSID no puede tener caracteres especiales" + fr_FR: "Le SSID ne peut pas contenir de caractères spéciaux" + pl_PL: "SSID nie może zawierać znaków specjalnych" + +net.wifi.password-no-special-characters: + en_US: "WiFi Password may not have special characters" + de_DE: "WiFi-Passwort darf keine Sonderzeichen enthalten" + es_ES: "La contraseña WiFi no puede tener caracteres especiales" + fr_FR: "Le mot de passe WiFi ne peut pas contenir de caractères spéciaux" + pl_PL: "Hasło WiFi nie może zawierać znaków specjalnych" + +net.wifi.adding-network: + en_US: "Adding new WiFi network: '%{ssid}'" + de_DE: "Füge neues WiFi-Netzwerk hinzu: '%{ssid}'" + es_ES: "Añadiendo nueva red WiFi: '%{ssid}'" + fr_FR: "Ajout d'un nouveau réseau WiFi : '%{ssid}'" + pl_PL: "Dodawanie nowej sieci WiFi: '%{ssid}'" + +net.wifi.no-interface-available: + en_US: "No WiFi interface available" + de_DE: "Keine WiFi-Schnittstelle verfügbar" + es_ES: "No hay interfaz WiFi disponible" + fr_FR: "Aucune interface WiFi disponible" + pl_PL: "Brak dostępnego interfejsu WiFi" + +net.wifi.failed-to-add-network: + en_US: "Failed to add new WiFi network '%{ssid}': %{error}" + de_DE: "Fehler beim Hinzufügen des neuen WiFi-Netzwerks '%{ssid}': %{error}" + es_ES: "Error al añadir nueva red WiFi '%{ssid}': %{error}" + fr_FR: "Échec de l'ajout du nouveau réseau WiFi '%{ssid}' : %{error}" + pl_PL: "Nie udało się dodać nowej sieci WiFi '%{ssid}': %{error}" + +net.wifi.failed-adding: + en_US: "Failed adding %{ssid}" + de_DE: "Fehler beim Hinzufügen von %{ssid}" + es_ES: "Error al añadir %{ssid}" + fr_FR: "Échec de l'ajout de %{ssid}" + pl_PL: "Nie udało się dodać %{ssid}" + +net.wifi.connected-successfully: + en_US: "Successfully connected to WiFi: '%{ssid}'" + de_DE: "Erfolgreich mit WiFi verbunden: '%{ssid}'" + es_ES: "Conectado exitosamente a WiFi: '%{ssid}'" + fr_FR: "Connecté avec succès au WiFi : '%{ssid}'" + pl_PL: "Pomyślnie połączono z WiFi: '%{ssid}'" + +net.wifi.connection-failed: + en_US: "Failed to connect to WiFi: '%{ssid}'" + de_DE: "Verbindung zu WiFi fehlgeschlagen: '%{ssid}'" + es_ES: "Error al conectar a WiFi: '%{ssid}'" + fr_FR: "Échec de la connexion au WiFi : '%{ssid}'" + pl_PL: "Nie udało się połączyć z WiFi: '%{ssid}'" + +net.wifi.no-wifi-to-revert: + en_US: "No WiFi to revert to!" + de_DE: "Kein WiFi zum Zurücksetzen!" + es_ES: "¡No hay WiFi al que volver!" + fr_FR: "Aucun WiFi vers lequel revenir !" + pl_PL: "Brak WiFi do przywrócenia!" + +net.wifi.failed-to-connect: + en_US: "Failed to connect to WiFi network '%{ssid}': %{error}" + de_DE: "Verbindung zum WiFi-Netzwerk '%{ssid}' fehlgeschlagen: %{error}" + es_ES: "Error al conectar a la red WiFi '%{ssid}': %{error}" + fr_FR: "Échec de la connexion au réseau WiFi '%{ssid}' : %{error}" + pl_PL: "Nie udało się połączyć z siecią WiFi '%{ssid}': %{error}" + +net.wifi.cant-connect: + en_US: "Can't connect to %{ssid}" + de_DE: "Kann nicht mit %{ssid} verbinden" + es_ES: "No se puede conectar a %{ssid}" + fr_FR: "Impossible de se connecter à %{ssid}" + pl_PL: "Nie można połączyć się z %{ssid}" + +net.wifi.forbidden-delete-would-disconnect: + en_US: "Forbidden: Deleting this network would make your server unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this." + de_DE: "Verboten: Das Löschen dieses Netzwerks würde Ihren Server unerreichbar machen. Verbinden Sie sich mit Ethernet oder einem anderen WiFi-Netzwerk, um dies zu beheben." + es_ES: "Prohibido: Eliminar esta red haría que su servidor sea inalcanzable. Conéctese a ethernet o a una red WiFi diferente para solucionar esto." + fr_FR: "Interdit : Supprimer ce réseau rendrait votre serveur inaccessible. Connectez-vous à ethernet ou à un autre réseau WiFi pour remédier à cela." + pl_PL: "Zabronione: Usunięcie tej sieci uczyniłoby serwer nieosiągalnym. Podłącz się przez ethernet lub do innej sieci WiFi, aby to naprawić." + +net.wifi.wont-change-country-without-ethernet: + en_US: "Won't change country without hardwire connection" + de_DE: "Länderwechsel ohne Kabelverbindung nicht möglich" + es_ES: "No cambiará el país sin conexión por cable" + fr_FR: "Ne changera pas de pays sans connexion filaire" + pl_PL: "Nie zmieni kraju bez połączenia kablowego" + +net.wifi.failed-to-set-interface: + en_US: "Failed to set interface %{interface} for %{ssid}" + de_DE: "Fehler beim Setzen der Schnittstelle %{interface} für %{ssid}" + es_ES: "Error al configurar la interfaz %{interface} para %{ssid}" + fr_FR: "Échec de la configuration de l'interface %{interface} pour %{ssid}" + pl_PL: "Nie udało się ustawić interfejsu %{interface} dla %{ssid}" + +net.wifi.could-not-find-country-config: + en_US: "Could not find a country config lines" + de_DE: "Länder-Konfigurationszeilen nicht gefunden" + es_ES: "No se pudieron encontrar líneas de configuración de país" + fr_FR: "Impossible de trouver les lignes de configuration de pays" + pl_PL: "Nie można znaleźć linii konfiguracji kraju" + +net.wifi.could-not-parse-country-config: + en_US: "Could not find a country config with regex" + de_DE: "Länder-Konfiguration mit Regex nicht gefunden" + es_ES: "No se pudo encontrar una configuración de país con regex" + fr_FR: "Impossible de trouver une configuration de pays avec regex" + pl_PL: "Nie można znaleźć konfiguracji kraju za pomocą regex" + +net.wifi.invalid-country-code: + en_US: "Invalid Country Code: %{country}" + de_DE: "Ungültiger Ländercode: %{country}" + es_ES: "Código de país inválido: %{country}" + fr_FR: "Code de pays invalide : %{country}" + pl_PL: "Nieprawidłowy kod kraju: %{country}" + +net.wifi.ssid-not-found: + en_US: "SSID Not Found" + de_DE: "SSID nicht gefunden" + es_ES: "SSID no encontrado" + fr_FR: "SSID non trouvé" + pl_PL: "Nie znaleziono SSID" + +net.wifi.setting-region: + en_US: "Setting the region" + de_DE: "Region wird gesetzt" + es_ES: "Configurando la región" + fr_FR: "Configuration de la région" + pl_PL: "Ustawianie regionu" + +net.wifi.setting-region-fallback: + en_US: "Setting the region fallback" + de_DE: "Regions-Fallback wird gesetzt" + es_ES: "Configurando la región de respaldo" + fr_FR: "Configuration de la région de secours" + pl_PL: "Ustawianie regionu awaryjnego" + +# net/forward.rs +net.forward.no-dynamic-ports-available: + en_US: "No more dynamic ports available!" + de_DE: "Keine dynamischen Ports mehr verfügbar!" + es_ES: "¡No hay más puertos dinámicos disponibles!" + fr_FR: "Plus de ports dynamiques disponibles !" + pl_PL: "Brak dostępnych portów dynamicznych!" + +net.forward.error-initializing-controller: + en_US: "error initializing PortForwardController: %{error}" + de_DE: "Fehler bei der Initialisierung des PortForwardControllers: %{error}" + es_ES: "error al inicializar PortForwardController: %{error}" + fr_FR: "erreur lors de l'initialisation de PortForwardController : %{error}" + pl_PL: "błąd inicjalizacji PortForwardController: %{error}" + +net.forward.mismatched-external-port: + en_US: "Mismatched external port in InterfaceForwardEntry" + de_DE: "Nicht übereinstimmender externer Port in InterfaceForwardEntry" + es_ES: "Puerto externo no coincide en InterfaceForwardEntry" + fr_FR: "Port externe incompatible dans InterfaceForwardEntry" + pl_PL: "Niezgodny port zewnętrzny w InterfaceForwardEntry" + +net.forward.controller-thread-exited: + en_US: "PortForwardController thread has exited" + de_DE: "PortForwardController-Thread wurde beendet" + es_ES: "El hilo PortForwardController ha terminado" + fr_FR: "Le thread PortForwardController s'est terminé" + pl_PL: "Wątek PortForwardController zakończył się" + +# net/gateway.rs +net.gateway.no-devices-returned: + en_US: "NetworkManager returned no devices. Trying again..." + de_DE: "NetworkManager hat keine Geräte zurückgegeben. Versuche erneut..." + es_ES: "NetworkManager no devolvió dispositivos. Intentando de nuevo..." + fr_FR: "NetworkManager n'a renvoyé aucun appareil. Nouvelle tentative..." + pl_PL: "NetworkManager nie zwrócił żadnych urządzeń. Ponowna próba..." + +net.gateway.failed-to-determine-wan-ip: + en_US: "Failed to determine WAN IP for %{iface}: %{error}" + de_DE: "WAN-IP für %{iface} konnte nicht ermittelt werden: %{error}" + es_ES: "Error al determinar IP WAN para %{iface}: %{error}" + fr_FR: "Échec de la détermination de l'IP WAN pour %{iface} : %{error}" + pl_PL: "Nie udało się określić IP WAN dla %{iface}: %{error}" + +net.gateway.error-loading-interface-info: + en_US: "Error loading network interface info: %{error}" + de_DE: "Fehler beim Laden der Netzwerkschnittstelleninformationen: %{error}" + es_ES: "Error al cargar información de interfaz de red: %{error}" + fr_FR: "Erreur lors du chargement des infos d'interface réseau : %{error}" + pl_PL: "Błąd ładowania informacji o interfejsie sieciowym: %{error}" + +net.gateway.error-syncing-ip-info: + en_US: "Error syncing ip info to db: %{error}" + de_DE: "Fehler bei der Synchronisierung der IP-Informationen zur Datenbank: %{error}" + es_ES: "Error al sincronizar info de IP a la base de datos: %{error}" + fr_FR: "Erreur de synchronisation des infos IP vers la BDD : %{error}" + pl_PL: "Błąd synchronizacji informacji IP z bazą danych: %{error}" + +net.gateway.cannot-forget-connected-interface: + en_US: "Cannot forget currently connected interface" + de_DE: "Aktuell verbundene Schnittstelle kann nicht vergessen werden" + es_ES: "No se puede olvidar la interfaz actualmente conectada" + fr_FR: "Impossible d'oublier l'interface actuellement connectée" + pl_PL: "Nie można zapomnieć aktualnie podłączonego interfejsu" + +net.gateway.cannot-delete-without-connection: + en_US: "Cannot delete device without active connection" + de_DE: "Gerät kann nicht ohne aktive Verbindung gelöscht werden" + es_ES: "No se puede eliminar el dispositivo sin conexión activa" + fr_FR: "Impossible de supprimer l'appareil sans connexion active" + pl_PL: "Nie można usunąć urządzenia bez aktywnego połączenia" + +# net/dns.rs +net.dns.timeout-updating-catalog: + en_US: "timed out waiting to update dns catalog" + de_DE: "Zeitüberschreitung beim Warten auf Aktualisierung des DNS-Katalogs" + es_ES: "tiempo de espera agotado esperando actualizar catálogo DNS" + fr_FR: "délai d'attente dépassé pour la mise à jour du catalogue DNS" + pl_PL: "przekroczono limit czasu oczekiwania na aktualizację katalogu DNS" + +net.dns.could-not-determine-source-interface: + en_US: "Could not determine source interface of %{src}" + de_DE: "Quellschnittstelle von %{src} konnte nicht ermittelt werden" + es_ES: "No se pudo determinar la interfaz de origen de %{src}" + fr_FR: "Impossible de déterminer l'interface source de %{src}" + pl_PL: "Nie można określić interfejsu źródłowego %{src}" + +net.dns.error-resolving-internal: + en_US: "Error resolving internal DNS: %{error}" + de_DE: "Fehler bei der Auflösung des internen DNS: %{error}" + es_ES: "Error al resolver DNS interno: %{error}" + fr_FR: "Erreur de résolution DNS interne : %{error}" + pl_PL: "Błąd rozwiązywania wewnętrznego DNS: %{error}" + +net.dns.server-thread-exited: + en_US: "DNS Server Thread has exited" + de_DE: "DNS-Server-Thread wurde beendet" + es_ES: "El hilo del servidor DNS ha terminado" + fr_FR: "Le thread du serveur DNS s'est terminé" + pl_PL: "Wątek serwera DNS zakończył się" + +# lxc/mod.rs +lxc.mod.rootfs-not-empty: + en_US: "rootfs is not empty, refusing to delete" + de_DE: "rootfs ist nicht leer, Löschung verweigert" + es_ES: "rootfs no está vacío, se rechaza eliminar" + fr_FR: "rootfs n'est pas vide, suppression refusée" + pl_PL: "rootfs nie jest pusty, odmowa usunięcia" + +lxc.mod.dhcp-timeout: + en_US: "Timed out waiting for container to acquire DHCP lease" + de_DE: "Zeitüberschreitung beim Warten auf DHCP-Lease für Container" + es_ES: "Tiempo de espera agotado esperando que el contenedor adquiera lease DHCP" + fr_FR: "Délai d'attente dépassé pour l'acquisition du bail DHCP par le conteneur" + pl_PL: "Przekroczono limit czasu oczekiwania na uzyskanie dzierżawy DHCP przez kontener" + +lxc.mod.command-failed: + en_US: "Command failed with exit code: %{code}\nMessage: %{message}" + de_DE: "Befehl mit Exit-Code fehlgeschlagen: %{code}\nNachricht: %{message}" + es_ES: "Comando falló con código de salida: %{code}\nMensaje: %{message}" + fr_FR: "La commande a échoué avec le code de sortie : %{code}\nMessage : %{message}" + pl_PL: "Polecenie nie powiodło się z kodem wyjścia: %{code}\nKomunikat: %{message}" + +lxc.mod.socket-timeout: + en_US: "timed out waiting for socket" + de_DE: "Zeitüberschreitung beim Warten auf Socket" + es_ES: "tiempo de espera agotado esperando socket" + fr_FR: "délai d'attente dépassé pour le socket" + pl_PL: "przekroczono limit czasu oczekiwania na gniazdo" + +lxc.mod.connected-to-socket: + en_US: "Connected to socket in %{elapsed}" + de_DE: "Mit Socket verbunden in %{elapsed}" + es_ES: "Conectado al socket en %{elapsed}" + fr_FR: "Connecté au socket en %{elapsed}" + pl_PL: "Połączono z gniazdem w %{elapsed}" + +lxc.mod.container-ungracefully-dropped: + en_US: "Container %{container} was ungracefully dropped. Cleaning up dangling containers..." + de_DE: "Container %{container} wurde unsauber beendet. Bereinige hängende Container..." + es_ES: "El contenedor %{container} fue eliminado abruptamente. Limpiando contenedores colgantes..." + fr_FR: "Le conteneur %{container} a été abandonné brutalement. Nettoyage des conteneurs orphelins..." + pl_PL: "Kontener %{container} został nieprawidłowo usunięty. Czyszczenie wiszących kontenerów..." + +lxc.mod.error-reading-crashed-logs: + en_US: "Error reading logs from crashed container: %{error}" + de_DE: "Fehler beim Lesen der Protokolle vom abgestürzten Container: %{error}" + es_ES: "Error al leer registros del contenedor caído: %{error}" + fr_FR: "Erreur de lecture des journaux du conteneur planté : %{error}" + pl_PL: "Błąd odczytu logów z uszkodzonego kontenera: %{error}" + +lxc.mod.error-cleaning-up-containers: + en_US: "Error cleaning up dangling LXC containers: %{error}" + de_DE: "Fehler bei der Bereinigung hängender LXC-Container: %{error}" + es_ES: "Error al limpiar contenedores LXC colgantes: %{error}" + fr_FR: "Erreur lors du nettoyage des conteneurs LXC orphelins : %{error}" + pl_PL: "Błąd czyszczenia wiszących kontenerów LXC: %{error}" + +lxc.mod.cleaned-up-containers: + en_US: "Successfully cleaned up dangling LXC containers" + de_DE: "Hängende LXC-Container erfolgreich bereinigt" + es_ES: "Contenedores LXC colgantes limpiados exitosamente" + fr_FR: "Conteneurs LXC orphelins nettoyés avec succès" + pl_PL: "Pomyślnie wyczyszczono wiszące kontenery LXC" + +# registry/admin.rs +registry.admin.unknown-signer: + en_US: "Unknown signer" + de_DE: "Unbekannter Unterzeichner" + es_ES: "Firmante desconocido" + fr_FR: "Signataire inconnu" + pl_PL: "Nieznany sygnatariusz" + +registry.admin.signer-already-exists: + en_US: "A signer %{guid} (%{name}) already exists with a matching key" + de_DE: "Ein Unterzeichner %{guid} (%{name}) existiert bereits mit einem passenden Schlüssel" + es_ES: "Un firmante %{guid} (%{name}) ya existe con una clave coincidente" + fr_FR: "Un signataire %{guid} (%{name}) existe déjà avec une clé correspondante" + pl_PL: "Sygnatariusz %{guid} (%{name}) już istnieje z pasującym kluczem" + +# registry/signer.rs +registry.signer.not-accepted: + en_US: "Signer(s) not accepted" + de_DE: "Unterzeichner nicht akzeptiert" + es_ES: "Firmante(s) no aceptado(s)" + fr_FR: "Signataire(s) non accepté(s)" + pl_PL: "Sygnatariusz(e) nieakceptowany(i)" + +# registry/asset.rs +registry.asset.failed-to-load-http-url: + en_US: "Failed to load any HTTP URL" + de_DE: "Fehler beim Laden einer HTTP-URL" + es_ES: "Error al cargar cualquier URL HTTP" + fr_FR: "Échec du chargement de toute URL HTTP" + pl_PL: "Nie udało się załadować żadnego adresu HTTP" + +# registry/context.rs +registry.context.missing-hostname: + en_US: "Missing required configuration: registry-hostname" + de_DE: "Fehlende erforderliche Konfiguration: registry-hostname" + es_ES: "Falta configuración requerida: registry-hostname" + fr_FR: "Configuration requise manquante : registry-hostname" + pl_PL: "Brak wymaganej konfiguracji: registry-hostname" + +registry.context.registry-required: + en_US: "`--registry` required" + de_DE: "`--registry` erforderlich" + es_ES: "`--registry` requerido" + fr_FR: "`--registry` requis" + pl_PL: "Wymagany `--registry`" + +registry.context.unauthorized: + en_US: "Unauthorized" + de_DE: "Nicht autorisiert" + es_ES: "No autorizado" + fr_FR: "Non autorisé" + pl_PL: "Brak autoryzacji" + +# registry/os/asset/add.rs +registry.os.asset.commitment-mismatch: + en_US: "Commitment does not match" + de_DE: "Commitment stimmt nicht überein" + es_ES: "El compromiso no coincide" + fr_FR: "L'engagement ne correspond pas" + pl_PL: "Zobowiązanie nie pasuje" + +registry.os.asset.unauthorized: + en_US: "Unauthorized" + de_DE: "Nicht autorisiert" + es_ES: "No autorizado" + fr_FR: "Non autorisé" + pl_PL: "Brak autoryzacji" + +registry.os.asset.unknown-extension: + en_US: "Unknown extension" + de_DE: "Unbekannte Erweiterung" + es_ES: "Extensión desconocida" + fr_FR: "Extension inconnue" + pl_PL: "Nieznane rozszerzenie" + +registry.os.asset.failed-read-metadata: + en_US: "Failed to read file metadata" + de_DE: "Fehler beim Lesen der Dateimetadaten" + es_ES: "Error al leer los metadatos del archivo" + fr_FR: "Échec de la lecture des métadonnées du fichier" + pl_PL: "Nie udało się odczytać metadanych pliku" + +registry.os.asset.signer-not-authorized: + en_US: "Signer %{guid} is not authorized" + de_DE: "Unterzeichner %{guid} ist nicht autorisiert" + es_ES: "El firmante %{guid} no está autorizado" + fr_FR: "Le signataire %{guid} n'est pas autorisé" + pl_PL: "Sygnatariusz %{guid} nie jest autoryzowany" + +# registry/os/version/signer.rs +registry.os.version.signer-not-authorized: + en_US: "Signer %{signer} is not authorized to sign for v%{version}" + de_DE: "Unterzeichner %{signer} ist nicht autorisiert, für v%{version} zu signieren" + es_ES: "El firmante %{signer} no está autorizado para firmar v%{version}" + fr_FR: "Le signataire %{signer} n'est pas autorisé à signer pour v%{version}" + pl_PL: "Sygnatariusz %{signer} nie jest autoryzowany do podpisywania v%{version}" + +# registry/package/mod.rs +registry.package.remove-not-exist: + en_US: "%{id}@%{version}%{sighash} does not exist, so not removed" + de_DE: "%{id}@%{version}%{sighash} existiert nicht, daher nicht entfernt" + es_ES: "%{id}@%{version}%{sighash} no existe, por lo que no se eliminó" + fr_FR: "%{id}@%{version}%{sighash} n'existe pas, donc non supprimé" + pl_PL: "%{id}@%{version}%{sighash} nie istnieje, więc nie usunięto" + +# registry/package/add.rs +registry.package.add.must-specify-url: + en_US: "Must specify at least 1 URL" + de_DE: "Mindestens 1 URL muss angegeben werden" + es_ES: "Debe especificar al menos 1 URL" + fr_FR: "Doit spécifier au moins 1 URL" + pl_PL: "Należy podać co najmniej 1 adres URL" + +registry.package.add.unauthorized: + en_US: "Unauthorized" + de_DE: "Nicht autorisiert" + es_ES: "No autorizado" + fr_FR: "Non autorisé" + pl_PL: "Brak autoryzacji" + +registry.package.missing-signer: + en_US: "Missing signer" + de_DE: "Fehlender Unterzeichner" + es_ES: "Falta firmante" + fr_FR: "Signataire manquant" + pl_PL: "Brak sygnatariusza" + +registry.package.unauthorized: + en_US: "Unauthorized" + de_DE: "Nicht autorisiert" + es_ES: "No autorizado" + fr_FR: "Non autorisé" + pl_PL: "Brak autoryzacji" + +registry.package.add-mirror.unauthorized: + en_US: "Unauthorized" + de_DE: "Nicht autorisiert" + es_ES: "No autorizado" + fr_FR: "Non autorisé" + pl_PL: "Brak autoryzacji" + +registry.package.cannot-remove-last-mirror: + en_US: "Cannot remove last mirror from an s9pk" + de_DE: "Letzter Spiegel kann nicht aus einem s9pk entfernt werden" + es_ES: "No se puede eliminar el último espejo de un s9pk" + fr_FR: "Impossible de supprimer le dernier miroir d'un s9pk" + pl_PL: "Nie można usunąć ostatniego serwera lustrzanego z s9pk" + +registry.package.remove-mirror.unauthorized: + en_US: "Unauthorized" + de_DE: "Nicht autorisiert" + es_ES: "No autorizado" + fr_FR: "Non autorisé" + pl_PL: "Brak autoryzacji" + +# registry/package/get.rs +registry.package.get.version-not-found: + en_US: "Could not find a version of %{id} that satisfies %{version}" + de_DE: "Keine Version von %{id} gefunden, die %{version} erfüllt" + es_ES: "No se pudo encontrar una versión de %{id} que satisfaga %{version}" + fr_FR: "Impossible de trouver une version de %{id} qui satisfait %{version}" + pl_PL: "Nie można znaleźć wersji %{id} spełniającej %{version}" + +registry.package.get.download-complete: + en_US: "Download Complete" + de_DE: "Download abgeschlossen" + es_ES: "Descarga completada" + fr_FR: "Téléchargement terminé" + pl_PL: "Pobieranie zakończone" + +# registry/package/signer.rs +registry.package.signer.not-authorized: + en_US: "Signer %{signer} is not authorized to sign for %{id}" + de_DE: "Unterzeichner %{signer} ist nicht autorisiert, für %{id} zu signieren" + es_ES: "El firmante %{signer} no está autorizado para firmar %{id}" + fr_FR: "Le signataire %{signer} n'est pas autorisé à signer pour %{id}" + pl_PL: "Sygnatariusz %{signer} nie jest autoryzowany do podpisywania %{id}" + +# service/mod.rs +service.mod.service-actor-held-after-shutdown: + en_US: "ServiceActor held somewhere after actor shutdown" + de_DE: "ServiceActor wird nach dem Herunterfahren des Actors noch irgendwo gehalten" + es_ES: "ServiceActor retenido en algún lugar después del cierre del actor" + fr_FR: "ServiceActor détenu quelque part après l'arrêt de l'acteur" + pl_PL: "ServiceActor przetrzymywany gdzieś po zamknięciu aktora" + +service.mod.service-actor-seed-held-after-shutdown: + en_US: "ServiceActorSeed held somewhere after actor shutdown" + de_DE: "ServiceActorSeed wird nach dem Herunterfahren des Actors noch irgendwo gehalten" + es_ES: "ServiceActorSeed retenido en algún lugar después del cierre del actor" + fr_FR: "ServiceActorSeed détenu quelque part après l'arrêt de l'acteur" + pl_PL: "ServiceActorSeed przetrzymywany gdzieś po zamknięciu aktora" + +service.mod.race-condition-detected: + en_US: "Race condition detected - package state changed during load" + de_DE: "Race Condition erkannt - Paketstatus hat sich während des Ladens geändert" + es_ES: "Condición de carrera detectada - el estado del paquete cambió durante la carga" + fr_FR: "Condition de concurrence détectée - l'état du paquet a changé pendant le chargement" + pl_PL: "Wykryto wyścig - stan pakietu zmienił się podczas ładowania" + +service.mod.failed-to-parse-package-data-entry: + en_US: "Failed to parse PackageDataEntry, found %{error}" + de_DE: "Fehler beim Parsen von PackageDataEntry, gefunden: %{error}" + es_ES: "Error al analizar PackageDataEntry, encontrado: %{error}" + fr_FR: "Échec de l'analyse de PackageDataEntry, trouvé : %{error}" + pl_PL: "Nie udało się przeanalizować PackageDataEntry, znaleziono: %{error}" + +service.mod.no-matching-subcontainers: + en_US: "no matching subcontainers are running for %{id}; some possible choices are:\n%{subcontainers}" + de_DE: "keine passenden Subcontainer laufen für %{id}; einige mögliche Optionen sind:\n%{subcontainers}" + es_ES: "no hay subcontenedores coincidentes ejecutándose para %{id}; algunas opciones posibles son:\n%{subcontainers}" + fr_FR: "aucun sous-conteneur correspondant n'est en cours d'exécution pour %{id} ; voici quelques choix possibles :\n%{subcontainers}" + pl_PL: "nie działają pasujące podkontenery dla %{id}; niektóre możliwe wybory to:\n%{subcontainers}" + +service.mod.multiple-subcontainers-found: + en_US: "multiple subcontainers found for %{id}: \n%{subcontainer_ids}" + de_DE: "mehrere Subcontainer für %{id} gefunden: \n%{subcontainer_ids}" + es_ES: "se encontraron múltiples subcontenedores para %{id}: \n%{subcontainer_ids}" + fr_FR: "plusieurs sous-conteneurs trouvés pour %{id} : \n%{subcontainer_ids}" + pl_PL: "znaleziono wiele podkontenerów dla %{id}: \n%{subcontainer_ids}" + +service.mod.invalid-byte-length-for-signal: + en_US: "invalid byte length for signal: %{length}" + de_DE: "ungültige Bytelänge für Signal: %{length}" + es_ES: "longitud de bytes inválida para señal: %{length}" + fr_FR: "longueur d'octets invalide pour le signal : %{length}" + pl_PL: "nieprawidłowa długość bajtów dla sygnału: %{length}" + +service.mod.could-not-parse-etc-passwd: + en_US: "Could not parse /etc/passwd for shell: %{contents}" + de_DE: "Konnte /etc/passwd für Shell nicht parsen: %{contents}" + es_ES: "No se pudo analizar /etc/passwd para shell: %{contents}" + fr_FR: "Impossible d'analyser /etc/passwd pour le shell : %{contents}" + pl_PL: "Nie można przeanalizować /etc/passwd dla powłoki: %{contents}" + +service.mod.could-not-get-etc-passwd: + en_US: "Could not get the /etc/passwd: %{error}" + de_DE: "Konnte /etc/passwd nicht abrufen: %{error}" + es_ES: "No se pudo obtener /etc/passwd: %{error}" + fr_FR: "Impossible d'obtenir /etc/passwd : %{error}" + pl_PL: "Nie można uzyskać /etc/passwd: %{error}" + +service.mod.invalid-byte-length-for-exit-code: + en_US: "invalid byte length for exit code: %{length}" + de_DE: "ungültige Bytelänge für Exit-Code: %{length}" + es_ES: "longitud de bytes inválida para código de salida: %{length}" + fr_FR: "longueur d'octets invalide pour le code de sortie : %{length}" + pl_PL: "nieprawidłowa długość bajtów dla kodu wyjścia: %{length}" + +service.mod.deleting-task-action-no-longer-exists: + en_US: "Deleting task %{id} because action no longer exists" + de_DE: "Lösche Aufgabe %{id}, da die Aktion nicht mehr existiert" + es_ES: "Eliminando tarea %{id} porque la acción ya no existe" + fr_FR: "Suppression de la tâche %{id} car l'action n'existe plus" + pl_PL: "Usuwanie zadania %{id}, ponieważ akcja już nie istnieje" + +# service/action.rs +service.action.action-request-invalid-state: + en_US: "action request exists in an invalid state %{task}" + de_DE: "Aktionsanfrage existiert in ungültigem Zustand %{task}" + es_ES: "la solicitud de acción existe en un estado inválido %{task}" + fr_FR: "la demande d'action existe dans un état invalide %{task}" + pl_PL: "żądanie akcji istnieje w nieprawidłowym stanie %{task}" + +service.action.action-is-disabled: + en_US: "action %{action_id} is disabled" + de_DE: "Aktion %{action_id} ist deaktiviert" + es_ES: "la acción %{action_id} está deshabilitada" + fr_FR: "l'action %{action_id} est désactivée" + pl_PL: "akcja %{action_id} jest wyłączona" + +service.action.service-not-in-allowed-status: + en_US: "service is not in allowed status for %{action_id}" + de_DE: "Dienst ist nicht im erlaubten Status für %{action_id}" + es_ES: "el servicio no está en un estado permitido para %{action_id}" + fr_FR: "le service n'est pas dans un état autorisé pour %{action_id}" + pl_PL: "usługa nie jest w dozwolonym stanie dla %{action_id}" + +# service/effects/action.rs +service.effects.action.calling-actions-on-other-packages-unsupported: + en_US: "calling actions on other packages is unsupported at this time" + de_DE: "Aufrufen von Aktionen auf anderen Paketen wird derzeit nicht unterstützt" + es_ES: "llamar acciones en otros paquetes no está soportado en este momento" + fr_FR: "l'appel d'actions sur d'autres paquets n'est pas pris en charge pour le moment" + pl_PL: "wywoływanie akcji na innych pakietach nie jest obecnie obsługiwane" + +service.effects.action.input-not-matches-requires-input: + en_US: "input-not-matches trigger requires input to be specified" + de_DE: "input-not-matches Trigger erfordert Eingabe" + es_ES: "el disparador input-not-matches requiere que se especifique la entrada" + fr_FR: "le déclencheur input-not-matches nécessite une entrée spécifiée" + pl_PL: "wyzwalacz input-not-matches wymaga określenia wejścia" + +service.effects.action.action-has-no-input: + en_US: "action %{action_id} of %{package_id} has no input" + de_DE: "Aktion %{action_id} von %{package_id} hat keine Eingabe" + es_ES: "la acción %{action_id} de %{package_id} no tiene entrada" + fr_FR: "l'action %{action_id} de %{package_id} n'a pas d'entrée" + pl_PL: "akcja %{action_id} pakietu %{package_id} nie ma wejścia" + +# service/effects/dependency.rs +service.effects.dependency.unknown-dependency-kind: + en_US: "unknown dependency kind %{kind}" + de_DE: "unbekannter Abhängigkeitstyp %{kind}" + es_ES: "tipo de dependencia desconocido %{kind}" + fr_FR: "type de dépendance inconnu %{kind}" + pl_PL: "nieznany rodzaj zależności %{kind}" + +# service/service_actor.rs +service.service-actor.error-synchronizing-state: + en_US: "error synchronizing state of service: %{error}" + de_DE: "Fehler beim Synchronisieren des Dienststatus: %{error}" + es_ES: "error al sincronizar el estado del servicio: %{error}" + fr_FR: "erreur lors de la synchronisation de l'état du service : %{error}" + pl_PL: "błąd synchronizacji stanu usługi: %{error}" + +service.service-actor.retrying-in-seconds: + en_US: "Retrying in %{seconds}s..." + de_DE: "Wiederholung in %{seconds}s..." + es_ES: "Reintentando en %{seconds}s..." + fr_FR: "Nouvelle tentative dans %{seconds}s..." + pl_PL: "Ponowna próba za %{seconds}s..." + +# service/persistent_container.rs +service.persistent-container.container-destroyed: + en_US: "PersistentContainer has been destroyed" + de_DE: "PersistentContainer wurde zerstört" + es_ES: "PersistentContainer ha sido destruido" + fr_FR: "PersistentContainer a été détruit" + pl_PL: "PersistentContainer został zniszczony" + +service.persistent-container.error-on-unix-socket: + en_US: "error on unix socket %{path}: %{error}" + de_DE: "Fehler auf Unix-Socket %{path}: %{error}" + es_ES: "error en socket unix %{path}: %{error}" + fr_FR: "erreur sur le socket unix %{path} : %{error}" + pl_PL: "błąd na gnieździe unix %{path}: %{error}" + +service.persistent-container.unix-socket-server-panicked: + en_US: "unix socket server thread panicked" + de_DE: "Unix-Socket-Server-Thread ist abgestürzt" + es_ES: "el hilo del servidor de socket unix entró en pánico" + fr_FR: "le thread du serveur de socket unix a paniqué" + pl_PL: "wątek serwera gniazda unix spanikował" + +service.persistent-container.already-initialized: + en_US: "PersistentContainer already initialized" + de_DE: "PersistentContainer bereits initialisiert" + es_ES: "PersistentContainer ya inicializado" + fr_FR: "PersistentContainer déjà initialisé" + pl_PL: "PersistentContainer już zainicjalizowany" + +service.persistent-container.service-exited: + en_US: "Service for %{id} exited" + de_DE: "Dienst für %{id} beendet" + es_ES: "El servicio para %{id} terminó" + fr_FR: "Le service pour %{id} s'est terminé" + pl_PL: "Usługa dla %{id} zakończyła działanie" + +# service/uninstall.rs +service.uninstall.invalid-package-state-for-cleanup: + en_US: "Invalid package state for cleanup: %{state}" + de_DE: "Ungültiger Paketstatus für Bereinigung: %{state}" + es_ES: "Estado de paquete inválido para limpieza: %{state}" + fr_FR: "État du paquet invalide pour le nettoyage : %{state}" + pl_PL: "Nieprawidłowy stan pakietu do czyszczenia: %{state}" + +# service/transition/backup.rs +service.transition.backup.no-backup-to-resume: + en_US: "No backup to resume" + de_DE: "Keine Sicherung zum Fortsetzen" + es_ES: "No hay respaldo para reanudar" + fr_FR: "Aucune sauvegarde à reprendre" + pl_PL: "Brak kopii zapasowej do wznowienia" + +# context/setup.rs +context.setup.couldnt-generate-ec-key: + en_US: "Couldn't generate ec key" + de_DE: "EC-Schlüssel konnte nicht generiert werden" + es_ES: "No se pudo generar la clave ec" + fr_FR: "Impossible de générer la clé ec" + pl_PL: "Nie można wygenerować klucza ec" + +context.setup.setup-complete: + en_US: "Setup complete!" + de_DE: "Einrichtung abgeschlossen!" + es_ES: "¡Configuración completada!" + fr_FR: "Configuration terminée !" + pl_PL: "Konfiguracja zakończona!" + +context.setup.setup-failed: + en_US: "Setup failed: %{error}" + de_DE: "Einrichtung fehlgeschlagen: %{error}" + es_ES: "Configuración fallida: %{error}" + fr_FR: "Échec de la configuration : %{error}" + pl_PL: "Konfiguracja nie powiodła się: %{error}" + +context.setup.setup-already-complete: + en_US: "Setup already complete" + de_DE: "Einrichtung bereits abgeschlossen" + es_ES: "La configuración ya está completa" + fr_FR: "Configuration déjà terminée" + pl_PL: "Konfiguracja już zakończona" + +context.setup.setup-already-in-progress: + en_US: "Setup already in progress" + de_DE: "Einrichtung bereits im Gange" + es_ES: "La configuración ya está en progreso" + fr_FR: "Configuration déjà en cours" + pl_PL: "Konfiguracja już w toku" + +context.setup.error-in-setup-progress-websocket: + en_US: "Error in setup progress websocket: %{error}" + de_DE: "Fehler im Setup-Fortschritts-WebSocket: %{error}" + es_ES: "Error en el websocket de progreso de configuración: %{error}" + fr_FR: "Erreur dans le websocket de progression de la configuration : %{error}" + pl_PL: "Błąd w websocket postępu konfiguracji: %{error}" + +# context/diagnostic.rs +context.diagnostic.starting-diagnostic-ui: + en_US: "Error: %{error}: Starting diagnostic UI" + de_DE: "Fehler: %{error}: Starte Diagnose-UI" + es_ES: "Error: %{error}: Iniciando interfaz de diagnóstico" + fr_FR: "Erreur : %{error} : Démarrage de l'interface de diagnostic" + pl_PL: "Błąd: %{error}: Uruchamianie interfejsu diagnostycznego" + +# context/cli.rs +context.cli.pkcs8-key-incorrect-length: + en_US: "pkcs8 key is of incorrect length" + de_DE: "PKCS8-Schlüssel hat falsche Länge" + es_ES: "la clave pkcs8 tiene una longitud incorrecta" + fr_FR: "la clé pkcs8 a une longueur incorrecte" + pl_PL: "klucz pkcs8 ma nieprawidłową długość" + +context.cli.developer-key-does-not-exist: + en_US: "Developer Key does not exist! Please run `start-cli init-key` before running this command." + de_DE: "Entwicklerschlüssel existiert nicht! Bitte führen Sie `start-cli init-key` aus, bevor Sie diesen Befehl ausführen." + es_ES: "¡La clave de desarrollador no existe! Por favor ejecute `start-cli init-key` antes de ejecutar este comando." + fr_FR: "La clé développeur n'existe pas ! Veuillez exécuter `start-cli init-key` avant d'exécuter cette commande." + pl_PL: "Klucz programisty nie istnieje! Proszę uruchomić `start-cli init-key` przed wykonaniem tego polecenia." + +context.cli.cannot-parse-scheme-from-base-url: + en_US: "Cannot parse scheme from base URL" + de_DE: "Schema kann nicht aus Basis-URL geparst werden" + es_ES: "No se puede analizar el esquema de la URL base" + fr_FR: "Impossible d'analyser le schéma de l'URL de base" + pl_PL: "Nie można przeanalizować schematu z podstawowego URL" + +context.cli.cannot-set-url-scheme: + en_US: "Cannot set URL scheme" + de_DE: "URL-Schema kann nicht gesetzt werden" + es_ES: "No se puede establecer el esquema de URL" + fr_FR: "Impossible de définir le schéma d'URL" + pl_PL: "Nie można ustawić schematu URL" + +# context/rpc.rs +context.rpc.rpc-context-dropped: + en_US: "RpcContext is dropped" + de_DE: "RpcContext wurde verworfen" + es_ES: "RpcContext ha sido descartado" + fr_FR: "RpcContext a été abandonné" + pl_PL: "RpcContext został porzucony" + +context.rpc.opened-patchdb: + en_US: "Opened PatchDB" + de_DE: "PatchDB geöffnet" + es_ES: "PatchDB abierto" + fr_FR: "PatchDB ouvert" + pl_PL: "Otwarto PatchDB" + +context.rpc.initialized-net-controller: + en_US: "Initialized Net Controller" + de_DE: "Net Controller initialisiert" + es_ES: "Controlador de red inicializado" + fr_FR: "Contrôleur réseau initialisé" + pl_PL: "Zainicjalizowano kontroler sieci" + +context.rpc.nvidia-smi-error: + en_US: "nvidia-smi: %{error}" + de_DE: "nvidia-smi: %{error}" + es_ES: "nvidia-smi: %{error}" + fr_FR: "nvidia-smi : %{error}" + pl_PL: "nvidia-smi: %{error}" + +context.rpc.nvidia-warning-can-be-ignored: + en_US: "The above warning can be ignored if no NVIDIA card is present" + de_DE: "Die obige Warnung kann ignoriert werden, wenn keine NVIDIA-Karte vorhanden ist" + es_ES: "La advertencia anterior se puede ignorar si no hay tarjeta NVIDIA presente" + fr_FR: "L'avertissement ci-dessus peut être ignoré si aucune carte NVIDIA n'est présente" + pl_PL: "Powyższe ostrzeżenie można zignorować, jeśli nie ma karty NVIDIA" + +context.rpc.os-partition-info-missing: + en_US: "OS Partition Information Missing" + de_DE: "OS-Partitionsinformationen fehlen" + es_ES: "Falta información de la partición del SO" + fr_FR: "Informations de partition du système d'exploitation manquantes" + pl_PL: "Brak informacji o partycji systemu operacyjnego" + +context.rpc.couldnt-generate-ec-key: + en_US: "Couldn't generate ec key" + de_DE: "EC-Schlüssel konnte nicht generiert werden" + es_ES: "No se pudo generar la clave ec" + fr_FR: "Impossible de générer la clé ec" + pl_PL: "Nie można wygenerować klucza ec" + +context.rpc.cleaned-up-transient-states: + en_US: "Cleaned up transient states" + de_DE: "Transiente Zustände bereinigt" + es_ES: "Estados transitorios limpiados" + fr_FR: "États transitoires nettoyés" + pl_PL: "Wyczyszczono stany przejściowe" + +context.rpc.completed-migrations: + en_US: "Completed migrations" + de_DE: "Migrationen abgeschlossen" + es_ES: "Migraciones completadas" + fr_FR: "Migrations terminées" + pl_PL: "Migracje zakończone" + +context.rpc.rpc-context-shutdown: + en_US: "RpcContext is shutdown" + de_DE: "RpcContext ist heruntergefahren" + es_ES: "RpcContext está apagado" + fr_FR: "RpcContext est arrêté" + pl_PL: "RpcContext jest wyłączony" + +context.rpc.error-in-session-cleanup-cron: + en_US: "Error in session cleanup cron: %{error}" + de_DE: "Fehler im Sitzungsbereinigung-Cron: %{error}" + es_ES: "Error en el cron de limpieza de sesión: %{error}" + fr_FR: "Erreur dans le cron de nettoyage de session : %{error}" + pl_PL: "Błąd w cronie czyszczenia sesji: %{error}" + +# middleware/auth/local.rs +middleware.auth.unauthorized: + en_US: "UNAUTHORIZED" + de_DE: "NICHT AUTORISIERT" + es_ES: "NO AUTORIZADO" + fr_FR: "NON AUTORISÉ" + pl_PL: "BRAK AUTORYZACJI" + +# middleware/auth/session.rs +middleware.auth.rate-limited-login: + en_US: "Please limit login attempts to 3 per 20 seconds." + de_DE: "Bitte beschränken Sie Anmeldeversuche auf 3 pro 20 Sekunden." + es_ES: "Por favor limite los intentos de inicio de sesión a 3 por cada 20 segundos." + fr_FR: "Veuillez limiter les tentatives de connexion à 3 par 20 secondes." + pl_PL: "Proszę ograniczyć próby logowania do 3 na 20 sekund." + +# middleware/auth/signature.rs +middleware.auth.key-not-authorized: + en_US: "Key is not authorized" + de_DE: "Schlüssel ist nicht autorisiert" + es_ES: "La clave no está autorizada" + fr_FR: "La clé n'est pas autorisée" + pl_PL: "Klucz nie jest autoryzowany" + +middleware.auth.replay-attack-detected: + en_US: "replay attack detected" + de_DE: "Replay-Angriff erkannt" + es_ES: "ataque de repetición detectado" + fr_FR: "attaque par rejeu détectée" + pl_PL: "wykryto atak powtórzeniowy" + +middleware.auth.no-valid-sig-context: + en_US: "no valid signature context available to verify" + de_DE: "kein gültiger Signaturkontext zur Verifizierung verfügbar" + es_ES: "no hay contexto de firma válido disponible para verificar" + fr_FR: "aucun contexte de signature valide disponible pour vérifier" + pl_PL: "brak prawidłowego kontekstu podpisu do weryfikacji" + +middleware.auth.timestamp-not-within-30s: + en_US: "timestamp not within 30s of now" + de_DE: "Zeitstempel nicht innerhalb von 30s der aktuellen Zeit" + es_ES: "la marca de tiempo no está dentro de 30s del momento actual" + fr_FR: "l'horodatage n'est pas dans les 30s de maintenant" + pl_PL: "znacznik czasu nie mieści się w 30s od teraz" + +middleware.auth.unknown-content-type: + en_US: "unknown content type" + de_DE: "unbekannter Inhaltstyp" + es_ES: "tipo de contenido desconocido" + fr_FR: "type de contenu inconnu" + pl_PL: "nieznany typ zawartości" + +# middleware/db.rs +middleware.db.error-writing-patch-sequence-header: + en_US: "error writing X-Patch-Sequence header: %{error}" + de_DE: "Fehler beim Schreiben des X-Patch-Sequence Headers: %{error}" + es_ES: "error al escribir el encabezado X-Patch-Sequence: %{error}" + fr_FR: "erreur lors de l'écriture de l'en-tête X-Patch-Sequence : %{error}" + pl_PL: "błąd zapisu nagłówka X-Patch-Sequence: %{error}" + +# ssh.rs +ssh.key-not-found: + en_US: "SSH Key Not Found" + de_DE: "SSH-Schlüssel nicht gefunden" + es_ES: "Clave SSH no encontrada" + fr_FR: "Clé SSH non trouvée" + pl_PL: "Nie znaleziono klucza SSH" + +# logs.rs +logs.no-stdout-available: + en_US: "No stdout available" + de_DE: "Keine Standardausgabe verfügbar" + es_ES: "No hay stdout disponible" + fr_FR: "Pas de stdout disponible" + pl_PL: "Brak dostępnego stdout" + +logs.error-in-log-stream: + en_US: "Error in log stream: %{error}" + de_DE: "Fehler im Log-Stream: %{error}" + es_ES: "Error en el flujo de logs: %{error}" + fr_FR: "Erreur dans le flux de logs : %{error}" + pl_PL: "Błąd w strumieniu logów: %{error}" + +# notifications.rs +notifications.invalid-level: + en_US: "Invalid Notification Level: %{level}" + de_DE: "Ungültige Benachrichtigungsstufe: %{level}" + es_ES: "Nivel de notificación inválido: %{level}" + fr_FR: "Niveau de notification invalide : %{level}" + pl_PL: "Nieprawidłowy poziom powiadomienia: %{level}" + +# update/mod.rs +update.already-updated-restart-required: + en_US: "Server was already updated. Please restart your device before attempting to update again." + de_DE: "Server wurde bereits aktualisiert. Bitte starten Sie Ihr Gerät neu, bevor Sie erneut versuchen zu aktualisieren." + es_ES: "El servidor ya fue actualizado. Por favor reinicie su dispositivo antes de intentar actualizar nuevamente." + fr_FR: "Le serveur a déjà été mis à jour. Veuillez redémarrer votre appareil avant de tenter une nouvelle mise à jour." + pl_PL: "Serwer został już zaktualizowany. Proszę zrestartować urządzenie przed ponowną próbą aktualizacji." + +update.no-updates-available: + en_US: "No updates available" + de_DE: "Keine Updates verfügbar" + es_ES: "No hay actualizaciones disponibles" + fr_FR: "Aucune mise à jour disponible" + pl_PL: "Brak dostępnych aktualizacji" + +update.updating-to-version: + en_US: "Updating to v%{version}..." + de_DE: "Aktualisiere auf v%{version}..." + es_ES: "Actualizando a v%{version}..." + fr_FR: "Mise à jour vers v%{version}..." + pl_PL: "Aktualizacja do v%{version}..." + +update.complete-restart-to-apply: + en_US: "Update complete. Restart your server to apply the update." + de_DE: "Update abgeschlossen. Starten Sie Ihren Server neu, um das Update anzuwenden." + es_ES: "Actualización completada. Reinicie su servidor para aplicar la actualización." + fr_FR: "Mise à jour terminée. Redémarrez votre serveur pour appliquer la mise à jour." + pl_PL: "Aktualizacja zakończona. Zrestartuj serwer, aby zastosować aktualizację." + +update.already-updating: + en_US: "Server is already updating!" + de_DE: "Server wird bereits aktualisiert!" + es_ES: "¡El servidor ya se está actualizando!" + fr_FR: "Le serveur est déjà en cours de mise à jour !" + pl_PL: "Serwer jest już w trakcie aktualizacji!" + +update.error-returning-progress: + en_US: "Error returning progress of update: %{error}" + de_DE: "Fehler beim Zurückgeben des Update-Fortschritts: %{error}" + es_ES: "Error al devolver el progreso de la actualización: %{error}" + fr_FR: "Erreur lors du retour de la progression de la mise à jour : %{error}" + pl_PL: "Błąd zwracania postępu aktualizacji: %{error}" + +update.not-successful: + en_US: "Update was not successful because of %{error}" + de_DE: "Update war nicht erfolgreich wegen %{error}" + es_ES: "La actualización no fue exitosa debido a %{error}" + fr_FR: "La mise à jour n'a pas réussi à cause de %{error}" + pl_PL: "Aktualizacja nie powiodła się z powodu %{error}" + +update.failed-title: + en_US: "StartOS Update Failed" + de_DE: "StartOS-Update fehlgeschlagen" + es_ES: "Actualización de StartOS fallida" + fr_FR: "Échec de la mise à jour de StartOS" + pl_PL: "Aktualizacja StartOS nie powiodła się" + +# shutdown.rs +shutdown.beginning-restart: + en_US: "Beginning server restart" + de_DE: "Server-Neustart wird begonnen" + es_ES: "Iniciando reinicio del servidor" + fr_FR: "Début du redémarrage du serveur" + pl_PL: "Rozpoczynanie restartu serwera" + +shutdown.beginning-shutdown: + en_US: "Beginning server shutdown" + de_DE: "Server-Herunterfahren wird begonnen" + es_ES: "Iniciando apagado del servidor" + fr_FR: "Début de l'arrêt du serveur" + pl_PL: "Rozpoczynanie zamykania serwera" + +shutdown.error-stopping-journald: + en_US: "Error Stopping Journald: %{error}" + de_DE: "Fehler beim Stoppen von Journald: %{error}" + es_ES: "Error al detener Journald: %{error}" + fr_FR: "Erreur lors de l'arrêt de Journald : %{error}" + pl_PL: "Błąd zatrzymywania Journald: %{error}" + +shutdown.error-exporting-volume-group: + en_US: "Error Exporting Volume Group: %{error}" + de_DE: "Fehler beim Exportieren der Volume-Gruppe: %{error}" + es_ES: "Error al exportar el grupo de volúmenes: %{error}" + fr_FR: "Erreur lors de l'exportation du groupe de volumes : %{error}" + pl_PL: "Błąd eksportowania grupy woluminów: %{error}" + +shutdown.error-playing-shutdown-song: + en_US: "Error Playing Shutdown Song: %{error}" + de_DE: "Fehler beim Abspielen der Herunterfahren-Melodie: %{error}" + es_ES: "Error al reproducir la canción de apagado: %{error}" + fr_FR: "Erreur lors de la lecture de la mélodie d'arrêt : %{error}" + pl_PL: "Błąd odtwarzania melodii zamykania: %{error}" + +# auth.rs +auth.malformed-etc-shadow: + en_US: "malformed /etc/shadow" + de_DE: "fehlerhafte /etc/shadow" + es_ES: "/etc/shadow mal formado" + fr_FR: "/etc/shadow mal formé" + pl_PL: "zniekształcony /etc/shadow" + +auth.couldnt-decode-password: + en_US: "Couldn't decode password" + de_DE: "Passwort konnte nicht dekodiert werden" + es_ES: "No se pudo decodificar la contraseña" + fr_FR: "Impossible de décoder le mot de passe" + pl_PL: "Nie można zdekodować hasła" + +auth.password-incorrect: + en_US: "Password Incorrect" + de_DE: "Passwort falsch" + es_ES: "Contraseña incorrecta" + fr_FR: "Mot de passe incorrect" + pl_PL: "Nieprawidłowe hasło" + +auth.prompt-current-password: + en_US: "Current Password: " + de_DE: "Aktuelles Passwort: " + es_ES: "Contraseña actual: " + fr_FR: "Mot de passe actuel : " + pl_PL: "Obecne hasło: " + +auth.prompt-new-password: + en_US: "New Password: " + de_DE: "Neues Passwort: " + es_ES: "Nueva contraseña: " + fr_FR: "Nouveau mot de passe : " + pl_PL: "Nowe hasło: " + +auth.prompt-confirm: + en_US: "Confirm: " + de_DE: "Bestätigen: " + es_ES: "Confirmar: " + fr_FR: "Confirmer : " + pl_PL: "Potwierdź: " + +auth.passwords-do-not-match: + en_US: "Passwords do not match" + de_DE: "Passwörter stimmen nicht überein" + es_ES: "Las contraseñas no coinciden" + fr_FR: "Les mots de passe ne correspondent pas" + pl_PL: "Hasła nie pasują do siebie" + +# util/tui.rs +util.tui.terminal-must-be-interactive: + en_US: "Terminal must be in interactive mode for this wizard" + de_DE: "Terminal muss für diesen Assistenten im interaktiven Modus sein" + es_ES: "El terminal debe estar en modo interactivo para este asistente" + fr_FR: "Le terminal doit être en mode interactif pour cet assistant" + pl_PL: "Terminal musi być w trybie interaktywnym dla tego kreatora" + +util.tui.enter-valid-value: + en_US: "Please enter a valid %{what}." + de_DE: "Bitte geben Sie einen gültigen %{what} ein." + es_ES: "Por favor ingrese un %{what} válido." + fr_FR: "Veuillez entrer un %{what} valide." + pl_PL: "Proszę wprowadzić prawidłowe %{what}." + +util.tui.aborted: + en_US: "Aborted" + de_DE: "Abgebrochen" + es_ES: "Abortado" + fr_FR: "Abandonné" + pl_PL: "Przerwano" + +util.tui.selected-choice-not-in-input: + en_US: "selected choice does not appear in input" + de_DE: "ausgewählte Option erscheint nicht in der Eingabe" + es_ES: "la opción seleccionada no aparece en la entrada" + fr_FR: "le choix sélectionné n'apparaît pas dans l'entrée" + pl_PL: "wybrany wybór nie pojawia się w danych wejściowych" + +# util/serde.rs +util.serde.must-specify-units-for-duration: + en_US: "Must specify units for duration" + de_DE: "Einheiten für die Dauer müssen angegeben werden" + es_ES: "Debe especificar unidades para la duración" + fr_FR: "Doit spécifier les unités pour la durée" + pl_PL: "Należy określić jednostki dla czasu trwania" + +util.serde.invalid-units-for-duration: + en_US: "Invalid units for duration" + de_DE: "Ungültige Einheiten für die Dauer" + es_ES: "Unidades inválidas para la duración" + fr_FR: "Unités invalides pour la durée" + pl_PL: "Nieprawidłowe jednostki dla czasu trwania" + +util.serde.failed-to-create-from-buffer: + en_US: "failed to create from buffer" + de_DE: "Erstellung aus Puffer fehlgeschlagen" + es_ES: "error al crear desde el búfer" + fr_FR: "échec de la création à partir du tampon" + pl_PL: "nie udało się utworzyć z bufora" + +util.serde.failed-to-parse-expression: + en_US: "Failed to parse expression: %{errors}" + de_DE: "Ausdruck konnte nicht geparst werden: %{errors}" + es_ES: "Error al analizar la expresión: %{errors}" + fr_FR: "Échec de l'analyse de l'expression : %{errors}" + pl_PL: "Nie udało się przeanalizować wyrażenia: %{errors}" + +util.serde.failed-to-compile-expression: + en_US: "Failed to compile expression: %{errors}" + de_DE: "Ausdruck konnte nicht kompiliert werden: %{errors}" + es_ES: "Error al compilar la expresión: %{errors}" + fr_FR: "Échec de la compilation de l'expression : %{errors}" + pl_PL: "Nie udało się skompilować wyrażenia: %{errors}" + +util.serde.expr-returned-no-results: + en_US: "expr returned no results" + de_DE: "Ausdruck hat keine Ergebnisse zurückgegeben" + es_ES: "la expresión no devolvió resultados" + fr_FR: "l'expression n'a retourné aucun résultat" + pl_PL: "wyrażenie nie zwróciło wyników" + +util.serde.expr-returned-too-many-results: + en_US: "expr returned too many results" + de_DE: "Ausdruck hat zu viele Ergebnisse zurückgegeben" + es_ES: "la expresión devolvió demasiados resultados" + fr_FR: "l'expression a retourné trop de résultats" + pl_PL: "wyrażenie zwróciło zbyt wiele wyników" + +# util/cpupower.rs +util.cpupower.governors-listed-before-cpu: + en_US: "governors listed before cpu" + de_DE: "Governors vor CPU aufgelistet" + es_ES: "governors listados antes de cpu" + fr_FR: "gouverneurs listés avant cpu" + pl_PL: "regulatory wymienione przed cpu" + +util.cpupower.failed-to-parse-output: + en_US: "Failed to parse cpupower output:\n%{output}" + de_DE: "Fehler beim Parsen der cpupower-Ausgabe:\n%{output}" + es_ES: "Error al analizar la salida de cpupower:\n%{output}" + fr_FR: "Échec de l'analyse de la sortie de cpupower :\n%{output}" + pl_PL: "Nie udało się przeanalizować wyjścia cpupower:\n%{output}" + +# util/rpc.rs +util.rpc.unknown-scheme: + en_US: "unknown scheme: %{scheme}" + de_DE: "unbekanntes Schema: %{scheme}" + es_ES: "esquema desconocido: %{scheme}" + fr_FR: "schéma inconnu : %{scheme}" + pl_PL: "nieznany schemat: %{scheme}" + +# util/net.rs +util.net.websocket-ping-timeout: + en_US: "Timeout: WebSocket did not respond to ping within %{timeout}" + de_DE: "Zeitüberschreitung: WebSocket hat nicht innerhalb von %{timeout} auf Ping geantwortet" + es_ES: "Tiempo de espera: WebSocket no respondió al ping dentro de %{timeout}" + fr_FR: "Délai d'attente : le WebSocket n'a pas répondu au ping dans les %{timeout}" + pl_PL: "Przekroczono limit czasu: WebSocket nie odpowiedział na ping w ciągu %{timeout}" + +# CLI command descriptions (about.*) +about.add-address-to-host: + en_US: "Add an address to this host" + de_DE: "Eine Adresse zu diesem Host hinzufügen" + es_ES: "Agregar una dirección a este host" + fr_FR: "Ajouter une adresse à cet hôte" + pl_PL: "Dodaj adres do tego hosta" + +about.add-admin-signer: + en_US: "Add admin signer" + de_DE: "Admin-Unterzeichner hinzufügen" + es_ES: "Agregar firmante administrador" + fr_FR: "Ajouter un signataire administrateur" + pl_PL: "Dodaj sygnatariusza administratora" + +about.add-asset-registry: + en_US: "Add asset to registry" + de_DE: "Asset zum Registry hinzufügen" + es_ES: "Agregar activo al registro" + fr_FR: "Ajouter un actif au registre" + pl_PL: "Dodaj zasób do rejestru" + +about.add-category-registry: + en_US: "Add a category to the registry" + de_DE: "Eine Kategorie zum Registry hinzufügen" + es_ES: "Agregar una categoría al registro" + fr_FR: "Ajouter une catégorie au registre" + pl_PL: "Dodaj kategorię do rejestru" + +about.add-device-to-subnet: + en_US: "Add a device to a subnet" + de_DE: "Ein Gerät zu einem Subnetz hinzufügen" + es_ES: "Agregar un dispositivo a una subred" + fr_FR: "Ajouter un appareil à un sous-réseau" + pl_PL: "Dodaj urządzenie do podsieci" + +about.add-image-to-s9pk: + en_US: "Add image to s9pk" + de_DE: "Image zu s9pk hinzufügen" + es_ES: "Agregar imagen a s9pk" + fr_FR: "Ajouter une image au s9pk" + pl_PL: "Dodaj obraz do s9pk" + +about.add-mirror-s9pk: + en_US: "Add a mirror for an s9pk" + de_DE: "Einen Spiegel für ein s9pk hinzufügen" + es_ES: "Agregar un espejo para un s9pk" + fr_FR: "Ajouter un miroir pour un s9pk" + pl_PL: "Dodaj serwer lustrzany dla s9pk" + +about.add-new-authorized-key: + en_US: "Add a new authorized key" + de_DE: "Einen neuen autorisierten Schlüssel hinzufügen" + es_ES: "Agregar una nueva clave autorizada" + fr_FR: "Ajouter une nouvelle clé autorisée" + pl_PL: "Dodaj nowy autoryzowany klucz" + +about.add-new-backup-target: + en_US: "Add a new backup target" + de_DE: "Ein neues Backup-Ziel hinzufügen" + es_ES: "Agregar un nuevo destino de respaldo" + fr_FR: "Ajouter une nouvelle cible de sauvegarde" + pl_PL: "Dodaj nowy cel kopii zapasowej" + +about.add-new-port-forward: + en_US: "Add a new port forward" + de_DE: "Eine neue Portweiterleitung hinzufügen" + es_ES: "Agregar un nuevo reenvío de puerto" + fr_FR: "Ajouter une nouvelle redirection de port" + pl_PL: "Dodaj nowe przekierowanie portu" + +about.add-new-subnet: + en_US: "Add a new subnet" + de_DE: "Ein neues Subnetz hinzufügen" + es_ES: "Agregar una nueva subred" + fr_FR: "Ajouter un nouveau sous-réseau" + pl_PL: "Dodaj nową podsieć" + +about.add-new-tunnel: + en_US: "Add a new tunnel" + de_DE: "Einen neuen Tunnel hinzufügen" + es_ES: "Agregar un nuevo túnel" + fr_FR: "Ajouter un nouveau tunnel" + pl_PL: "Dodaj nowy tunel" + +about.add-onion-service-key-to-store: + en_US: "Add an onion service key to the key store" + de_DE: "Einen Onion-Service-Schlüssel zum Schlüsselspeicher hinzufügen" + es_ES: "Agregar una clave de servicio onion al almacén de claves" + fr_FR: "Ajouter une clé de service onion au magasin de clés" + pl_PL: "Dodaj klucz usługi onion do magazynu kluczy" + +about.add-or-remove-authorized-clients: + en_US: "Add or remove authorized clients" + de_DE: "Autorisierte Clients hinzufügen oder entfernen" + es_ES: "Agregar o eliminar clientes autorizados" + fr_FR: "Ajouter ou supprimer des clients autorisés" + pl_PL: "Dodaj lub usuń autoryzowanych klientów" + +about.add-os-version: + en_US: "Add OS version" + de_DE: "Betriebssystemversion hinzufügen" + es_ES: "Agregar versión del SO" + fr_FR: "Ajouter une version du système d'exploitation" + pl_PL: "Dodaj wersję systemu operacyjnego" + +about.add-package-category: + en_US: "Add a package to a category" + de_DE: "Ein Paket zu einer Kategorie hinzufügen" + es_ES: "Agregar un paquete a una categoría" + fr_FR: "Ajouter un paquet à une catégorie" + pl_PL: "Dodaj pakiet do kategorii" + +about.add-package-registry: + en_US: "Add package to registry index" + de_DE: "Paket zum Registry-Index hinzufügen" + es_ES: "Agregar paquete al índice del registro" + fr_FR: "Ajouter un paquet à l'index du registre" + pl_PL: "Dodaj pakiet do indeksu rejestru" + +about.add-package-signer: + en_US: "Add package signer" + de_DE: "Paket-Unterzeichner hinzufügen" + es_ES: "Agregar firmante de paquete" + fr_FR: "Ajouter un signataire de paquet" + pl_PL: "Dodaj sygnatariusza pakietu" + +about.add-path-value-db: + en_US: "Add path and value to db" + de_DE: "Pfad und Wert zur Datenbank hinzufügen" + es_ES: "Agregar ruta y valor a la base de datos" + fr_FR: "Ajouter un chemin et une valeur à la base de données" + pl_PL: "Dodaj ścieżkę i wartość do bazy danych" + +about.add-private-domain-to-host: + en_US: "Add a private domain to this host" + de_DE: "Eine private Domain zu diesem Host hinzufügen" + es_ES: "Agregar un dominio privado a este host" + fr_FR: "Ajouter un domaine privé à cet hôte" + pl_PL: "Dodaj prywatną domenę do tego hosta" + +about.add-public-domain-to-host: + en_US: "Add a public domain to this host" + de_DE: "Eine öffentliche Domain zu diesem Host hinzufügen" + es_ES: "Agregar un dominio público a este host" + fr_FR: "Ajouter un domaine public à cet hôte" + pl_PL: "Dodaj publiczną domenę do tego hosta" + +about.add-remove-list-package-signers: + en_US: "Add, remove, and list package signers" + de_DE: "Paket-Unterzeichner hinzufügen, entfernen und auflisten" + es_ES: "Agregar, eliminar y listar firmantes de paquetes" + fr_FR: "Ajouter, supprimer et lister les signataires de paquets" + pl_PL: "Dodaj, usuń i wyświetl sygnatariuszy pakietów" + +about.add-remove-list-version-signers: + en_US: "Add, remove, and list version signers" + de_DE: "Versions-Unterzeichner hinzufügen, entfernen und auflisten" + es_ES: "Agregar, eliminar y listar firmantes de versiones" + fr_FR: "Ajouter, supprimer et lister les signataires de versions" + pl_PL: "Dodaj, usuń i wyświetl sygnatariuszy wersji" + +about.add-remove-or-list-devices-in-subnets: + en_US: "Add, remove, or list devices in subnets" + de_DE: "Geräte in Subnetzen hinzufügen, entfernen oder auflisten" + es_ES: "Agregar, eliminar o listar dispositivos en subredes" + fr_FR: "Ajouter, supprimer ou lister les appareils dans les sous-réseaux" + pl_PL: "Dodaj, usuń lub wyświetl urządzenia w podsieciach" + +about.add-remove-or-modify-subnets: + en_US: "Add, remove, or modify subnets" + de_DE: "Subnetze hinzufügen, entfernen oder ändern" + es_ES: "Agregar, eliminar o modificar subredes" + fr_FR: "Ajouter, supprimer ou modifier des sous-réseaux" + pl_PL: "Dodaj, usuń lub zmodyfikuj podsieci" + +about.add-remove-update-backup-target: + en_US: "Add, remove, or update a backup target" + de_DE: "Backup-Ziel hinzufügen, entfernen oder aktualisieren" + es_ES: "Agregar, eliminar o actualizar un destino de respaldo" + fr_FR: "Ajouter, supprimer ou mettre à jour une cible de sauvegarde" + pl_PL: "Dodaj, usuń lub zaktualizuj cel kopii zapasowej" + +about.add-signer: + en_US: "Add signer" + de_DE: "Unterzeichner hinzufügen" + es_ES: "Agregar firmante" + fr_FR: "Ajouter un signataire" + pl_PL: "Dodaj sygnatariusza" + +about.add-ssh-key: + en_US: "Add ssh key" + de_DE: "SSH-Schlüssel hinzufügen" + es_ES: "Agregar clave ssh" + fr_FR: "Ajouter une clé ssh" + pl_PL: "Dodaj klucz ssh" + +about.add-version-signer: + en_US: "Add version signer" + de_DE: "Versions-Unterzeichner hinzufügen" + es_ES: "Agregar firmante de versión" + fr_FR: "Ajouter un signataire de version" + pl_PL: "Dodaj sygnatariusza wersji" + +about.add-wifi-ssid-password: + en_US: "Add wifi ssid and password" + de_DE: "WLAN-SSID und Passwort hinzufügen" + es_ES: "Agregar ssid y contraseña de wifi" + fr_FR: "Ajouter un ssid et mot de passe wifi" + pl_PL: "Dodaj ssid i hasło wifi" + +about.allow-gateway-infer-inbound-access-from-wan: + en_US: "Allow this gateway to infer whether it has inbound access from the WAN based on its IPv4 address" + de_DE: "Diesem Gateway erlauben, anhand seiner IPv4-Adresse zu ermitteln, ob es eingehenden Zugriff vom WAN hat" + es_ES: "Permitir que este gateway deduzca si tiene acceso entrante desde WAN basado en su dirección IPv4" + fr_FR: "Permettre à cette passerelle de déduire si elle a un accès entrant depuis le WAN en fonction de son adresse IPv4" + pl_PL: "Pozwól tej bramce wywnioskować, czy ma dostęp przychodzący z WAN na podstawie adresu IPv4" + +about.calculate-blake3-hash-for-file: + en_US: "Calculate blake3 hash for a file" + de_DE: "Blake3-Hash für eine Datei berechnen" + es_ES: "Calcular hash blake3 para un archivo" + fr_FR: "Calculer le hachage blake3 d'un fichier" + pl_PL: "Oblicz hash blake3 dla pliku" + +about.cancel-install-package: + en_US: "Cancel an install of a package" + de_DE: "Eine Paketinstallation abbrechen" + es_ES: "Cancelar la instalación de un paquete" + fr_FR: "Annuler l'installation d'un paquet" + pl_PL: "Anuluj instalację pakietu" + +about.check-update-startos: + en_US: "Check a given registry for StartOS updates and update if available" + de_DE: "Ein bestimmtes Registry auf StartOS-Updates prüfen und bei Verfügbarkeit aktualisieren" + es_ES: "Verificar un registro dado para actualizaciones de StartOS y actualizar si está disponible" + fr_FR: "Vérifier un registre donné pour les mises à jour StartOS et mettre à jour si disponible" + pl_PL: "Sprawdź dany rejestr pod kątem aktualizacji StartOS i zaktualizuj, jeśli dostępne" + +about.clear-service-task: + en_US: "Clear a service task" + de_DE: "Eine Dienstaufgabe löschen" + es_ES: "Borrar una tarea de servicio" + fr_FR: "Effacer une tâche de service" + pl_PL: "Wyczyść zadanie usługi" + +about.clear-smtp: + en_US: "Remove system smtp server and credentials" + de_DE: "System-SMTP-Server und Anmeldedaten entfernen" + es_ES: "Eliminar servidor smtp del sistema y credenciales" + fr_FR: "Supprimer le serveur smtp système et les identifiants" + pl_PL: "Usuń systemowy serwer smtp i dane uwierzytelniające" + +about.command-add-ui-record-db: + en_US: "Command for adding UI record to db" + de_DE: "Befehl zum Hinzufügen eines UI-Eintrags zur Datenbank" + es_ES: "Comando para agregar registro de UI a la base de datos" + fr_FR: "Commande pour ajouter un enregistrement UI à la base de données" + pl_PL: "Polecenie dodania rekordu UI do bazy danych" + +about.command-blake3-hash: + en_US: "Command for calculating the blake3 hash of a file" + de_DE: "Befehl zur Berechnung des Blake3-Hashs einer Datei" + es_ES: "Comando para calcular el hash blake3 de un archivo" + fr_FR: "Commande pour calculer le hachage blake3 d'un fichier" + pl_PL: "Polecenie obliczenia hashu blake3 pliku" + +about.command-list-available-wifi: + en_US: "Command to list available wifi networks" + de_DE: "Befehl zum Auflisten verfügbarer WLAN-Netzwerke" + es_ES: "Comando para listar redes wifi disponibles" + fr_FR: "Commande pour lister les réseaux wifi disponibles" + pl_PL: "Polecenie wyświetlenia dostępnych sieci wifi" + +about.command-remove-disk-filesystem: + en_US: "Command to remove disk from filesystem" + de_DE: "Befehl zum Entfernen der Festplatte aus dem Dateisystem" + es_ES: "Comando para eliminar disco del sistema de archivos" + fr_FR: "Commande pour retirer le disque du système de fichiers" + pl_PL: "Polecenie usunięcia dysku z systemu plików" + +about.commands-action: + en_US: "Commands to get action input or run an action" + de_DE: "Befehle zum Abrufen von Aktionseingaben oder Ausführen einer Aktion" + es_ES: "Comandos para obtener entrada de acción o ejecutar una acción" + fr_FR: "Commandes pour obtenir une entrée d'action ou exécuter une action" + pl_PL: "Polecenia do uzyskania wejścia akcji lub uruchomienia akcji" + +about.commands-add-image-or-edit-manifest: + en_US: "Commands to add an image to an s9pk or edit the manifest" + de_DE: "Befehle zum Hinzufügen eines Images zu einem s9pk oder Bearbeiten des Manifests" + es_ES: "Comandos para agregar una imagen a un s9pk o editar el manifiesto" + fr_FR: "Commandes pour ajouter une image à un s9pk ou éditer le manifeste" + pl_PL: "Polecenia dodania obrazu do s9pk lub edycji manifestu" + +about.commands-add-list-admins-signers: + en_US: "Commands to add or list admins or signers" + de_DE: "Befehle zum Hinzufügen oder Auflisten von Admins oder Unterzeichnern" + es_ES: "Comandos para agregar o listar administradores o firmantes" + fr_FR: "Commandes pour ajouter ou lister les administrateurs ou signataires" + pl_PL: "Polecenia dodania lub wyświetlenia administratorów lub sygnatariuszy" + +about.commands-add-list-signers: + en_US: "Commands to add or list signers" + de_DE: "Befehle zum Hinzufügen oder Auflisten von Unterzeichnern" + es_ES: "Comandos para agregar o listar firmantes" + fr_FR: "Commandes pour ajouter ou lister les signataires" + pl_PL: "Polecenia dodania lub wyświetlenia sygnatariuszy" + +about.commands-add-remove-list-versions: + en_US: "Commands to add, remove, or list versions or version signers" + de_DE: "Befehle zum Hinzufügen, Entfernen oder Auflisten von Versionen oder Versions-Unterzeichnern" + es_ES: "Comandos para agregar, eliminar o listar versiones o firmantes de versiones" + fr_FR: "Commandes pour ajouter, supprimer ou lister les versions ou signataires de versions" + pl_PL: "Polecenia dodania, usunięcia lub wyświetlenia wersji lub sygnatariuszy wersji" + +about.commands-add-sign-get-assets: + en_US: "Commands to add, sign, or get registry assets" + de_DE: "Befehle zum Hinzufügen, Signieren oder Abrufen von Registry-Assets" + es_ES: "Comandos para agregar, firmar u obtener activos del registro" + fr_FR: "Commandes pour ajouter, signer ou obtenir des actifs du registre" + pl_PL: "Polecenia dodania, podpisania lub pobrania zasobów rejestru" + +about.commands-authentication: + en_US: "Commands related to Authentication i.e. login, logout" + de_DE: "Befehle zur Authentifizierung, z.B. Anmelden, Abmelden" + es_ES: "Comandos relacionados con la autenticación, como iniciar sesión, cerrar sesión" + fr_FR: "Commandes liées à l'authentification, comme connexion, déconnexion" + pl_PL: "Polecenia związane z uwierzytelnianiem, np. logowanie, wylogowanie" + +about.commands-backup: + en_US: "Commands related to backup creation and backup targets" + de_DE: "Befehle zur Backup-Erstellung und Backup-Zielen" + es_ES: "Comandos relacionados con la creación de respaldos y destinos de respaldo" + fr_FR: "Commandes liées à la création de sauvegardes et aux cibles de sauvegarde" + pl_PL: "Polecenia związane z tworzeniem kopii zapasowych i celami kopii zapasowych" + +about.commands-backup-target: + en_US: "Commands related to a backup target" + de_DE: "Befehle zu einem Backup-Ziel" + es_ES: "Comandos relacionados con un destino de respaldo" + fr_FR: "Commandes liées à une cible de sauvegarde" + pl_PL: "Polecenia związane z celem kopii zapasowej" + +about.commands-db: + en_US: "Commands to interact with the db i.e. dump, put, apply" + de_DE: "Befehle zur Interaktion mit der Datenbank, z.B. dump, put, apply" + es_ES: "Comandos para interactuar con la base de datos, como dump, put, apply" + fr_FR: "Commandes pour interagir avec la base de données, comme dump, put, apply" + pl_PL: "Polecenia interakcji z bazą danych, np. dump, put, apply" + +about.commands-diagnostic: + en_US: "Commands to display logs, restart the server, etc" + de_DE: "Befehle zum Anzeigen von Logs, Neustart des Servers, usw." + es_ES: "Comandos para mostrar logs, reiniciar el servidor, etc." + fr_FR: "Commandes pour afficher les logs, redémarrer le serveur, etc." + pl_PL: "Polecenia wyświetlania logów, restartu serwera itp." + +about.commands-disk: + en_US: "Commands for listing disk info and repairing" + de_DE: "Befehle zum Auflisten von Festplatteninfo und Reparieren" + es_ES: "Comandos para listar información de disco y reparar" + fr_FR: "Commandes pour lister les informations de disque et réparer" + pl_PL: "Polecenia wyświetlania informacji o dysku i naprawy" + +about.commands-display-file-paths-contents-manifest: + en_US: "Commands to display file paths, file contents, or manifest" + de_DE: "Befehle zum Anzeigen von Dateipfaden, Dateiinhalten oder Manifest" + es_ES: "Comandos para mostrar rutas de archivos, contenidos de archivos o manifiesto" + fr_FR: "Commandes pour afficher les chemins de fichiers, contenus de fichiers ou manifeste" + pl_PL: "Polecenia wyświetlania ścieżek plików, zawartości plików lub manifestu" + +about.commands-download-assets: + en_US: "Commands to download image, iso, or squashfs files" + de_DE: "Befehle zum Herunterladen von Image-, ISO- oder Squashfs-Dateien" + es_ES: "Comandos para descargar archivos de imagen, iso o squashfs" + fr_FR: "Commandes pour télécharger des fichiers image, iso ou squashfs" + pl_PL: "Polecenia pobierania plików obrazu, iso lub squashfs" + +about.command-set-country: + en_US: "Command to set country" + de_DE: "Befehl zum Festlegen des Landes" + es_ES: "Comando para establecer el país" + fr_FR: "Commande pour définir le pays" + pl_PL: "Polecenie ustawienia kraju" + +about.commands-experimental: + en_US: "Commands related to configuring experimental options such as zram and cpu governor" + de_DE: "Befehle zur Konfiguration experimenteller Optionen wie zram und CPU-Governor" + es_ES: "Comandos relacionados con la configuración de opciones experimentales como zram y gobernador de CPU" + fr_FR: "Commandes liées à la configuration d'options expérimentales comme zram et le gouverneur CPU" + pl_PL: "Polecenia konfiguracji opcji eksperymentalnych jak zram i regulator CPU" + +about.commands-host-system-ui: + en_US: "Commands for modifying the host for the system ui" + de_DE: "Befehle zum Ändern des Hosts für die System-UI" + es_ES: "Comandos para modificar el host para la interfaz del sistema" + fr_FR: "Commandes pour modifier l'hôte pour l'interface système" + pl_PL: "Polecenia modyfikacji hosta dla interfejsu systemowego" + +about.commands-index-add-get-packages: + en_US: "Commands to index, add, or get packages" + de_DE: "Befehle zum Indizieren, Hinzufügen oder Abrufen von Paketen" + es_ES: "Comandos para indexar, agregar u obtener paquetes" + fr_FR: "Commandes pour indexer, ajouter ou obtenir des paquets" + pl_PL: "Polecenia indeksowania, dodawania lub pobierania pakietów" + +about.commands-interact-with-db-dump-apply: + en_US: "Commands to interact with the db i.e. dump and apply" + de_DE: "Befehle zur Interaktion mit der Datenbank, z.B. dump und apply" + es_ES: "Comandos para interactuar con la base de datos, como dump y apply" + fr_FR: "Commandes pour interagir avec la base de données, comme dump et apply" + pl_PL: "Polecenia interakcji z bazą danych, np. dump i apply" + +about.commands-notifications: + en_US: "Create, delete, or list notifications" + de_DE: "Benachrichtigungen erstellen, löschen oder auflisten" + es_ES: "Crear, eliminar o listar notificaciones" + fr_FR: "Créer, supprimer ou lister les notifications" + pl_PL: "Twórz, usuwaj lub wyświetlaj powiadomienia" + +about.commands-os-assets-versions: + en_US: "Commands related to OS assets and versions" + de_DE: "Befehle zu Betriebssystem-Assets und Versionen" + es_ES: "Comandos relacionados con activos y versiones del SO" + fr_FR: "Commandes liées aux actifs et versions du système d'exploitation" + pl_PL: "Polecenia związane z zasobami i wersjami systemu operacyjnego" + +about.commands-packages: + en_US: "Commands related to packages" + de_DE: "Befehle zu Paketen" + es_ES: "Comandos relacionados con paquetes" + fr_FR: "Commandes liées aux paquets" + pl_PL: "Polecenia związane z pakietami" + +about.commands-registry: + en_US: "Commands related to the registry" + de_DE: "Befehle zum Registry" + es_ES: "Comandos relacionados con el registro" + fr_FR: "Commandes liées au registre" + pl_PL: "Polecenia związane z rejestrem" + +about.commands-registry-db: + en_US: "Commands to interact with the db i.e. dump and apply" + de_DE: "Befehle zur Interaktion mit der Datenbank, z.B. dump und apply" + es_ES: "Comandos para interactuar con la base de datos, como dump y apply" + fr_FR: "Commandes pour interagir avec la base de données, comme dump et apply" + pl_PL: "Polecenia interakcji z bazą danych, np. dump i apply" + +about.commands-restore-backup: + en_US: "Commands for restoring package(s) from backup" + de_DE: "Befehle zum Wiederherstellen von Paketen aus dem Backup" + es_ES: "Comandos para restaurar paquete(s) desde respaldo" + fr_FR: "Commandes pour restaurer des paquets depuis une sauvegarde" + pl_PL: "Polecenia przywracania pakietów z kopii zapasowej" + +about.commands-s9pk: + en_US: "Commands for interacting with s9pk files" + de_DE: "Befehle zur Interaktion mit s9pk-Dateien" + es_ES: "Comandos para interactuar con archivos s9pk" + fr_FR: "Commandes pour interagir avec les fichiers s9pk" + pl_PL: "Polecenia interakcji z plikami s9pk" + +about.commands-server: + en_US: "Commands related to the server i.e. restart, update, and shutdown" + de_DE: "Befehle zum Server, z.B. Neustart, Update und Herunterfahren" + es_ES: "Comandos relacionados con el servidor, como reiniciar, actualizar y apagar" + fr_FR: "Commandes liées au serveur, comme redémarrer, mettre à jour et arrêter" + pl_PL: "Polecenia związane z serwerem, np. restart, aktualizacja i zamknięcie" + +about.commands-ssh-keys: + en_US: "Commands for interacting with ssh keys i.e. add, delete, list" + de_DE: "Befehle zur Interaktion mit SSH-Schlüsseln, z.B. hinzufügen, löschen, auflisten" + es_ES: "Comandos para interactuar con claves ssh, como agregar, eliminar, listar" + fr_FR: "Commandes pour interagir avec les clés ssh, comme ajouter, supprimer, lister" + pl_PL: "Polecenia interakcji z kluczami ssh, np. dodaj, usuń, wyświetl" + +about.commands-tunnel: + en_US: "Commands related to StartTunnel" + de_DE: "Befehle zu StartTunnel" + es_ES: "Comandos relacionados con StartTunnel" + fr_FR: "Commandes liées à StartTunnel" + pl_PL: "Polecenia związane z StartTunnel" + +about.commands-wifi: + en_US: "Commands related to wifi networks i.e. add, connect, delete" + de_DE: "Befehle zu WLAN-Netzwerken, z.B. hinzufügen, verbinden, löschen" + es_ES: "Comandos relacionados con redes wifi, como agregar, conectar, eliminar" + fr_FR: "Commandes liées aux réseaux wifi, comme ajouter, connecter, supprimer" + pl_PL: "Polecenia związane z sieciami wifi, np. dodaj, połącz, usuń" + +about.connect-wifi-network: + en_US: "Connect to a wifi network" + de_DE: "Mit einem WLAN-Netzwerk verbinden" + es_ES: "Conectar a una red wifi" + fr_FR: "Se connecter à un réseau wifi" + pl_PL: "Połącz z siecią wifi" + +about.convert-s9pk-v1-to-v2: + en_US: "Convert an s9pk from v1 to v2" + de_DE: "Konvertiere ein s9pk von v1 zu v2" + es_ES: "Convertir un s9pk de v1 a v2" + fr_FR: "Convertir un s9pk de v1 à v2" + pl_PL: "Konwertuj s9pk z v1 do v2" + +about.create-backup-all-packages: + en_US: "Create a backup for all packages" + de_DE: "Erstelle ein Backup für alle Pakete" + es_ES: "Crear una copia de seguridad de todos los paquetes" + fr_FR: "Créer une sauvegarde de tous les packages" + pl_PL: "Utwórz kopię zapasową wszystkich pakietów" + +about.create-developer-key: + en_US: "Create a new developer key" + de_DE: "Erstelle einen neuen Entwicklerschlüssel" + es_ES: "Crear una nueva clave de desarrollador" + fr_FR: "Créer une nouvelle clé de développeur" + pl_PL: "Utwórz nowy klucz deweloperski" + +about.disable-kiosk-mode: + en_US: "Disable kiosk mode" + de_DE: "Kioskmodus deaktivieren" + es_ES: "Desactivar el modo kiosco" + fr_FR: "Désactiver le mode kiosque" + pl_PL: "Wyłącz tryb kiosku" + +about.disable-webserver: + en_US: "Disable the webserver" + de_DE: "Webserver deaktivieren" + es_ES: "Desactivar el servidor web" + fr_FR: "Désactiver le serveur web" + pl_PL: "Wyłącz serwer internetowy" + +about.display-all-auth-sessions: + en_US: "Display all auth sessions" + de_DE: "Alle Authentifizierungssitzungen anzeigen" + es_ES: "Mostrar todas las sesiones de autenticación" + fr_FR: "Afficher toutes les sessions d'authentification" + pl_PL: "Wyświetl wszystkie sesje uwierzytelniania" + +about.display-current-api: + en_US: "Display the current api specification" + de_DE: "Aktuelle API-Spezifikation anzeigen" + es_ES: "Mostrar la especificación de la API actual" + fr_FR: "Afficher la spécification de l'API actuelle" + pl_PL: "Wyświetl bieżącą specyfikację API" + +about.display-diagnostic-error: + en_US: "Display diagnostic error" + de_DE: "Diagnosefehler anzeigen" + es_ES: "Mostrar error de diagnóstico" + fr_FR: "Afficher l'erreur de diagnostic" + pl_PL: "Wyświetl błąd diagnostyczny" + +about.display-file-contents: + en_US: "Display file contents from s9pk" + de_DE: "Dateiinhalte aus s9pk anzeigen" + es_ES: "Mostrar contenido del archivo desde s9pk" + fr_FR: "Afficher le contenu du fichier depuis s9pk" + pl_PL: "Wyświetl zawartość pliku z s9pk" + +about.display-githash: + en_US: "Display the git hash of this build" + de_DE: "Git-Hash dieses Builds anzeigen" + es_ES: "Mostrar el hash de git de esta compilación" + fr_FR: "Afficher le hash git de cette version" + pl_PL: "Wyświetl hash git tej kompilacji" + +about.display-installed-version: + en_US: "Display the installed version of a package" + de_DE: "Installierte Version eines Pakets anzeigen" + es_ES: "Mostrar la versión instalada de un paquete" + fr_FR: "Afficher la version installée d'un package" + pl_PL: "Wyświetl zainstalowaną wersję pakietu" + +about.display-kernel-logs: + en_US: "Display kernel logs" + de_DE: "Kernel-Logs anzeigen" + es_ES: "Mostrar registros del kernel" + fr_FR: "Afficher les journaux du noyau" + pl_PL: "Wyświetl logi jądra" + +about.display-list-of-paths: + en_US: "Display list of paths in s9pk" + de_DE: "Liste der Pfade in s9pk anzeigen" + es_ES: "Mostrar lista de rutas en s9pk" + fr_FR: "Afficher la liste des chemins dans s9pk" + pl_PL: "Wyświetl listę ścieżek w s9pk" + +about.display-os-logs: + en_US: "Display OS logs" + de_DE: "Betriebssystem-Logs anzeigen" + es_ES: "Mostrar registros del sistema operativo" + fr_FR: "Afficher les journaux du système d'exploitation" + pl_PL: "Wyświetl logi systemu operacyjnego" + +about.display-package-backup-information: + en_US: "Display backup information for a package" + de_DE: "Backup-Informationen für ein Paket anzeigen" + es_ES: "Mostrar información de copia de seguridad de un paquete" + fr_FR: "Afficher les informations de sauvegarde d'un package" + pl_PL: "Wyświetl informacje o kopii zapasowej pakietu" + +about.display-package-logs: + en_US: "Display logs for a package" + de_DE: "Logs für ein Paket anzeigen" + es_ES: "Mostrar registros de un paquete" + fr_FR: "Afficher les journaux d'un package" + pl_PL: "Wyświetl logi pakietu" + +about.display-registry-info: + en_US: "Display registry info" + de_DE: "Registry-Informationen anzeigen" + es_ES: "Mostrar información del registro" + fr_FR: "Afficher les informations du registre" + pl_PL: "Wyświetl informacje o rejestrze" + +about.display-s9pk-manifest: + en_US: "Display the s9pk manifest" + de_DE: "Das s9pk-Manifest anzeigen" + es_ES: "Mostrar el manifiesto s9pk" + fr_FR: "Afficher le manifeste s9pk" + pl_PL: "Wyświetl manifest s9pk" + +about.display-server-metrics: + en_US: "Display server metrics" + de_DE: "Server-Metriken anzeigen" + es_ES: "Mostrar métricas del servidor" + fr_FR: "Afficher les métriques du serveur" + pl_PL: "Wyświetl metryki serwera" + +about.display-time-uptime: + en_US: "Display server time and uptime" + de_DE: "Serverzeit und Betriebszeit anzeigen" + es_ES: "Mostrar tiempo del servidor y tiempo de actividad" + fr_FR: "Afficher l'heure et le temps de fonctionnement du serveur" + pl_PL: "Wyświetl czas serwera i czas działania" + +about.display-tor-logs: + en_US: "Display Tor logs" + de_DE: "Tor-Logs anzeigen" + es_ES: "Mostrar registros de Tor" + fr_FR: "Afficher les journaux Tor" + pl_PL: "Wyświetl logi Tora" + +about.display-tor-v3-onion-addresses: + en_US: "Display Tor v3 onion addresses" + de_DE: "Tor v3 Onion-Adressen anzeigen" + es_ES: "Mostrar direcciones onion Tor v3" + fr_FR: "Afficher les adresses onion Tor v3" + pl_PL: "Wyświetl adresy onion Tor v3" + +about.download-img: + en_US: "Download IMG file" + de_DE: "IMG-Datei herunterladen" + es_ES: "Descargar archivo IMG" + fr_FR: "Télécharger le fichier IMG" + pl_PL: "Pobierz plik IMG" + +about.download-iso: + en_US: "Download ISO file" + de_DE: "ISO-Datei herunterladen" + es_ES: "Descargar archivo ISO" + fr_FR: "Télécharger le fichier ISO" + pl_PL: "Pobierz plik ISO" + +about.download-s9pk: + en_US: "Download s9pk package" + de_DE: "s9pk-Paket herunterladen" + es_ES: "Descargar paquete s9pk" + fr_FR: "Télécharger le package s9pk" + pl_PL: "Pobierz pakiet s9pk" + +about.download-squashfs: + en_US: "Download squashfs file" + de_DE: "Squashfs-Datei herunterladen" + es_ES: "Descargar archivo squashfs" + fr_FR: "Télécharger le fichier squashfs" + pl_PL: "Pobierz plik squashfs" + +about.dump-address-resolution-table: + en_US: "Dump address resolution table" + de_DE: "Adressauflösungstabelle ausgeben" + es_ES: "Volcar tabla de resolución de direcciones" + fr_FR: "Exporter la table de résolution d'adresses" + pl_PL: "Zrzuć tabelę rozpoznawania adresów" + +about.echo-message: + en_US: "Echo a message back" + de_DE: "Eine Nachricht zurückgeben" + es_ES: "Devolver un mensaje" + fr_FR: "Renvoyer un message" + pl_PL: "Odzew wiadomości" + +about.edit-s9pk-manifest: + en_US: "Edit s9pk manifest" + de_DE: "s9pk-Manifest bearbeiten" + es_ES: "Editar manifiesto s9pk" + fr_FR: "Modifier le manifeste s9pk" + pl_PL: "Edytuj manifest s9pk" + +about.enable-disable-wifi: + en_US: "Enable or disable wifi" + de_DE: "WLAN aktivieren oder deaktivieren" + es_ES: "Activar o desactivar wifi" + fr_FR: "Activer ou désactiver le wifi" + pl_PL: "Włącz lub wyłącz wifi" + +about.enable-kiosk-mode: + en_US: "Enable kiosk mode" + de_DE: "Kioskmodus aktivieren" + es_ES: "Activar el modo kiosco" + fr_FR: "Activer le mode kiosque" + pl_PL: "Włącz tryb kiosku" + +about.enable-webserver: + en_US: "Enable the webserver" + de_DE: "Webserver aktivieren" + es_ES: "Activar el servidor web" + fr_FR: "Activer le serveur web" + pl_PL: "Włącz serwer internetowy" + +about.enable-zram: + en_US: "Enable ZRAM" + de_DE: "ZRAM aktivieren" + es_ES: "Activar ZRAM" + fr_FR: "Activer ZRAM" + pl_PL: "Włącz ZRAM" + +about.execute-commands-container: + en_US: "Execute commands in container" + de_DE: "Befehle im Container ausführen" + es_ES: "Ejecutar comandos en el contenedor" + fr_FR: "Exécuter des commandes dans le conteneur" + pl_PL: "Wykonaj polecenia w kontenerze" + +about.filter-query-db: + en_US: "Filter and query the database" + de_DE: "Datenbank filtern und abfragen" + es_ES: "Filtrar y consultar la base de datos" + fr_FR: "Filtrer et interroger la base de données" + pl_PL: "Filtruj i odpytuj bazę danych" + +about.filter-query-db-display-tables-records: + en_US: "Filter and query the database, display tables and records" + de_DE: "Datenbank filtern und abfragen, Tabellen und Einträge anzeigen" + es_ES: "Filtrar y consultar la base de datos, mostrar tablas y registros" + fr_FR: "Filtrer et interroger la base de données, afficher les tables et les enregistrements" + pl_PL: "Filtruj i odpytuj bazę danych, wyświetlaj tabele i rekordy" + +about.flash-startos: + en_US: "Flash StartOS to a drive" + de_DE: "StartOS auf ein Laufwerk flashen" + es_ES: "Flashear StartOS a una unidad" + fr_FR: "Flasher StartOS sur un lecteur" + pl_PL: "Flashuj StartOS na dysk" + +about.forget-disconnected-gateway: + en_US: "Forget a disconnected gateway" + de_DE: "Getrenntes Gateway vergessen" + es_ES: "Olvidar un gateway desconectado" + fr_FR: "Oublier une passerelle déconnectée" + pl_PL: "Zapomnij odłączoną bramę" + +about.generate-certificate-for-webserver: + en_US: "Generate a certificate for the webserver" + de_DE: "Zertifikat für den Webserver generieren" + es_ES: "Generar un certificado para el servidor web" + fr_FR: "Générer un certificat pour le serveur web" + pl_PL: "Wygeneruj certyfikat dla serwera internetowego" + +about.generate-onion-service-key-add-to-store: + en_US: "Generate an onion service key and add to store" + de_DE: "Onion-Service-Schlüssel generieren und zum Speicher hinzufügen" + es_ES: "Generar una clave de servicio onion y agregar al almacén" + fr_FR: "Générer une clé de service onion et ajouter au stockage" + pl_PL: "Wygeneruj klucz usługi onion i dodaj do magazynu" + +about.get-action-input-spec: + en_US: "Get action input specification" + de_DE: "Eingabespezifikation der Aktion abrufen" + es_ES: "Obtener especificación de entrada de la acción" + fr_FR: "Obtenir la spécification d'entrée de l'action" + pl_PL: "Pobierz specyfikację wejścia akcji" + +about.get-available-ip-addresses-to-bind: + en_US: "Get available IP addresses to bind" + de_DE: "Verfügbare IP-Adressen zum Binden abrufen" + es_ES: "Obtener direcciones IP disponibles para vincular" + fr_FR: "Obtenir les adresses IP disponibles à lier" + pl_PL: "Pobierz dostępne adresy IP do powiązania" + +about.get-certificate-for-webserver: + en_US: "Get the certificate for the webserver" + de_DE: "Zertifikat für den Webserver abrufen" + es_ES: "Obtener el certificado del servidor web" + fr_FR: "Obtenir le certificat du serveur web" + pl_PL: "Pobierz certyfikat dla serwera internetowego" + +about.get-developer-pubkey: + en_US: "Get the developer public key" + de_DE: "Öffentlichen Entwicklerschlüssel abrufen" + es_ES: "Obtener la clave pública del desarrollador" + fr_FR: "Obtenir la clé publique du développeur" + pl_PL: "Pobierz klucz publiczny dewelopera" + +about.get-initialization-progress: + en_US: "Get initialization progress" + de_DE: "Initialisierungsfortschritt abrufen" + es_ES: "Obtener progreso de inicialización" + fr_FR: "Obtenir la progression de l'initialisation" + pl_PL: "Pobierz postęp inicjalizacji" + +about.get-listen-address-for-webserver: + en_US: "Get the listen address for the webserver" + de_DE: "Adresse abrufen, auf der der Webserver lauscht" + es_ES: "Obtener la dirección de escucha del servidor web" + fr_FR: "Obtenir l'adresse d'écoute du serveur web" + pl_PL: "Pobierz adres nasłuchiwania serwera internetowego" + +about.get-os-versions-info: + en_US: "Get OS versions info" + de_DE: "Informationen zu Betriebssystemversionen abrufen" + es_ES: "Obtener información de versiones del sistema operativo" + fr_FR: "Obtenir les informations sur les versions du système d'exploitation" + pl_PL: "Pobierz informacje o wersjach systemu operacyjnego" + +about.get-pubkey-from-server: + en_US: "Get the public key from the server" + de_DE: "Öffentlichen Schlüssel vom Server abrufen" + es_ES: "Obtener la clave pública del servidor" + fr_FR: "Obtenir la clé publique du serveur" + pl_PL: "Pobierz klucz publiczny z serwera" + +about.import-certificate-for-webserver: + en_US: "Import a certificate for the webserver" + de_DE: "Zertifikat für den Webserver importieren" + es_ES: "Importar un certificado para el servidor web" + fr_FR: "Importer un certificat pour le serveur web" + pl_PL: "Importuj certyfikat dla serwera internetowego" + +about.indicate-gateway-inbound-access-from-wan: + en_US: "Indicate gateway inbound access from WAN" + de_DE: "Eingehenden Zugriff vom WAN zum Gateway anzeigen" + es_ES: "Indicar acceso entrante del gateway desde WAN" + fr_FR: "Indiquer l'accès entrant de la passerelle depuis le WAN" + pl_PL: "Wskaż dostęp przychodzący bramy z WAN" + +about.initialize-webserver: + en_US: "Initialize the webserver" + de_DE: "Webserver initialisieren" + es_ES: "Inicializar el servidor web" + fr_FR: "Initialiser le serveur web" + pl_PL: "Zainicjuj serwer internetowy" + +about.install-package: + en_US: "Install a package" + de_DE: "Ein Paket installieren" + es_ES: "Instalar un paquete" + fr_FR: "Installer un package" + pl_PL: "Zainstaluj pakiet" + +about.list-addresses-for-host: + en_US: "List addresses for a host" + de_DE: "Adressen für einen Host auflisten" + es_ES: "Listar direcciones de un host" + fr_FR: "Lister les adresses d'un hôte" + pl_PL: "Wyświetl adresy dla hosta" + +about.list-admin-signers: + en_US: "List admin signers" + de_DE: "Admin-Unterzeichner auflisten" + es_ES: "Listar firmantes de administrador" + fr_FR: "Lister les signataires administrateurs" + pl_PL: "Wyświetl sygnatariuszy administratorów" + +about.list-authorized-keys: + en_US: "List authorized keys" + de_DE: "Autorisierte Schlüssel auflisten" + es_ES: "Listar claves autorizadas" + fr_FR: "Lister les clés autorisées" + pl_PL: "Wyświetl autoryzowane klucze" + +about.list-available-wifi-networks: + en_US: "List available wifi networks" + de_DE: "Verfügbare WLAN-Netzwerke auflisten" + es_ES: "Listar redes wifi disponibles" + fr_FR: "Lister les réseaux wifi disponibles" + pl_PL: "Wyświetl dostępne sieci wifi" + +about.list-bindings-for-host: + en_US: "List bindings for a host" + de_DE: "Bindungen für einen Host auflisten" + es_ES: "Listar enlaces de un host" + fr_FR: "Lister les liaisons d'un hôte" + pl_PL: "Wyświetl powiązania dla hosta" + +about.list-devices-in-subnet: + en_US: "List devices in a subnet" + de_DE: "Geräte in einem Subnetz auflisten" + es_ES: "Listar dispositivos en una subred" + fr_FR: "Lister les appareils dans un sous-réseau" + pl_PL: "Wyświetl urządzenia w podsieci" + +about.list-disk-info: + en_US: "List disk information" + de_DE: "Festplatteninformationen auflisten" + es_ES: "Listar información del disco" + fr_FR: "Lister les informations du disque" + pl_PL: "Wyświetl informacje o dysku" + +about.list-existing-backup-targets: + en_US: "List existing backup targets" + de_DE: "Vorhandene Backup-Ziele auflisten" + es_ES: "Listar objetivos de copia de seguridad existentes" + fr_FR: "Lister les cibles de sauvegarde existantes" + pl_PL: "Wyświetl istniejące cele kopii zapasowych" + +about.list-host-ids-for-service: + en_US: "List host IDs for a service" + de_DE: "Host-IDs für einen Dienst auflisten" + es_ES: "Listar IDs de host para un servicio" + fr_FR: "Lister les IDs d'hôte pour un service" + pl_PL: "Wyświetl identyfikatory hostów dla usługi" + +about.list-installation-candidates: + en_US: "List installation candidates" + de_DE: "Installationskandidaten auflisten" + es_ES: "Listar candidatos de instalación" + fr_FR: "Lister les candidats à l'installation" + pl_PL: "Wyświetl kandydatów do instalacji" + +about.list-installed-packages: + en_US: "List installed packages" + de_DE: "Installierte Pakete auflisten" + es_ES: "Listar paquetes instalados" + fr_FR: "Lister les packages installés" + pl_PL: "Wyświetl zainstalowane pakiety" + +about.list-lxc-container-info: + en_US: "List LXC container information" + de_DE: "LXC-Container-Informationen auflisten" + es_ES: "Listar información del contenedor LXC" + fr_FR: "Lister les informations du conteneur LXC" + pl_PL: "Wyświetl informacje o kontenerze LXC" + +about.list-notifications: + en_US: "List notifications" + de_DE: "Benachrichtigungen auflisten" + es_ES: "Listar notificaciones" + fr_FR: "Lister les notifications" + pl_PL: "Wyświetl powiadomienia" + +about.list-onion-services-with-keys-in-store: + en_US: "List onion services with keys in store" + de_DE: "Onion-Dienste mit Schlüsseln im Speicher auflisten" + es_ES: "Listar servicios onion con claves en el almacén" + fr_FR: "Lister les services onion avec des clés en stockage" + pl_PL: "Wyświetl usługi onion z kluczami w magazynie" + +about.list-or-kill-auth-sessions: + en_US: "List or kill auth sessions" + de_DE: "Authentifizierungssitzungen auflisten oder beenden" + es_ES: "Listar o terminar sesiones de autenticación" + fr_FR: "Lister ou terminer les sessions d'authentification" + pl_PL: "Wyświetl lub zakończ sesje uwierzytelniania" + +about.list-os-versions-index: + en_US: "List OS versions index" + de_DE: "Index der Betriebssystemversionen auflisten" + es_ES: "Listar índice de versiones del sistema operativo" + fr_FR: "Lister l'index des versions du système d'exploitation" + pl_PL: "Wyświetl indeks wersji systemu operacyjnego" + +about.list-packages-categories: + en_US: "List packages and categories" + de_DE: "Pakete und Kategorien auflisten" + es_ES: "Listar paquetes y categorías" + fr_FR: "Lister les packages et catégories" + pl_PL: "Wyświetl pakiety i kategorie" + +about.list-package-signers: + en_US: "List package signers" + de_DE: "Paket-Unterzeichner auflisten" + es_ES: "Listar firmantes de paquetes" + fr_FR: "Lister les signataires de packages" + pl_PL: "Wyświetl sygnatariuszy pakietów" + +about.list-paths-of-package-ingredients: + en_US: "List paths of package ingredients" + de_DE: "Pfade der Paketbestandteile auflisten" + es_ES: "Listar rutas de los componentes del paquete" + fr_FR: "Lister les chemins des composants du package" + pl_PL: "Wyświetl ścieżki składników pakietu" + +about.list-registry-info-packages: + en_US: "List registry info and packages" + de_DE: "Registry-Informationen und Pakete auflisten" + es_ES: "Listar información del registro y paquetes" + fr_FR: "Lister les informations du registre et les packages" + pl_PL: "Wyświetl informacje o rejestrze i pakiety" + +about.list-signers: + en_US: "List signers" + de_DE: "Unterzeichner auflisten" + es_ES: "Listar firmantes" + fr_FR: "Lister les signataires" + pl_PL: "Wyświetl sygnatariuszy" + +about.list-ssh-keys: + en_US: "List SSH keys" + de_DE: "SSH-Schlüssel auflisten" + es_ES: "Listar claves SSH" + fr_FR: "Lister les clés SSH" + pl_PL: "Wyświetl klucze SSH" + +about.list-version-signers: + en_US: "List version signers" + de_DE: "Versions-Unterzeichner auflisten" + es_ES: "Listar firmantes de versiones" + fr_FR: "Lister les signataires de versions" + pl_PL: "Wyświetl sygnatariuszy wersji" + +about.list-wifi-info: + en_US: "List wifi information" + de_DE: "WLAN-Informationen auflisten" + es_ES: "Listar información wifi" + fr_FR: "Lister les informations wifi" + pl_PL: "Wyświetl informacje o wifi" + +about.login-new-auth-session: + en_US: "Login to a new auth session" + de_DE: "Bei einer neuen Authentifizierungssitzung anmelden" + es_ES: "Iniciar sesión en una nueva sesión de autenticación" + fr_FR: "Se connecter à une nouvelle session d'authentification" + pl_PL: "Zaloguj się do nowej sesji uwierzytelniania" + +about.logout-current-auth-session: + en_US: "Logout from current auth session" + de_DE: "Von aktueller Authentifizierungssitzung abmelden" + es_ES: "Cerrar sesión de la sesión de autenticación actual" + fr_FR: "Se déconnecter de la session d'authentification actuelle" + pl_PL: "Wyloguj się z bieżącej sesji uwierzytelniania" + +about.manage-network-hosts-package: + en_US: "Manage network hosts for a package" + de_DE: "Netzwerk-Hosts für ein Paket verwalten" + es_ES: "Gestionar hosts de red de un paquete" + fr_FR: "Gérer les hôtes réseau d'un package" + pl_PL: "Zarządzaj hostami sieciowymi dla pakietu" + +about.manage-onion-service-key-store: + en_US: "Manage onion service key store" + de_DE: "Onion-Service-Schlüsselspeicher verwalten" + es_ES: "Gestionar almacén de claves de servicio onion" + fr_FR: "Gérer le stockage des clés de service onion" + pl_PL: "Zarządzaj magazynem kluczy usługi onion" + +about.manage-port-forwards: + en_US: "Manage port forwards" + de_DE: "Portweiterleitungen verwalten" + es_ES: "Gestionar reenvíos de puertos" + fr_FR: "Gérer les redirections de ports" + pl_PL: "Zarządzaj przekierowaniami portów" + +about.manage-query-dns: + en_US: "Manage and query DNS" + de_DE: "DNS verwalten und abfragen" + es_ES: "Gestionar y consultar DNS" + fr_FR: "Gérer et interroger le DNS" + pl_PL: "Zarządzaj i odpytuj DNS" + +about.manage-ssl-vhost-proxy: + en_US: "Manage SSL vhost proxy" + de_DE: "SSL-vhost-Proxy verwalten" + es_ES: "Gestionar proxy SSL vhost" + fr_FR: "Gérer le proxy SSL vhost" + pl_PL: "Zarządzaj proxy SSL vhost" + +about.manage-tunnels: + en_US: "Manage tunnels" + de_DE: "Tunnel verwalten" + es_ES: "Gestionar túneles" + fr_FR: "Gérer les tunnels" + pl_PL: "Zarządzaj tunelami" + +about.mark-notifications-seen: + en_US: "Mark notifications as seen" + de_DE: "Benachrichtigungen als gelesen markieren" + es_ES: "Marcar notificaciones como vistas" + fr_FR: "Marquer les notifications comme vues" + pl_PL: "Oznacz powiadomienia jako przeczytane" + +about.mark-notifications-seen-before-id: + en_US: "Mark notifications as seen before a given ID" + de_DE: "Benachrichtigungen vor einer bestimmten ID als gelesen markieren" + es_ES: "Marcar notificaciones como vistas antes de un ID dado" + fr_FR: "Marquer les notifications comme vues avant un ID donné" + pl_PL: "Oznacz powiadomienia jako przeczytane przed danym ID" + +about.mark-notifications-unseen: + en_US: "Mark notifications as unseen" + de_DE: "Benachrichtigungen als ungelesen markieren" + es_ES: "Marcar notificaciones como no vistas" + fr_FR: "Marquer les notifications comme non vues" + pl_PL: "Oznacz powiadomienia jako nieprzeczytane" + +about.mount-backup-target: + en_US: "Mount a backup target" + de_DE: "Ein Backup-Ziel einbinden" + es_ES: "Montar un objetivo de copia de seguridad" + fr_FR: "Monter une cible de sauvegarde" + pl_PL: "Zamontuj cel kopii zapasowej" + +about.network-commands: + en_US: "Network commands" + de_DE: "Netzwerkbefehle" + es_ES: "Comandos de red" + fr_FR: "Commandes réseau" + pl_PL: "Polecenia sieciowe" + +about.package-s9pk-input-files-into-valid-s9pk: + en_US: "Package input files into a valid s9pk" + de_DE: "Eingabedateien in ein gültiges s9pk verpacken" + es_ES: "Empaquetar archivos de entrada en un s9pk válido" + fr_FR: "Emballer les fichiers d'entrée dans un s9pk valide" + pl_PL: "Spakuj pliki wejściowe do prawidłowego s9pk" + +about.persist-new-notification: + en_US: "Persist a new notification" + de_DE: "Neue Benachrichtigung speichern" + es_ES: "Persistir una nueva notificación" + fr_FR: "Persister une nouvelle notification" + pl_PL: "Utrwal nowe powiadomienie" + +about.rebuild-service-container: + en_US: "Rebuild service container" + de_DE: "Dienst-Container neu erstellen" + es_ES: "Reconstruir contenedor de servicio" + fr_FR: "Reconstruire le conteneur du service" + pl_PL: "Przebuduj kontener usługi" + +about.remove-acme-certificate-acquisition-configuration: + en_US: "Remove ACME certificate acquisition configuration" + de_DE: "ACME-Zertifikatsbeschaffungskonfiguration entfernen" + es_ES: "Eliminar configuración de adquisición de certificado ACME" + fr_FR: "Supprimer la configuration d'acquisition de certificat ACME" + pl_PL: "Usuń konfigurację pozyskiwania certyfikatu ACME" + +about.remove-address-from-host: + en_US: "Remove address from host" + de_DE: "Adresse vom Host entfernen" + es_ES: "Eliminar dirección del host" + fr_FR: "Supprimer l'adresse de l'hôte" + pl_PL: "Usuń adres z hosta" + +about.remove-admin-signer: + en_US: "Remove admin signer" + de_DE: "Admin-Unterzeichner entfernen" + es_ES: "Eliminar firmante de administrador" + fr_FR: "Supprimer le signataire administrateur" + pl_PL: "Usuń sygnatariusza administratora" + +about.remove-authorized-key: + en_US: "Remove authorized key" + de_DE: "Autorisierten Schlüssel entfernen" + es_ES: "Eliminar clave autorizada" + fr_FR: "Supprimer la clé autorisée" + pl_PL: "Usuń autoryzowany klucz" + +about.remove-category-registry: + en_US: "Remove category from registry" + de_DE: "Kategorie aus der Registry entfernen" + es_ES: "Eliminar categoría del registro" + fr_FR: "Supprimer la catégorie du registre" + pl_PL: "Usuń kategorię z rejestru" + +about.remove-device-from-subnet: + en_US: "Remove device from subnet" + de_DE: "Gerät aus dem Subnetz entfernen" + es_ES: "Eliminar dispositivo de la subred" + fr_FR: "Supprimer l'appareil du sous-réseau" + pl_PL: "Usuń urządzenie z podsieci" + +about.remove-disk-filesystem: + en_US: "Remove disk filesystem" + de_DE: "Festplatten-Dateisystem entfernen" + es_ES: "Eliminar sistema de archivos del disco" + fr_FR: "Supprimer le système de fichiers du disque" + pl_PL: "Usuń system plików dysku" + +about.remove-existing-backup-target: + en_US: "Remove existing backup target" + de_DE: "Vorhandenes Backup-Ziel entfernen" + es_ES: "Eliminar objetivo de copia de seguridad existente" + fr_FR: "Supprimer la cible de sauvegarde existante" + pl_PL: "Usuń istniejący cel kopii zapasowej" + +about.remove-mirror-package: + en_US: "Remove mirror package" + de_DE: "Mirror-Paket entfernen" + es_ES: "Eliminar paquete espejo" + fr_FR: "Supprimer le package miroir" + pl_PL: "Usuń pakiet lustrzany" + +about.remove-notification-for-ids: + en_US: "Remove notification for IDs" + de_DE: "Benachrichtigung für IDs entfernen" + es_ES: "Eliminar notificación por IDs" + fr_FR: "Supprimer la notification par IDs" + pl_PL: "Usuń powiadomienie dla identyfikatorów" + +about.remove-notifications-before-id: + en_US: "Remove notifications before a given ID" + de_DE: "Benachrichtigungen vor einer bestimmten ID entfernen" + es_ES: "Eliminar notificaciones antes de un ID dado" + fr_FR: "Supprimer les notifications avant un ID donné" + pl_PL: "Usuń powiadomienia przed danym ID" + +about.remove-os-version: + en_US: "Remove OS version" + de_DE: "Betriebssystemversion entfernen" + es_ES: "Eliminar versión del sistema operativo" + fr_FR: "Supprimer la version du système d'exploitation" + pl_PL: "Usuń wersję systemu operacyjnego" + +about.remove-package: + en_US: "Remove a package" + de_DE: "Ein Paket entfernen" + es_ES: "Eliminar un paquete" + fr_FR: "Supprimer un package" + pl_PL: "Usuń pakiet" + +about.remove-package-category: + en_US: "Remove package from category" + de_DE: "Paket aus der Kategorie entfernen" + es_ES: "Eliminar paquete de la categoría" + fr_FR: "Supprimer le package de la catégorie" + pl_PL: "Usuń pakiet z kategorii" + +about.remove-package-registry: + en_US: "Remove package from registry" + de_DE: "Paket aus der Registry entfernen" + es_ES: "Eliminar paquete del registro" + fr_FR: "Supprimer le package du registre" + pl_PL: "Usuń pakiet z rejestru" + +about.remove-package-signer: + en_US: "Remove package signer" + de_DE: "Paket-Unterzeichner entfernen" + es_ES: "Eliminar firmante de paquete" + fr_FR: "Supprimer le signataire du package" + pl_PL: "Usuń sygnatariusza pakietu" + +about.remove-port-forward: + en_US: "Remove port forward" + de_DE: "Portweiterleitung entfernen" + es_ES: "Eliminar reenvío de puerto" + fr_FR: "Supprimer la redirection de port" + pl_PL: "Usuń przekierowanie portu" + +about.remove-private-domain-from-host: + en_US: "Remove private domain from host" + de_DE: "Private Domain vom Host entfernen" + es_ES: "Eliminar dominio privado del host" + fr_FR: "Supprimer le domaine privé de l'hôte" + pl_PL: "Usuń prywatną domenę z hosta" + +about.remove-public-domain-from-host: + en_US: "Remove public domain from host" + de_DE: "Öffentliche Domain vom Host entfernen" + es_ES: "Eliminar dominio público del host" + fr_FR: "Supprimer le domaine public de l'hôte" + pl_PL: "Usuń publiczną domenę z hosta" + +about.remove-ssh-key: + en_US: "Remove an SSH key" + de_DE: "Einen SSH-Schlüssel entfernen" + es_ES: "Eliminar una clave SSH" + fr_FR: "Supprimer une clé SSH" + pl_PL: "Usuń klucz SSH" + +about.remove-subnet: + en_US: "Remove a subnet" + de_DE: "Ein Subnetz entfernen" + es_ES: "Eliminar una subred" + fr_FR: "Supprimer un sous-réseau" + pl_PL: "Usuń podsieć" + +about.remove-tunnel: + en_US: "Remove a tunnel" + de_DE: "Einen Tunnel entfernen" + es_ES: "Eliminar un túnel" + fr_FR: "Supprimer un tunnel" + pl_PL: "Usuń tunel" + +about.remove-version-signer: + en_US: "Remove version signer" + de_DE: "Versions-Unterzeichner entfernen" + es_ES: "Eliminar firmante de versión" + fr_FR: "Supprimer le signataire de version" + pl_PL: "Usuń sygnatariusza wersji" + +about.remove-wifi-network: + en_US: "Remove a wifi network" + de_DE: "Ein WLAN-Netzwerk entfernen" + es_ES: "Eliminar una red wifi" + fr_FR: "Supprimer un réseau wifi" + pl_PL: "Usuń sieć wifi" + +about.rename-gateway: + en_US: "Rename a gateway" + de_DE: "Ein Gateway umbenennen" + es_ES: "Renombrar un gateway" + fr_FR: "Renommer une passerelle" + pl_PL: "Zmień nazwę bramy" + +about.repair-disk-corruption: + en_US: "Repair disk corruption" + de_DE: "Festplattenfehler reparieren" + es_ES: "Reparar corrupción del disco" + fr_FR: "Réparer la corruption du disque" + pl_PL: "Napraw uszkodzenie dysku" + +about.reset-password: + en_US: "Reset the password" + de_DE: "Das Passwort zurücksetzen" + es_ES: "Restablecer la contraseña" + fr_FR: "Réinitialiser le mot de passe" + pl_PL: "Zresetuj hasło" + +about.reset-tor-daemon: + en_US: "Reset Tor daemon" + de_DE: "Tor-Daemon zurücksetzen" + es_ES: "Restablecer demonio Tor" + fr_FR: "Réinitialiser le démon Tor" + pl_PL: "Zresetuj demona Tora" + +about.reset-user-interface-password: + en_US: "Reset user interface password" + de_DE: "Passwort der Benutzeroberfläche zurücksetzen" + es_ES: "Restablecer contraseña de la interfaz de usuario" + fr_FR: "Réinitialiser le mot de passe de l'interface utilisateur" + pl_PL: "Zresetuj hasło interfejsu użytkownika" + +about.reset-webserver: + en_US: "Reset the webserver" + de_DE: "Den Webserver zurücksetzen" + es_ES: "Restablecer el servidor web" + fr_FR: "Réinitialiser le serveur web" + pl_PL: "Zresetuj serwer internetowy" + +about.restart-server: + en_US: "Restart the server" + de_DE: "Den Server neu starten" + es_ES: "Reiniciar el servidor" + fr_FR: "Redémarrer le serveur" + pl_PL: "Uruchom ponownie serwer" + +about.restart-service: + en_US: "Restart a service" + de_DE: "Einen Dienst neu starten" + es_ES: "Reiniciar un servicio" + fr_FR: "Redémarrer un service" + pl_PL: "Uruchom ponownie usługę" + +about.restore-packages-from-backup: + en_US: "Restore packages from backup" + de_DE: "Pakete aus Backup wiederherstellen" + es_ES: "Restaurar paquetes desde copia de seguridad" + fr_FR: "Restaurer les packages depuis la sauvegarde" + pl_PL: "Przywróć pakiety z kopii zapasowej" + +about.run-service-action: + en_US: "Run a service action" + de_DE: "Eine Dienstaktion ausführen" + es_ES: "Ejecutar una acción de servicio" + fr_FR: "Exécuter une action de service" + pl_PL: "Uruchom akcję usługi" + +about.set-country: + en_US: "Set the country" + de_DE: "Das Land festlegen" + es_ES: "Establecer el país" + fr_FR: "Définir le pays" + pl_PL: "Ustaw kraj" + +about.set-gateway-enabled-for-binding: + en_US: "Set gateway enabled for binding" + de_DE: "Gateway für Bindung aktivieren" + es_ES: "Establecer gateway habilitado para enlace" + fr_FR: "Définir la passerelle activée pour la liaison" + pl_PL: "Ustaw bramę jako włączoną dla powiązania" + +about.set-listen-address-for-webserver: + en_US: "Set the listen address for the webserver" + de_DE: "Adresse festlegen, auf der der Webserver lauscht" + es_ES: "Establecer la dirección de escucha del servidor web" + fr_FR: "Définir l'adresse d'écoute du serveur web" + pl_PL: "Ustaw adres nasłuchiwania serwera internetowego" + +about.set-registry-icon: + en_US: "Set the registry icon" + de_DE: "Das Registry-Symbol festlegen" + es_ES: "Establecer el icono del registro" + fr_FR: "Définir l'icône du registre" + pl_PL: "Ustaw ikonę rejestru" + +about.set-registry-name: + en_US: "Set the registry name" + de_DE: "Den Registry-Namen festlegen" + es_ES: "Establecer el nombre del registro" + fr_FR: "Définir le nom du registre" + pl_PL: "Ustaw nazwę rejestru" + +about.set-smtp: + en_US: "Set SMTP configuration" + de_DE: "SMTP-Konfiguration festlegen" + es_ES: "Establecer configuración SMTP" + fr_FR: "Définir la configuration SMTP" + pl_PL: "Ustaw konfigurację SMTP" + +about.set-static-dns-servers: + en_US: "Set static DNS servers" + de_DE: "Statische DNS-Server festlegen" + es_ES: "Establecer servidores DNS estáticos" + fr_FR: "Définir les serveurs DNS statiques" + pl_PL: "Ustaw statyczne serwery DNS" + +about.set-user-interface-password: + en_US: "Set user interface password" + de_DE: "Passwort der Benutzeroberfläche festlegen" + es_ES: "Establecer contraseña de la interfaz de usuario" + fr_FR: "Définir le mot de passe de l'interface utilisateur" + pl_PL: "Ustaw hasło interfejsu użytkownika" + +about.setup-acme-certificate: + en_US: "Setup ACME certificate" + de_DE: "ACME-Zertifikat einrichten" + es_ES: "Configurar certificado ACME" + fr_FR: "Configurer le certificat ACME" + pl_PL: "Skonfiguruj certyfikat ACME" + +about.setup-acme-certificate-acquisition: + en_US: "Setup ACME certificate acquisition" + de_DE: "ACME-Zertifikatsbeschaffung einrichten" + es_ES: "Configurar adquisición de certificado ACME" + fr_FR: "Configurer l'acquisition de certificat ACME" + pl_PL: "Skonfiguruj pozyskiwanie certyfikatu ACME" + +about.show-cpu-governors: + en_US: "Show CPU governors" + de_DE: "CPU-Governors anzeigen" + es_ES: "Mostrar gobernadores de CPU" + fr_FR: "Afficher les gouverneurs CPU" + pl_PL: "Pokaż zarządców CPU" + +about.show-gateways-startos-can-listen-on: + en_US: "Show gateways StartOS can listen on" + de_DE: "Gateways anzeigen, auf denen StartOS lauschen kann" + es_ES: "Mostrar gateways en los que StartOS puede escuchar" + fr_FR: "Afficher les passerelles sur lesquelles StartOS peut écouter" + pl_PL: "Pokaż bramy, na których StartOS może nasłuchiwać" + +about.show-wireguard-configuration-for-device: + en_US: "Show WireGuard configuration for device" + de_DE: "WireGuard-Konfiguration für Gerät anzeigen" + es_ES: "Mostrar configuración de WireGuard para el dispositivo" + fr_FR: "Afficher la configuration WireGuard pour l'appareil" + pl_PL: "Pokaż konfigurację WireGuard dla urządzenia" + +about.shutdown-server: + en_US: "Shutdown the server" + de_DE: "Den Server herunterfahren" + es_ES: "Apagar el servidor" + fr_FR: "Arrêter le serveur" + pl_PL: "Wyłącz serwer" + +about.sign-file-add-registry: + en_US: "Sign file and add to registry" + de_DE: "Datei signieren und zur Registry hinzufügen" + es_ES: "Firmar archivo y agregar al registro" + fr_FR: "Signer le fichier et ajouter au registre" + pl_PL: "Podpisz plik i dodaj do rejestru" + +about.start-service: + en_US: "Start a service" + de_DE: "Einen Dienst starten" + es_ES: "Iniciar un servicio" + fr_FR: "Démarrer un service" + pl_PL: "Uruchom usługę" + +about.stop-service: + en_US: "Stop a service" + de_DE: "Einen Dienst stoppen" + es_ES: "Detener un servicio" + fr_FR: "Arrêter un service" + pl_PL: "Zatrzymaj usługę" + +about.teardown-rebuild-containers: + en_US: "Teardown and rebuild containers" + de_DE: "Container abbauen und neu erstellen" + es_ES: "Desmontar y reconstruir contenedores" + fr_FR: "Démonter et reconstruire les conteneurs" + pl_PL: "Rozmontuj i przebuduj kontenery" + +about.terminate-auth-sessions: + en_US: "Terminate auth sessions" + de_DE: "Authentifizierungssitzungen beenden" + es_ES: "Terminar sesiones de autenticación" + fr_FR: "Terminer les sessions d'authentification" + pl_PL: "Zakończ sesje uwierzytelniania" + +about.test-dns-configuration-for-domain: + en_US: "Test DNS configuration for a domain" + de_DE: "DNS-Konfiguration für eine Domain testen" + es_ES: "Probar configuración DNS para un dominio" + fr_FR: "Tester la configuration DNS pour un domaine" + pl_PL: "Przetestuj konfigurację DNS dla domeny" + +about.test-smtp: + en_US: "Test SMTP configuration" + de_DE: "SMTP-Konfiguration testen" + es_ES: "Probar configuración SMTP" + fr_FR: "Tester la configuration SMTP" + pl_PL: "Przetestuj konfigurację SMTP" + +about.tor-commands: + en_US: "Tor commands" + de_DE: "Tor-Befehle" + es_ES: "Comandos de Tor" + fr_FR: "Commandes Tor" + pl_PL: "Polecenia Tora" + +about.unmount-backup-target: + en_US: "Unmount a backup target" + de_DE: "Ein Backup-Ziel aushängen" + es_ES: "Desmontar un objetivo de copia de seguridad" + fr_FR: "Démonter une cible de sauvegarde" + pl_PL: "Odmontuj cel kopii zapasowej" + +about.update-categories-registry: + en_US: "Update categories in registry" + de_DE: "Kategorien in der Registry aktualisieren" + es_ES: "Actualizar categorías en el registro" + fr_FR: "Mettre à jour les catégories dans le registre" + pl_PL: "Zaktualizuj kategorie w rejestrze" + +about.update-db-record: + en_US: "Update a database record" + de_DE: "Einen Datenbankeintrag aktualisieren" + es_ES: "Actualizar un registro de la base de datos" + fr_FR: "Mettre à jour un enregistrement de la base de données" + pl_PL: "Zaktualizuj rekord bazy danych" + +about.update-existing-backup-target: + en_US: "Update an existing backup target" + de_DE: "Ein vorhandenes Backup-Ziel aktualisieren" + es_ES: "Actualizar un objetivo de copia de seguridad existente" + fr_FR: "Mettre à jour une cible de sauvegarde existante" + pl_PL: "Zaktualizuj istniejący cel kopii zapasowej" + +about.update-firmware: + en_US: "Update firmware" + de_DE: "Firmware aktualisieren" + es_ES: "Actualizar firmware" + fr_FR: "Mettre à jour le firmware" + pl_PL: "Zaktualizuj oprogramowanie układowe" + +about.view-edit-gateway-configs: + en_US: "View and edit gateway configurations" + de_DE: "Gateway-Konfigurationen anzeigen und bearbeiten" + es_ES: "Ver y editar configuraciones de gateway" + fr_FR: "Voir et modifier les configurations de passerelle" + pl_PL: "Wyświetl i edytuj konfiguracje bramy" diff --git a/core/src/action.rs b/core/src/action.rs index 50e610e7c..27ccdaf77 100644 --- a/core/src/action.rs +++ b/core/src/action.rs @@ -23,7 +23,7 @@ pub fn action_api() -> ParentHandler { "get-input", from_fn_async(get_action_input) .with_display_serializable() - .with_about("Get action input spec") + .with_about("about.get-action-input-spec") .with_call_remote::(), ) .subcommand( @@ -36,14 +36,14 @@ pub fn action_api() -> ParentHandler { } Ok(()) }) - .with_about("Run service action") + .with_about("about.run-service-action") .with_call_remote::(), ) .subcommand( "clear-task", from_fn_async(clear_task) .no_display() - .with_about("Clear a service task") + .with_about("about.clear-service-task") .with_call_remote::(), ) } diff --git a/core/src/auth.rs b/core/src/auth.rs index 0f590bbe1..c0fa18fc7 100644 --- a/core/src/auth.rs +++ b/core/src/auth.rs @@ -51,7 +51,10 @@ pub async fn write_shadow(password: &str) -> Result<(), Error> { match line.split_once(":") { Some((user, rest)) if user == "start9" || user == "kiosk" => { let (_, rest) = rest.split_once(":").ok_or_else(|| { - Error::new(eyre!("malformed /etc/shadow"), ErrorKind::ParseSysInfo) + Error::new( + eyre!("{}", t!("auth.malformed-etc-shadow")), + ErrorKind::ParseSysInfo, + ) })?; shadow_file .write_all(format!("{user}:{hash}:{rest}\n").as_bytes()) @@ -81,7 +84,7 @@ impl PasswordType { PasswordType::String(x) => Ok(x), PasswordType::EncryptedWire(x) => x.decrypt(current_secret).ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("Couldn't decode password"), + color_eyre::eyre::eyre!("{}", t!("auth.couldnt-decode-password")), crate::ErrorKind::Unknown, ) }), @@ -125,19 +128,19 @@ where "login", from_fn_async(cli_login::) .no_display() - .with_about("Log in a new auth session"), + .with_about("about.login-new-auth-session"), ) .subcommand( "logout", from_fn_async(logout::) .with_metadata("get_session", Value::Bool(true)) .no_display() - .with_about("Log out of current auth session") + .with_about("about.logout-current-auth-session") .with_call_remote::(), ) .subcommand( "session", - session::().with_about("List or kill auth sessions"), + session::().with_about("about.list-or-kill-auth-sessions"), ) .subcommand( "reset-password", @@ -147,14 +150,14 @@ where "reset-password", from_fn_async(cli_reset_password) .no_display() - .with_about("Reset password"), + .with_about("about.reset-password"), ) .subcommand( "get-pubkey", from_fn_async(get_pubkey) .with_metadata("authenticated", Value::Bool(false)) .no_display() - .with_about("Get public key derived from server private key") + .with_about("about.get-pubkey-from-server") .with_call_remote::(), ) } @@ -208,12 +211,12 @@ pub fn check_password(hash: &str, password: &str) -> Result<(), Error> { ensure_code!( argon2::verify_encoded(&hash, password.as_bytes()).map_err(|_| { Error::new( - eyre!("Password Incorrect"), + eyre!("{}", t!("auth.password-incorrect")), crate::ErrorKind::IncorrectPassword, ) })?, crate::ErrorKind::IncorrectPassword, - "Password Incorrect" + t!("auth.password-incorrect") ); Ok(()) } @@ -327,14 +330,14 @@ where .with_metadata("get_session", Value::Bool(true)) .with_display_serializable() .with_custom_display_fn(|handle, result| display_sessions(handle.params, result)) - .with_about("Display all auth sessions") + .with_about("about.display-all-auth-sessions") .with_call_remote::(), ) .subcommand( "kill", from_fn_async(kill::) .no_display() - .with_about("Terminate existing auth session(s)") + .with_about("about.terminate-auth-sessions") .with_call_remote::(), ) } @@ -447,13 +450,13 @@ async fn cli_reset_password( .. }: HandlerArgs, ) -> Result<(), RpcError> { - let old_password = rpassword::prompt_password("Current Password: ")?; + let old_password = rpassword::prompt_password(&t!("auth.prompt-current-password"))?; let new_password = { - let new_password = rpassword::prompt_password("New Password: ")?; - if new_password != rpassword::prompt_password("Confirm: ")? { + let new_password = rpassword::prompt_password(&t!("auth.prompt-new-password"))?; + if new_password != rpassword::prompt_password(&t!("auth.prompt-confirm"))? { return Err(Error::new( - eyre!("Passwords do not match"), + eyre!("{}", t!("auth.passwords-do-not-match")), crate::ErrorKind::IncorrectPassword, ) .into()); @@ -486,7 +489,7 @@ pub async fn reset_password_impl( .with_kind(crate::ErrorKind::IncorrectPassword)? { return Err(Error::new( - eyre!("Incorrect Password"), + eyre!("{}", t!("auth.password-incorrect")), crate::ErrorKind::IncorrectPassword, )); } diff --git a/core/src/backup/backup_bulk.rs b/core/src/backup/backup_bulk.rs index 0de39f34c..6affe1dfd 100644 --- a/core/src/backup/backup_bulk.rs +++ b/core/src/backup/backup_bulk.rs @@ -69,8 +69,8 @@ impl BackupStatusGuard { db, None, NotificationLevel::Success, - "Backup Complete".to_owned(), - "Your backup has completed".to_owned(), + t!("backup.bulk.complete-title").to_string(), + t!("backup.bulk.complete-message").to_string(), BackupReport { server: ServerBackupReport { attempted: true, @@ -88,9 +88,8 @@ impl BackupStatusGuard { db, None, NotificationLevel::Warning, - "Backup Complete".to_owned(), - "Your backup has completed, but some package(s) failed to backup" - .to_owned(), + t!("backup.bulk.complete-title").to_string(), + t!("backup.bulk.complete-with-failures").to_string(), BackupReport { server: ServerBackupReport { attempted: true, @@ -103,7 +102,7 @@ impl BackupStatusGuard { .await } Err(e) => { - tracing::error!("Backup Failed: {}", e); + tracing::error!("{}", t!("backup.bulk.failed-error", error = e)); tracing::debug!("{:?}", e); let err_string = e.to_string(); db.mutate(|db| { @@ -111,8 +110,8 @@ impl BackupStatusGuard { db, None, NotificationLevel::Error, - "Backup Failed".to_owned(), - "Your backup failed to complete.".to_owned(), + t!("backup.bulk.failed-title").to_string(), + t!("backup.bulk.failed-message").to_string(), BackupReport { server: ServerBackupReport { attempted: true, @@ -224,7 +223,7 @@ fn assure_backing_up<'a>( .as_backup_progress_mut(); if backing_up.transpose_ref().is_some() { return Err(Error::new( - eyre!("Server is already backing up!"), + eyre!("{}", t!("backup.bulk.already-backing-up")), ErrorKind::InvalidRequest, )); } @@ -303,7 +302,7 @@ async fn perform_backup( let mut backup_guard = Arc::try_unwrap(backup_guard).map_err(|_| { Error::new( - eyre!("leaked reference to BackupMountGuard"), + eyre!("{}", t!("backup.bulk.leaked-reference")), ErrorKind::Incoherent, ) })?; diff --git a/core/src/backup/mod.rs b/core/src/backup/mod.rs index a6e98b2b4..3e231afe2 100644 --- a/core/src/backup/mod.rs +++ b/core/src/backup/mod.rs @@ -37,12 +37,12 @@ pub fn backup() -> ParentHandler { "create", from_fn_async(backup_bulk::backup_all) .no_display() - .with_about("Create backup for all packages") + .with_about("about.create-backup-all-packages") .with_call_remote::(), ) .subcommand( "target", - target::target::().with_about("Commands related to a backup target"), + target::target::().with_about("about.commands-backup-target"), ) } @@ -51,7 +51,7 @@ pub fn package_backup() -> ParentHandler { "restore", from_fn_async(restore::restore_packages_rpc) .no_display() - .with_about("Restore package(s) from backup") + .with_about("about.restore-packages-from-backup") .with_call_remote::(), ) } diff --git a/core/src/backup/restore.rs b/core/src/backup/restore.rs index 17060db05..f6c23cb06 100644 --- a/core/src/backup/restore.rs +++ b/core/src/backup/restore.rs @@ -63,7 +63,7 @@ pub async fn restore_packages_rpc( match async { res.await?.await }.await { Ok(_) => (), Err(err) => { - tracing::error!("Error restoring package {}: {}", id, err); + tracing::error!("{}", t!("backup.restore.package-error", id = id, error = err)); tracing::debug!("{:?}", err); } } @@ -147,7 +147,7 @@ pub async fn recover_full_server( match async { res.await?.await }.await { Ok(_) => (), Err(err) => { - tracing::error!("Error restoring package {}: {}", id, err); + tracing::error!("{}", t!("backup.restore.package-error", id = id, error = err)); tracing::debug!("{:?}", err); } } diff --git a/core/src/backup/target/cifs.rs b/core/src/backup/target/cifs.rs index 350e53220..f365229a4 100644 --- a/core/src/backup/target/cifs.rs +++ b/core/src/backup/target/cifs.rs @@ -52,21 +52,21 @@ pub fn cifs() -> ParentHandler { "add", from_fn_async(add) .no_display() - .with_about("Add a new backup target") + .with_about("about.add-new-backup-target") .with_call_remote::(), ) .subcommand( "update", from_fn_async(update) .no_display() - .with_about("Update an existing backup target") + .with_about("about.update-existing-backup-target") .with_call_remote::(), ) .subcommand( "remove", from_fn_async(remove) .no_display() - .with_about("Remove an existing backup target") + .with_about("about.remove-existing-backup-target") .with_call_remote::(), ) } @@ -151,7 +151,7 @@ pub async fn update( id } else { return Err(Error::new( - eyre!("Backup Target ID {} Not Found", id), + eyre!("{}", t!("backup.target.cifs.target-not-found", id = id)), ErrorKind::NotFound, )); }; @@ -171,7 +171,7 @@ pub async fn update( .as_idx_mut(&id) .ok_or_else(|| { Error::new( - eyre!("Backup Target ID {} Not Found", BackupTargetId::Cifs { id }), + eyre!("{}", t!("backup.target.cifs.target-not-found", id = BackupTargetId::Cifs { id })), ErrorKind::NotFound, ) })? @@ -203,7 +203,7 @@ pub async fn remove(ctx: RpcContext, RemoveParams { id }: RemoveParams) -> Resul id } else { return Err(Error::new( - eyre!("Backup Target ID {} Not Found", id), + eyre!("{}", t!("backup.target.cifs.target-not-found", id = id)), ErrorKind::NotFound, )); }; @@ -220,7 +220,7 @@ pub fn load(db: &DatabaseModel, id: u32) -> Result { .as_idx(&id) .ok_or_else(|| { Error::new( - eyre!("Backup Target ID {} Not Found", id), + eyre!("{}", t!("backup.target.cifs.target-not-found-id", id = id)), ErrorKind::NotFound, ) })? diff --git a/core/src/backup/target/mod.rs b/core/src/backup/target/mod.rs index 616cffd3b..4c8f7095a 100644 --- a/core/src/backup/target/mod.rs +++ b/core/src/backup/target/mod.rs @@ -143,13 +143,13 @@ pub fn target() -> ParentHandler { ParentHandler::new() .subcommand( "cifs", - cifs::cifs::().with_about("Add, remove, or update a backup target"), + cifs::cifs::().with_about("about.add-remove-update-backup-target"), ) .subcommand( "list", from_fn_async(list) .with_display_serializable() - .with_about("List existing backup targets") + .with_about("about.list-existing-backup-targets") .with_call_remote::(), ) .subcommand( @@ -159,20 +159,20 @@ pub fn target() -> ParentHandler { .with_custom_display_fn::(|params, info| { display_backup_info(params.params, info) }) - .with_about("Display package backup information") + .with_about("about.display-package-backup-information") .with_call_remote::(), ) .subcommand( "mount", from_fn_async(mount) - .with_about("Mount backup target") + .with_about("about.mount-backup-target") .with_call_remote::(), ) .subcommand( "umount", from_fn_async(umount) .no_display() - .with_about("Unmount backup target") + .with_about("about.unmount-backup-target") .with_call_remote::(), ) } diff --git a/core/src/bins/container_cli.rs b/core/src/bins/container_cli.rs index 118133f55..a03204107 100644 --- a/core/src/bins/container_cli.rs +++ b/core/src/bins/container_cli.rs @@ -17,6 +17,7 @@ pub fn main(args: impl IntoIterator) { |cfg: ContainerClientConfig| Ok(ContainerCliContext::init(cfg)), crate::service::effects::handler(), ) + .mutate_command(super::translate_cli) .run(args) { match e.data { diff --git a/core/src/bins/deprecated.rs b/core/src/bins/deprecated.rs index 13e0290db..77eb9e62e 100644 --- a/core/src/bins/deprecated.rs +++ b/core/src/bins/deprecated.rs @@ -1,9 +1,14 @@ +use rust_i18n::t; + pub fn renamed(old: &str, new: &str) -> ! { - eprintln!("{old} has been renamed to {new}"); + eprintln!( + "{}", + t!("bins.deprecated.renamed", old = old, new = new) + ); std::process::exit(1) } pub fn removed(name: &str) -> ! { - eprintln!("{name} has been removed"); + eprintln!("{}", t!("bins.deprecated.removed", name = name)); std::process::exit(1) } diff --git a/core/src/bins/mod.rs b/core/src/bins/mod.rs index de809e093..dd61ee2b9 100644 --- a/core/src/bins/mod.rs +++ b/core/src/bins/mod.rs @@ -2,6 +2,8 @@ use std::collections::{BTreeMap, VecDeque}; use std::ffi::OsString; use std::path::Path; +use rust_i18n::t; + pub mod container_cli; pub mod deprecated; pub mod registry; @@ -10,6 +12,72 @@ pub mod start_init; pub mod startd; pub mod tunnel; +pub fn set_locale() { + let lang = std::env::var("LANG").ok(); + let lang = lang + .as_deref() + .map_or("C", |l| l.strip_suffix(".UTF-8").unwrap_or(l)); + let mut set_lang = lang; + for l in rust_i18n::available_locales!() { + if l == lang { + set_lang = l; + break; + } + if l.split("_").next().unwrap() == lang.split("_").next().unwrap() { + set_lang = l; + } + } + rust_i18n::set_locale(set_lang); +} + +pub fn translate_cli(mut cmd: clap::Command) -> clap::Command { + fn translate(s: impl std::fmt::Display) -> String { + t!(s.to_string()).into_owned() + } + if let Some(s) = cmd.get_about() { + let s = translate(s); + cmd = cmd.about(s); + } + if let Some(s) = cmd.get_long_about() { + let s = translate(s); + cmd = cmd.long_about(s); + } + if let Some(s) = cmd.get_before_help() { + let s = translate(s); + cmd = cmd.before_help(s); + } + if let Some(s) = cmd.get_before_long_help() { + let s = translate(s); + cmd = cmd.before_long_help(s); + } + if let Some(s) = cmd.get_after_help() { + let s = translate(s); + cmd = cmd.after_help(s); + } + if let Some(s) = cmd.get_after_long_help() { + let s = translate(s); + cmd = cmd.after_long_help(s); + } + + let args = cmd.get_arguments().cloned().collect::>(); + for mut arg in args { + if let Some(s) = arg.get_help() { + let s = translate(s); + arg = arg.help(s); + } + if let Some(s) = arg.get_long_help() { + let s = translate(s); + arg = arg.long_help(s); + } + cmd = cmd.arg(arg); + } + for cmd in cmd.get_subcommands_mut() { + *cmd = translate_cli(cmd.clone()); + } + + cmd +} + #[derive(Default)] pub struct MultiExecutable { default: Option<&'static str>, @@ -58,7 +126,7 @@ impl MultiExecutable { if let Some((name, _)) = self.bins.get_key_value(name) { self.default = Some(*name); } else { - panic!("{name} does not exist in MultiExecutable"); + panic!("{}", t!("bins.mod.does-not-exist", name = name)); } self } @@ -68,6 +136,8 @@ impl MultiExecutable { } pub fn execute(&self) { + set_locale(); + let mut popped = Vec::with_capacity(2); let mut args = std::env::args_os().collect::>(); @@ -96,11 +166,15 @@ impl MultiExecutable { } let args = std::env::args().collect::>(); eprintln!( - "unknown executable: {}", - args.get(1) - .or_else(|| args.get(0)) - .map(|s| s.as_str()) - .unwrap_or("N/A") + "{}", + t!( + "bins.mod.unknown-executable", + name = args + .get(1) + .or_else(|| args.get(0)) + .map(|s| s.as_str()) + .unwrap_or("N/A") + ) ); std::process::exit(1); } diff --git a/core/src/bins/registry.rs b/core/src/bins/registry.rs index cee20adc8..13d0c54c2 100644 --- a/core/src/bins/registry.rs +++ b/core/src/bins/registry.rs @@ -3,6 +3,7 @@ use std::ffi::OsString; use clap::Parser; use futures::FutureExt; use rpc_toolkit::CliApp; +use rust_i18n::t; use tokio::signal::unix::signal; use tracing::instrument; @@ -77,7 +78,7 @@ pub fn main(args: impl IntoIterator) { let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() - .expect("failed to initialize runtime"); + .expect(&t!("bins.registry.failed-to-initialize-runtime")); rt.block_on(inner_main(&config)) }; @@ -99,6 +100,7 @@ pub fn cli(args: impl IntoIterator) { |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), crate::registry::registry_api(), ) + .mutate_command(super::translate_cli) .run(args) { match e.data { diff --git a/core/src/bins/start_cli.rs b/core/src/bins/start_cli.rs index 647e509d4..e1d737be4 100644 --- a/core/src/bins/start_cli.rs +++ b/core/src/bins/start_cli.rs @@ -19,6 +19,7 @@ pub fn main(args: impl IntoIterator) { |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), crate::main_api(), ) + .mutate_command(super::translate_cli) .run(args) { match e.data { diff --git a/core/src/bins/start_init.rs b/core/src/bins/start_init.rs index fe2d61e79..48e65f5af 100644 --- a/core/src/bins/start_init.rs +++ b/core/src/bins/start_init.rs @@ -25,7 +25,13 @@ async fn setup_or_init( if let Some(firmware) = check_for_firmware_update() .await .map_err(|e| { - tracing::warn!("Error checking for firmware update: {e}"); + tracing::warn!( + "{}", + t!( + "bins.start-init.error-checking-firmware", + error = e.to_string() + ) + ); tracing::debug!("{e:?}"); }) .ok() @@ -33,14 +39,21 @@ async fn setup_or_init( { let init_ctx = InitContext::init(config).await?; let handle = &init_ctx.progress; - let mut update_phase = handle.add_phase("Updating Firmware".into(), Some(10)); - let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1)); + let mut update_phase = + handle.add_phase(t!("bins.start-init.updating-firmware").into(), Some(10)); + let mut reboot_phase = handle.add_phase(t!("bins.start-init.rebooting").into(), Some(1)); server.serve_ui_for(init_ctx); update_phase.start(); if let Err(e) = update_firmware(firmware).await { - tracing::warn!("Error performing firmware update: {e}"); + tracing::warn!( + "{}", + t!( + "bins.start-init.error-firmware-update", + error = e.to_string() + ) + ); tracing::debug!("{e:?}"); } else { update_phase.complete(); @@ -96,7 +109,13 @@ async fn setup_or_init( .invoke(ErrorKind::NotFound) .await { - tracing::error!("Failed to kill kiosk: {}", e); + tracing::error!( + "{}", + t!( + "bins.start-init.failed-to-kill-kiosk", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } @@ -105,7 +124,7 @@ async fn setup_or_init( Some(Err(e)) => return Err(e.clone_output()), None => { return Err(Error::new( - eyre!("Setup mode exited before setup completed"), + eyre!("{}", t!("bins.start-init.setup-mode-exited")), ErrorKind::Unknown, )); } @@ -115,7 +134,8 @@ async fn setup_or_init( let handle = init_ctx.progress.clone(); let err_channel = init_ctx.error.clone(); - let mut disk_phase = handle.add_phase("Opening data drive".into(), Some(10)); + let mut disk_phase = + handle.add_phase(t!("bins.start-init.opening-data-drive").into(), Some(10)); let init_phases = InitPhases::new(&handle); let rpc_ctx_phases = InitRpcContextPhases::new(&handle); @@ -147,11 +167,12 @@ async fn setup_or_init( .with_ctx(|_| (crate::ErrorKind::Filesystem, REPAIR_DISK_PATH))?; } disk_phase.complete(); - tracing::info!("Loaded Disk"); + tracing::info!("{}", t!("bins.start-init.loaded-disk")); if requires_reboot.0 { - tracing::info!("Rebooting..."); - let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1)); + tracing::info!("{}", t!("bins.start-init.rebooting")); + let mut reboot_phase = + handle.add_phase(t!("bins.start-init.rebooting").into(), Some(1)); reboot_phase.start(); return Ok(Err(Shutdown { disk_guid: Some(disk_guid), diff --git a/core/src/bins/startd.rs b/core/src/bins/startd.rs index a06162a0d..9cc8b310e 100644 --- a/core/src/bins/startd.rs +++ b/core/src/bins/startd.rs @@ -4,6 +4,7 @@ use std::time::Duration; use clap::Parser; use color_eyre::eyre::eyre; +use rust_i18n::t; use futures::{FutureExt, TryFutureExt}; use tokio::signal::unix::signal; use tracing::instrument; @@ -112,11 +113,11 @@ async fn inner_main( metrics_task .map_err(|e| { Error::new( - eyre!("{}", e).wrap_err("Metrics daemon panicked!"), + eyre!("{}", e).wrap_err(t!("bins.startd.metrics-daemon-panicked").to_string()), ErrorKind::Unknown, ) }) - .map_ok(|_| tracing::debug!("Metrics daemon Shutdown")) + .map_ok(|_| tracing::debug!("{}", t!("bins.startd.metrics-daemon-shutdown"))) .await?; let shutdown = shutdown_recv @@ -144,7 +145,7 @@ pub fn main(args: impl IntoIterator) { .worker_threads(max(1, num_cpus::get())) .enable_all() .build() - .expect("failed to initialize runtime"); + .expect(&t!("bins.startd.failed-to-initialize-runtime")); let res = rt.block_on(async { let mut server = WebServer::new( Acceptor::bind_upgradable(SelfContainedNetworkInterfaceListener::bind(BindTcp, 80)), diff --git a/core/src/bins/tunnel.rs b/core/src/bins/tunnel.rs index 5c018f796..97fb818ea 100644 --- a/core/src/bins/tunnel.rs +++ b/core/src/bins/tunnel.rs @@ -6,6 +6,7 @@ use std::time::Duration; use clap::Parser; use futures::FutureExt; use rpc_toolkit::CliApp; +use rust_i18n::t; use tokio::signal::unix::signal; use tracing::instrument; use visit_rs::Visit; @@ -70,7 +71,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> { true } Err(e) => { - tracing::error!("error adding ssl listener: {e}"); + tracing::error!("{}", t!("bins.tunnel.error-adding-ssl-listener", error = e.to_string())); tracing::debug!("{e:?}"); false @@ -92,7 +93,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> { } .await { - tracing::error!("error updating webserver bind: {e}"); + tracing::error!("{}", t!("bins.tunnel.error-updating-webserver-bind", error = e.to_string())); tracing::debug!("{e:?}"); tokio::time::sleep(Duration::from_secs(5)).await; } @@ -157,7 +158,7 @@ pub fn main(args: impl IntoIterator) { let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() - .expect("failed to initialize runtime"); + .expect(&t!("bins.tunnel.failed-to-initialize-runtime")); rt.block_on(inner_main(&config)) }; @@ -179,6 +180,7 @@ pub fn cli(args: impl IntoIterator) { |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), crate::tunnel::api::tunnel_api(), ) + .mutate_command(super::translate_cli) .run(args) { match e.data { diff --git a/core/src/context/cli.rs b/core/src/context/cli.rs index 4346f304c..39d1c7890 100644 --- a/core/src/context/cli.rs +++ b/core/src/context/cli.rs @@ -166,14 +166,14 @@ impl CliContext { .with_kind(crate::ErrorKind::Pem)?; let secret = ed25519_dalek::SecretKey::try_from(&pair.secret_key[..]).map_err(|_| { Error::new( - eyre!("pkcs8 key is of incorrect length"), + eyre!("{}", t!("context.cli.pkcs8-key-incorrect-length")), ErrorKind::OpenSsl, ) })?; return Ok(secret.into()) } Err(Error::new( - eyre!("Developer Key does not exist! Please run `start-cli init-key` before running this command."), + eyre!("{}", t!("context.cli.developer-key-does-not-exist")), crate::ErrorKind::Uninitialized )) }) @@ -189,14 +189,14 @@ impl CliContext { "http" => "ws", _ => { return Err(Error::new( - eyre!("Cannot parse scheme from base URL"), + eyre!("{}", t!("context.cli.cannot-parse-scheme-from-base-url")), crate::ErrorKind::ParseUrl, ) .into()); } }; url.set_scheme(ws_scheme) - .map_err(|_| Error::new(eyre!("Cannot set URL scheme"), crate::ErrorKind::ParseUrl))?; + .map_err(|_| Error::new(eyre!("{}", t!("context.cli.cannot-set-url-scheme")), crate::ErrorKind::ParseUrl))?; url.path_segments_mut() .map_err(|_| eyre!("Url cannot be base")) .with_kind(crate::ErrorKind::ParseUrl)? diff --git a/core/src/context/diagnostic.rs b/core/src/context/diagnostic.rs index 5c49ebc86..a0201163c 100644 --- a/core/src/context/diagnostic.rs +++ b/core/src/context/diagnostic.rs @@ -27,7 +27,7 @@ impl DiagnosticContext { disk_guid: Option, error: Error, ) -> Result { - tracing::error!("Error: {}: Starting diagnostic UI", error); + tracing::error!("{}", t!("context.diagnostic.starting-diagnostic-ui", error = error)); tracing::debug!("{:?}", error); let (shutdown, _) = tokio::sync::broadcast::channel(1); diff --git a/core/src/context/rpc.rs b/core/src/context/rpc.rs index a90cae869..28635759e 100644 --- a/core/src/context/rpc.rs +++ b/core/src/context/rpc.rs @@ -84,7 +84,7 @@ pub struct RpcContextSeed { } impl Drop for RpcContextSeed { fn drop(&mut self) { - tracing::info!("RpcContext is dropped"); + tracing::info!("{}", t!("context.rpc.rpc-context-dropped")); } } @@ -155,7 +155,7 @@ impl RpcContext { let peek = db.peek().await; let account = AccountInfo::load(&peek)?; load_db.complete(); - tracing::info!("Opened PatchDB"); + tracing::info!("{}", t!("context.rpc.opened-patchdb")); init_net_ctrl.start(); let (net_controller, os_net_service) = if let Some(InitResult { @@ -172,15 +172,15 @@ impl RpcContext { (net_ctrl, os_net_service) }; init_net_ctrl.complete(); - tracing::info!("Initialized Net Controller"); + tracing::info!("{}", t!("context.rpc.initialized-net-controller")); if PLATFORM.ends_with("-nonfree") { if let Err(e) = Command::new("nvidia-smi") .invoke(ErrorKind::ParseSysInfo) .await { - tracing::warn!("nvidia-smi: {e}"); - tracing::info!("The above warning can be ignored if no NVIDIA card is present"); + tracing::warn!("{}", t!("context.rpc.nvidia-smi-error", error = e)); + tracing::info!("{}", t!("context.rpc.nvidia-warning-can-be-ignored")); } else { async { let version: InternedString = String::from_utf8( @@ -335,7 +335,7 @@ impl RpcContext { is_closed: AtomicBool::new(false), os_partitions: config.os_partitions.clone().ok_or_else(|| { Error::new( - eyre!("OS Partition Information Missing"), + eyre!("{}", t!("context.rpc.os-partition-info-missing")), ErrorKind::Filesystem, ) })?, @@ -365,9 +365,9 @@ impl RpcContext { current_secret: Arc::new( Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).map_err(|e| { tracing::debug!("{:?}", e); - tracing::error!("Couldn't generate ec key"); + tracing::error!("{}", t!("context.rpc.couldnt-generate-ec-key")); Error::new( - color_eyre::eyre::eyre!("Couldn't generate ec key"), + color_eyre::eyre::eyre!("{}", t!("context.rpc.couldnt-generate-ec-key")), crate::ErrorKind::Unknown, ) })?, @@ -382,10 +382,10 @@ impl RpcContext { let res = Self(seed.clone()); res.cleanup_and_initialize(cleanup_init).await?; - tracing::info!("Cleaned up transient states"); + tracing::info!("{}", t!("context.rpc.cleaned-up-transient-states")); crate::version::post_init(&res, run_migrations).await?; - tracing::info!("Completed migrations"); + tracing::info!("{}", t!("context.rpc.completed-migrations")); Ok(res) } @@ -394,7 +394,7 @@ impl RpcContext { self.crons.mutate(|c| std::mem::take(c)); self.services.shutdown_all().await?; self.is_closed.store(true, Ordering::SeqCst); - tracing::info!("RpcContext is shutdown"); + tracing::info!("{}", t!("context.rpc.rpc-context-shutdown")); Ok(()) } @@ -463,7 +463,7 @@ impl RpcContext { .await .result { - tracing::error!("Error in session cleanup cron: {e}"); + tracing::error!("{}", t!("context.rpc.error-in-session-cleanup-cron", error = e)); tracing::debug!("{e:?}"); } } diff --git a/core/src/context/setup.rs b/core/src/context/setup.rs index 16303f9f2..fda7545e8 100644 --- a/core/src/context/setup.rs +++ b/core/src/context/setup.rs @@ -33,7 +33,7 @@ use crate::util::sync::SyncMutex; lazy_static::lazy_static! { pub static ref CURRENT_SECRET: Jwk = Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).unwrap_or_else(|e| { tracing::debug!("{:?}", e); - tracing::error!("Couldn't generate ec key"); + tracing::error!("{}", t!("context.setup.couldnt-generate-ec-key")); panic!("Couldn't generate ec key") }); } @@ -125,11 +125,11 @@ impl SetupContext { .get_or_init(|| async { match f().await { Ok(res) => { - tracing::info!("Setup complete!"); + tracing::info!("{}", t!("context.setup.setup-complete")); Ok(res) } Err(e) => { - tracing::error!("Setup failed: {e}"); + tracing::error!("{}", t!("context.setup.setup-failed", error = e)); tracing::debug!("{e:?}"); Err(e) } @@ -142,10 +142,10 @@ impl SetupContext { ) .map_err(|_| { if self.result.initialized() { - Error::new(eyre!("Setup already complete"), ErrorKind::InvalidRequest) + Error::new(eyre!("{}", t!("context.setup.setup-already-complete")), ErrorKind::InvalidRequest) } else { Error::new( - eyre!("Setup already in progress"), + eyre!("{}", t!("context.setup.setup-already-in-progress")), ErrorKind::InvalidRequest, ) } @@ -195,7 +195,7 @@ impl SetupContext { } .await { - tracing::error!("Error in setup progress websocket: {e}"); + tracing::error!("{}", t!("context.setup.error-in-setup-progress-websocket", error = e)); tracing::debug!("{e:?}"); } }, diff --git a/core/src/db/mod.rs b/core/src/db/mod.rs index bdcce8f7f..e2a7b7875 100644 --- a/core/src/db/mod.rs +++ b/core/src/db/mod.rs @@ -54,7 +54,7 @@ pub fn db() -> ParentHandler { "dump", from_fn_async(cli_dump) .with_display_serializable() - .with_about("Filter/query db to display tables and records"), + .with_about("about.filter-query-db"), ) .subcommand("dump", from_fn_async(dump).no_cli()) .subcommand( @@ -65,13 +65,13 @@ pub fn db() -> ParentHandler { ) .subcommand( "put", - put::().with_about("Command for adding UI record to db"), + put::().with_about("about.command-add-ui-record-db"), ) .subcommand( "apply", from_fn_async(cli_apply) .no_display() - .with_about("Update a db record"), + .with_about("about.update-db-record"), ) .subcommand("apply", from_fn_async(apply).no_cli()) } @@ -358,7 +358,7 @@ pub fn put() -> ParentHandler { "ui", from_fn_async(ui) .with_display_serializable() - .with_about("Add path and value to db") + .with_about("about.add-path-value-db") .with_call_remote::(), ) } diff --git a/core/src/diagnostic.rs b/core/src/diagnostic.rs index 820d05512..e9baaf386 100644 --- a/core/src/diagnostic.rs +++ b/core/src/diagnostic.rs @@ -17,45 +17,46 @@ pub fn diagnostic() -> ParentHandler { .subcommand( "error", from_fn(error) - .with_about("Display diagnostic error") + .with_about("about.display-diagnostic-error") .with_call_remote::(), ) .subcommand( "logs", - crate::system::logs::().with_about("Display OS logs"), + crate::system::logs::().with_about("about.display-os-logs"), ) .subcommand( "logs", from_fn_async(crate::logs::cli_logs::) .no_display() - .with_about("Display OS logs"), + .with_about("about.display-os-logs"), ) .subcommand( "kernel-logs", - crate::system::kernel_logs::().with_about("Display kernel logs"), + crate::system::kernel_logs::() + .with_about("about.display-kernel-logs"), ) .subcommand( "kernel-logs", from_fn_async(crate::logs::cli_logs::) .no_display() - .with_about("Display kernal logs"), + .with_about("about.display-kernel-logs"), ) .subcommand( "restart", from_fn(restart) .no_display() - .with_about("Restart the server") + .with_about("about.restart-server") .with_call_remote::(), ) .subcommand( "disk", - disk::().with_about("Command to remove disk from filesystem"), + disk::().with_about("about.command-remove-disk-filesystem"), ) .subcommand( "rebuild", from_fn_async(rebuild) .no_display() - .with_about("Teardown and rebuild service containers") + .with_about("about.teardown-rebuild-containers") .with_call_remote::(), ) } @@ -89,7 +90,7 @@ pub fn disk() -> ParentHandler { from_fn_async(forget_disk::).no_display(), ) .no_display() - .with_about("Remove disk from filesystem"), + .with_about("about.remove-disk-filesystem"), ) .subcommand("repair", from_fn_async(|_: C| repair()).no_cli()) .subcommand( @@ -97,7 +98,7 @@ pub fn disk() -> ParentHandler { CallRemoteHandler::::new( from_fn_async(|_: RpcContext| repair()) .no_display() - .with_about("Repair disk in the event of corruption"), + .with_about("about.repair-disk-corruption"), ), ) } diff --git a/core/src/disk/fsck/ext4.rs b/core/src/disk/fsck/ext4.rs index 534efd12d..696a806fc 100644 --- a/core/src/disk/fsck/ext4.rs +++ b/core/src/disk/fsck/ext4.rs @@ -4,6 +4,7 @@ use std::path::Path; use color_eyre::eyre::eyre; use futures::FutureExt; use futures::future::BoxFuture; +use rust_i18n::t; use tokio::process::Command; use tracing::instrument; @@ -62,33 +63,31 @@ async fn e2fsck_runner( let e2fsck_stderr = String::from_utf8(e2fsck_out.stderr)?; let code = e2fsck_out.status.code().ok_or_else(|| { Error::new( - eyre!("e2fsck: process terminated by signal"), + eyre!("{}", t!("disk.fsck.process-terminated-by-signal")), crate::ErrorKind::DiskManagement, ) })?; if code & 4 != 0 { tracing::error!( - "some filesystem errors NOT corrected on {}:\n{}", - logicalname.as_ref().display(), - e2fsck_stderr, + "{}", + t!("disk.fsck.errors-not-corrected", device = logicalname.as_ref().display(), stderr = e2fsck_stderr), ); } else if code & 1 != 0 { tracing::warn!( - "filesystem errors corrected on {}:\n{}", - logicalname.as_ref().display(), - e2fsck_stderr, + "{}", + t!("disk.fsck.errors-corrected", device = logicalname.as_ref().display(), stderr = e2fsck_stderr), ); } if code < 8 { if code & 2 != 0 { - tracing::warn!("reboot required"); + tracing::warn!("{}", t!("disk.fsck.reboot-required")); Ok(RequiresReboot(true)) } else { Ok(RequiresReboot(false)) } } else { Err(Error::new( - eyre!("e2fsck: {}", e2fsck_stderr), + eyre!("{}", t!("disk.fsck.e2fsck-error", stderr = e2fsck_stderr)), crate::ErrorKind::DiskManagement, )) } diff --git a/core/src/disk/main.rs b/core/src/disk/main.rs index af043dd69..349cb045e 100644 --- a/core/src/disk/main.rs +++ b/core/src/disk/main.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use color_eyre::eyre::eyre; use imbl_value::InternedString; +use rust_i18n::t; use tokio::process::Command; use tracing::instrument; @@ -225,7 +226,7 @@ pub async fn import>( .is_none() { return Err(Error::new( - eyre!("StartOS disk not found."), + eyre!("{}", t!("disk.main.disk-not-found")), crate::ErrorKind::DiskNotAvailable, )); } @@ -235,7 +236,7 @@ pub async fn import>( .any(|id| id == guid) { return Err(Error::new( - eyre!("A StartOS disk was found, but it is not the correct disk for this device."), + eyre!("{}", t!("disk.main.incorrect-disk")), crate::ErrorKind::IncorrectDisk, )); } diff --git a/core/src/disk/mod.rs b/core/src/disk/mod.rs index 63d1757b6..aea0ad9a3 100644 --- a/core/src/disk/mod.rs +++ b/core/src/disk/mod.rs @@ -51,7 +51,7 @@ pub fn disk() -> ParentHandler { from_fn_async(list) .with_display_serializable() .with_custom_display_fn(|handle, result| display_disk_info(handle.params, result)) - .with_about("List disk info") + .with_about("about.list-disk-info") .with_call_remote::(), ) .subcommand("repair", from_fn_async(|_: C| repair()).no_cli()) @@ -60,7 +60,7 @@ pub fn disk() -> ParentHandler { CallRemoteHandler::::new( from_fn_async(|_: RpcContext| repair()) .no_display() - .with_about("Repair disk in the event of corruption"), + .with_about("about.repair-disk-corruption"), ), ) } diff --git a/core/src/disk/mount/util.rs b/core/src/disk/mount/util.rs index 46fc27890..8586f4a03 100644 --- a/core/src/disk/mount/util.rs +++ b/core/src/disk/mount/util.rs @@ -23,9 +23,8 @@ pub async fn bind, P1: AsRef>( read_only: bool, ) -> Result<(), Error> { tracing::info!( - "Binding {} to {}", - src.as_ref().display(), - dst.as_ref().display() + "{}", + t!("disk.mount.binding", src = src.as_ref().display(), dst = dst.as_ref().display()) ); if is_mountpoint(&dst).await? { unmount(dst.as_ref(), true).await?; diff --git a/core/src/disk/util.rs b/core/src/disk/util.rs index 2ec9dd9f3..d832fee5d 100644 --- a/core/src/disk/util.rs +++ b/core/src/disk/util.rs @@ -95,7 +95,7 @@ pub async fn get_vendor>(path: P) -> Result, Error Path::new(SYS_BLOCK_PATH) .join(path.as_ref().strip_prefix("/dev").map_err(|_| { Error::new( - eyre!("not a canonical block device"), + eyre!("{}", t!("disk.util.not-canonical-block-device")), crate::ErrorKind::BlockDevice, ) })?) @@ -118,7 +118,7 @@ pub async fn get_model>(path: P) -> Result, Error> Path::new(SYS_BLOCK_PATH) .join(path.as_ref().strip_prefix("/dev").map_err(|_| { Error::new( - eyre!("not a canonical block device"), + eyre!("{}", t!("disk.util.not-canonical-block-device")), crate::ErrorKind::BlockDevice, ) })?) @@ -374,23 +374,22 @@ async fn disk_info(disk: PathBuf) -> DiskInfo { .await .map_err(|e| { tracing::warn!( - "Could not get partition table of {}: {}", - disk.display(), - e.source + "{}", + t!("disk.util.could-not-get-partition-table", disk = disk.display(), error = e.source) ) }) .unwrap_or_default(); let vendor = get_vendor(&disk) .await - .map_err(|e| tracing::warn!("Could not get vendor of {}: {}", disk.display(), e.source)) + .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-vendor", disk = disk.display(), error = e.source))) .unwrap_or_default(); let model = get_model(&disk) .await - .map_err(|e| tracing::warn!("Could not get model of {}: {}", disk.display(), e.source)) + .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-model", disk = disk.display(), error = e.source))) .unwrap_or_default(); let capacity = get_capacity(&disk) .await - .map_err(|e| tracing::warn!("Could not get capacity of {}: {}", disk.display(), e.source)) + .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-capacity", disk = disk.display(), error = e.source))) .unwrap_or_default(); DiskInfo { logicalname: disk, @@ -407,21 +406,21 @@ async fn part_info(part: PathBuf) -> PartitionInfo { let mut start_os = BTreeMap::new(); let label = get_label(&part) .await - .map_err(|e| tracing::warn!("Could not get label of {}: {}", part.display(), e.source)) + .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-label", part = part.display(), error = e.source))) .unwrap_or_default(); let capacity = get_capacity(&part) .await - .map_err(|e| tracing::warn!("Could not get capacity of {}: {}", part.display(), e.source)) + .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-capacity-part", part = part.display(), error = e.source))) .unwrap_or_default(); let mut used = None; match TmpMountGuard::mount(&BlockDev::new(&part), ReadOnly).await { - Err(e) => tracing::warn!("Could not collect usage information: {}", e.source), + Err(e) => tracing::warn!("{}", t!("disk.util.could-not-collect-usage-info", error = e.source)), Ok(mount_guard) => { used = get_used(mount_guard.path()) .await .map_err(|e| { - tracing::warn!("Could not get usage of {}: {}", part.display(), e.source) + tracing::warn!("{}", t!("disk.util.could-not-get-usage", part = part.display(), error = e.source)) }) .ok(); match recovery_info(mount_guard.path()).await { @@ -429,11 +428,11 @@ async fn part_info(part: PathBuf) -> PartitionInfo { start_os = a; } Err(e) => { - tracing::error!("Error fetching unencrypted backup metadata: {}", e); + tracing::error!("{}", t!("disk.util.error-fetching-backup-metadata", error = e)); } } if let Err(e) = mount_guard.unmount().await { - tracing::error!("Error unmounting partition {}: {}", part.display(), e); + tracing::error!("{}", t!("disk.util.error-unmounting-partition", part = part.display(), error = e)); } } } @@ -474,7 +473,7 @@ fn parse_pvscan_output(pvscan_output: &str) -> BTreeMap { - tracing::warn!("Failed to parse pvscan output line: {}", entry); + tracing::warn!("{}", t!("disk.util.failed-to-parse-pvscan", line = entry)); } } } diff --git a/core/src/error.rs b/core/src/error.rs index 3e6a1ee86..83518c8a0 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -9,6 +9,7 @@ use rpc_toolkit::reqwest; use rpc_toolkit::yajrc::{ INVALID_PARAMS_ERROR, INVALID_REQUEST_ERROR, METHOD_NOT_FOUND_ERROR, PARSE_ERROR, RpcError, }; +use rust_i18n::t; use serde::{Deserialize, Serialize}; use tokio::task::JoinHandle; use tokio_rustls::rustls; @@ -100,94 +101,94 @@ pub enum ErrorKind { SetSysInfo = 79, } impl ErrorKind { - pub fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> String { use ErrorKind::*; match self { - Unknown => "Unknown Error", - Filesystem => "Filesystem I/O Error", - Docker => "Docker Error", - ConfigSpecViolation => "Config Spec Violation", - ConfigRulesViolation => "Config Rules Violation", - NotFound => "Not Found", - IncorrectPassword => "Incorrect Password", - VersionIncompatible => "Version Incompatible", - Network => "Network Error", - Registry => "Registry Error", - Serialization => "Serialization Error", - Deserialization => "Deserialization Error", - Utf8 => "UTF-8 Parse Error", - ParseVersion => "Version Parsing Error", - IncorrectDisk => "Incorrect Disk", - // Nginx => "Nginx Error", - Dependency => "Dependency Error", - ParseS9pk => "S9PK Parsing Error", - ParseUrl => "URL Parsing Error", - DiskNotAvailable => "Disk Not Available", - BlockDevice => "Block Device Error", - InvalidOnionAddress => "Invalid Onion Address", - Pack => "Pack Error", - ValidateS9pk => "S9PK Validation Error", - DiskCorrupted => "Disk Corrupted", // Remove - Tor => "Tor Daemon Error", - ConfigGen => "Config Generation Error", - ParseNumber => "Number Parsing Error", - Database => "Database Error", - InvalidId => "Invalid ID", - InvalidSignature => "Invalid Signature", - Backup => "Backup Error", - Restore => "Restore Error", - Authorization => "Unauthorized", - AutoConfigure => "Auto-Configure Error", - Action => "Action Failed", - RateLimited => "Rate Limited", - InvalidRequest => "Invalid Request", - MigrationFailed => "Migration Failed", - Uninitialized => "Uninitialized", - ParseNetAddress => "Net Address Parsing Error", - ParseSshKey => "SSH Key Parsing Error", - SoundError => "Sound Interface Error", - ParseTimestamp => "Timestamp Parsing Error", - ParseSysInfo => "System Info Parsing Error", - Wifi => "WiFi Internal Error", - Journald => "Journald Error", - DiskManagement => "Disk Management Error", - OpenSsl => "OpenSSL Internal Error", - PasswordHashGeneration => "Password Hash Generation Error", - DiagnosticMode => "Server is in Diagnostic Mode", - ParseDbField => "Database Field Parse Error", - Duplicate => "Duplication Error", - MultipleErrors => "Multiple Errors", - Incoherent => "Incoherent", - InvalidBackupTargetId => "Invalid Backup Target ID", - ProductKeyMismatch => "Incompatible Product Keys", - LanPortConflict => "Incompatible LAN Port Configuration", - Javascript => "Javascript Engine Error", - Pem => "PEM Encoding Error", - TLSInit => "TLS Backend Initialization Error", - Ascii => "ASCII Parse Error", - MissingHeader => "Missing Header", - Grub => "Grub Error", - Systemd => "Systemd Error", - OpenSsh => "OpenSSH Error", - Zram => "Zram Error", - Lshw => "LSHW Error", - CpuSettings => "CPU Settings Error", - Firmware => "Firmware Error", - Timeout => "Timeout Error", - Lxc => "LXC Error", - Cancelled => "Cancelled", - Git => "Git Error", - DBus => "DBus Error", - InstallFailed => "Install Failed", - UpdateFailed => "Update Failed", - Smtp => "SMTP Error", - SetSysInfo => "Error Setting System Info", - } + Unknown => t!("error.unknown"), + Filesystem => t!("error.filesystem"), + Docker => t!("error.docker"), + ConfigSpecViolation => t!("error.config-spec-violation"), + ConfigRulesViolation => t!("error.config-rules-violation"), + NotFound => t!("error.not-found"), + IncorrectPassword => t!("error.incorrect-password"), + VersionIncompatible => t!("error.version-incompatible"), + Network => t!("error.network"), + Registry => t!("error.registry"), + Serialization => t!("error.serialization"), + Deserialization => t!("error.deserialization"), + Utf8 => t!("error.utf8"), + ParseVersion => t!("error.parse-version"), + IncorrectDisk => t!("error.incorrect-disk"), + // Nginx => t!("error.nginx"), + Dependency => t!("error.dependency"), + ParseS9pk => t!("error.parse-s9pk"), + ParseUrl => t!("error.parse-url"), + DiskNotAvailable => t!("error.disk-not-available"), + BlockDevice => t!("error.block-device"), + InvalidOnionAddress => t!("error.invalid-onion-address"), + Pack => t!("error.pack"), + ValidateS9pk => t!("error.validate-s9pk"), + DiskCorrupted => t!("error.disk-corrupted"), // Remove + Tor => t!("error.tor"), + ConfigGen => t!("error.config-gen"), + ParseNumber => t!("error.parse-number"), + Database => t!("error.database"), + InvalidId => t!("error.invalid-id"), + InvalidSignature => t!("error.invalid-signature"), + Backup => t!("error.backup"), + Restore => t!("error.restore"), + Authorization => t!("error.authorization"), + AutoConfigure => t!("error.auto-configure"), + Action => t!("error.action"), + RateLimited => t!("error.rate-limited"), + InvalidRequest => t!("error.invalid-request"), + MigrationFailed => t!("error.migration-failed"), + Uninitialized => t!("error.uninitialized"), + ParseNetAddress => t!("error.parse-net-address"), + ParseSshKey => t!("error.parse-ssh-key"), + SoundError => t!("error.sound-error"), + ParseTimestamp => t!("error.parse-timestamp"), + ParseSysInfo => t!("error.parse-sys-info"), + Wifi => t!("error.wifi"), + Journald => t!("error.journald"), + DiskManagement => t!("error.disk-management"), + OpenSsl => t!("error.openssl"), + PasswordHashGeneration => t!("error.password-hash-generation"), + DiagnosticMode => t!("error.diagnostic-mode"), + ParseDbField => t!("error.parse-db-field"), + Duplicate => t!("error.duplicate"), + MultipleErrors => t!("error.multiple-errors"), + Incoherent => t!("error.incoherent"), + InvalidBackupTargetId => t!("error.invalid-backup-target-id"), + ProductKeyMismatch => t!("error.product-key-mismatch"), + LanPortConflict => t!("error.lan-port-conflict"), + Javascript => t!("error.javascript"), + Pem => t!("error.pem"), + TLSInit => t!("error.tls-init"), + Ascii => t!("error.ascii"), + MissingHeader => t!("error.missing-header"), + Grub => t!("error.grub"), + Systemd => t!("error.systemd"), + OpenSsh => t!("error.openssh"), + Zram => t!("error.zram"), + Lshw => t!("error.lshw"), + CpuSettings => t!("error.cpu-settings"), + Firmware => t!("error.firmware"), + Timeout => t!("error.timeout"), + Lxc => t!("error.lxc"), + Cancelled => t!("error.cancelled"), + Git => t!("error.git"), + DBus => t!("error.dbus"), + InstallFailed => t!("error.install-failed"), + UpdateFailed => t!("error.update-failed"), + Smtp => t!("error.smtp"), + SetSysInfo => t!("error.set-sys-info"), + }.to_string() } } impl Display for ErrorKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.as_str()) + write!(f, "{}", &self.as_str()) } } @@ -201,7 +202,7 @@ pub struct Error { impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {:#}", self.kind.as_str(), self.source) + write!(f, "{}: {:#}", &self.kind.as_str(), self.source) } } impl Debug for Error { @@ -209,7 +210,7 @@ impl Debug for Error { write!( f, "{}: {:?}", - self.kind.as_str(), + &self.kind.as_str(), self.debug.as_ref().unwrap_or(&self.source) ) } diff --git a/core/src/init.rs b/core/src/init.rs index 741ca3e2d..39680015e 100644 --- a/core/src/init.rs +++ b/core/src/init.rs @@ -81,26 +81,28 @@ impl InitPhases { pub fn new(handle: &FullProgressTracker) -> Self { Self { preinit: if Path::new("/media/startos/config/preinit.sh").exists() { - Some(handle.add_phase("Running preinit.sh".into(), Some(5))) + Some(handle.add_phase(t!("init.running-preinit").into(), Some(5))) } else { None }, - local_auth: handle.add_phase("Enabling local authentication".into(), Some(1)), - load_database: handle.add_phase("Loading database".into(), Some(5)), - load_ssh_keys: handle.add_phase("Loading SSH Keys".into(), Some(1)), - start_net: handle.add_phase("Starting network controller".into(), Some(1)), - mount_logs: handle.add_phase("Switching logs to write to data drive".into(), Some(1)), - load_ca_cert: handle.add_phase("Loading CA certificate".into(), Some(1)), - load_wifi: handle.add_phase("Loading WiFi configuration".into(), Some(1)), - init_tmp: handle.add_phase("Initializing temporary files".into(), Some(1)), - set_governor: handle.add_phase("Setting CPU performance profile".into(), Some(1)), - sync_clock: handle.add_phase("Synchronizing system clock".into(), Some(10)), - enable_zram: handle.add_phase("Enabling ZRAM".into(), Some(1)), - update_server_info: handle.add_phase("Updating server info".into(), Some(1)), - launch_service_network: handle.add_phase("Launching service intranet".into(), Some(1)), - validate_db: handle.add_phase("Validating database".into(), Some(1)), + local_auth: handle.add_phase(t!("init.enabling-local-auth").into(), Some(1)), + load_database: handle.add_phase(t!("init.loading-database").into(), Some(5)), + load_ssh_keys: handle.add_phase(t!("init.loading-ssh-keys").into(), Some(1)), + start_net: handle.add_phase(t!("init.starting-network-controller").into(), Some(1)), + mount_logs: handle.add_phase(t!("init.switching-logs-to-data-drive").into(), Some(1)), + load_ca_cert: handle.add_phase(t!("init.loading-ca-certificate").into(), Some(1)), + load_wifi: handle.add_phase(t!("init.loading-wifi-configuration").into(), Some(1)), + init_tmp: handle.add_phase(t!("init.initializing-temporary-files").into(), Some(1)), + set_governor: handle + .add_phase(t!("init.setting-cpu-performance-profile").into(), Some(1)), + sync_clock: handle.add_phase(t!("init.synchronizing-system-clock").into(), Some(10)), + enable_zram: handle.add_phase(t!("init.enabling-zram").into(), Some(1)), + update_server_info: handle.add_phase(t!("init.updating-server-info").into(), Some(1)), + launch_service_network: handle + .add_phase(t!("init.launching-service-intranet").into(), Some(1)), + validate_db: handle.add_phase(t!("init.validating-database").into(), Some(1)), postinit: if Path::new("/media/startos/config/postinit.sh").exists() { - Some(handle.add_phase("Running postinit.sh".into(), Some(5))) + Some(handle.add_phase(t!("init.running-postinit").into(), Some(5))) } else { None }, @@ -127,7 +129,14 @@ pub async fn run_script>(path: P, mut progress: PhaseProgressTrac } .await { - tracing::error!("Error Running {}: {}", script.display(), e); + tracing::error!( + "{}", + t!( + "init.error-running-script", + script = script.display(), + error = e + ) + ); tracing::debug!("{:?}", e); } progress.complete(); @@ -230,6 +239,7 @@ pub async fn init( .arg("-R") .arg("+C") .arg("/var/log/journal") + .env("LANG", "C.UTF-8") .invoke(ErrorKind::Filesystem) .await { @@ -314,14 +324,17 @@ pub async fn init( { Some(governor) } else { - tracing::warn!("CPU Governor \"{governor}\" Not Available"); + tracing::warn!( + "{}", + t!("init.cpu-governor-not-available", governor = governor) + ); None } } else { cpupower::get_preferred_governor().await? }; if let Some(governor) = governor { - tracing::info!("Setting CPU Governor to \"{governor}\""); + tracing::info!("{}", t!("init.setting-cpu-governor", governor = governor)); cpupower::set_governor(governor).await?; } set_governor.complete(); @@ -349,14 +362,14 @@ pub async fn init( } } if !ntp_synced { - tracing::warn!("Timed out waiting for system time to synchronize"); + tracing::warn!("{}", t!("init.clock-sync-timeout")); } sync_clock.complete(); enable_zram.start(); if server_info.as_zram().de()? { crate::system::enable_zram().await?; - tracing::info!("Enabled ZRAM"); + tracing::info!("{}", t!("init.enabled-zram")); } enable_zram.complete(); @@ -404,7 +417,7 @@ pub async fn init( run_script("/media/startos/config/postinit.sh", progress).await; } - tracing::info!("System initialized."); + tracing::info!("{}", t!("init.system-initialized")); Ok(InitResult { net_ctrl, @@ -416,30 +429,30 @@ pub fn init_api() -> ParentHandler { ParentHandler::new() .subcommand( "logs", - crate::system::logs::().with_about("Disply OS logs"), + crate::system::logs::().with_about("about.display-os-logs"), ) .subcommand( "logs", from_fn_async(crate::logs::cli_logs::) .no_display() - .with_about("Display OS logs"), + .with_about("about.display-os-logs"), ) .subcommand( "kernel-logs", - crate::system::kernel_logs::().with_about("Display kernel logs"), + crate::system::kernel_logs::().with_about("about.display-kernel-logs"), ) .subcommand( "kernel-logs", from_fn_async(crate::logs::cli_logs::) .no_display() - .with_about("Display kernel logs"), + .with_about("about.display-kernel-logs"), ) .subcommand("subscribe", from_fn_async(init_progress).no_cli()) .subcommand( "subscribe", from_fn_async(cli_init_progress) .no_display() - .with_about("Get initialization progress"), + .with_about("about.get-initialization-progress"), ) } @@ -495,7 +508,7 @@ pub async fn init_progress(ctx: InitContext) -> Result { ); if let Err(e) = ws.close_result(res.map(|_| "complete")).await { - tracing::error!("error closing init progress websocket: {e}"); + tracing::error!("{}", t!("init.error-closing-websocket", error = e)); tracing::debug!("{e:?}"); } }, @@ -526,7 +539,7 @@ pub async fn cli_init_progress( .await?, )?; let mut ws = ctx.ws_continuation(res.guid).await?; - let mut bar = PhasedProgressBar::new("Initializing..."); + let mut bar = PhasedProgressBar::new(&t!("init.initializing")); while let Some(msg) = ws.try_next().await.with_kind(ErrorKind::Network)? { if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg { bar.update(&serde_json::from_str(&msg).with_kind(ErrorKind::Deserialization)?); diff --git a/core/src/lib.rs b/core/src/lib.rs index 4e9f186a0..80cdca71b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -135,80 +135,63 @@ pub fn main_api() -> ParentHandler { let mut api = ParentHandler::new() .subcommand( "git-info", - from_fn(|_: C| version::git_info()).with_about("Display the githash of StartOS CLI"), + from_fn(|_: C| version::git_info()).with_about("about.display-githash"), ) .subcommand( "echo", from_fn(echo::) .with_metadata("authenticated", Value::Bool(false)) - .with_about("Echo a message") + .with_about("about.echo-message") .with_call_remote::(), ) .subcommand( "state", from_fn(|_: RpcContext| Ok::<_, Error>(ApiState::Running)) .with_metadata("authenticated", Value::Bool(false)) - .with_about("Display the API that is currently serving") + .with_about("about.display-current-api") .with_call_remote::(), ) .subcommand( "state", from_fn(|_: InitContext| Ok::<_, Error>(ApiState::Initializing)) .with_metadata("authenticated", Value::Bool(false)) - .with_about("Display the API that is currently serving") + .with_about("about.display-current-api") .with_call_remote::(), ) .subcommand( "state", from_fn(|_: DiagnosticContext| Ok::<_, Error>(ApiState::Error)) .with_metadata("authenticated", Value::Bool(false)) - .with_about("Display the API that is currently serving") + .with_about("about.display-current-api") .with_call_remote::(), ) - .subcommand( - "server", - server::() - .with_about("Commands related to the server i.e. restart, update, and shutdown"), - ) + .subcommand("server", server::().with_about("about.commands-server")) .subcommand( "package", - package::().with_about("Commands related to packages"), + package::().with_about("about.commands-packages"), ) .subcommand( "net", - net::net_api::().with_about("Network commands related to tor and dhcp"), + net::net_api::().with_about("about.network-commands"), ) .subcommand( "auth", - auth::auth::() - .with_about("Commands related to Authentication i.e. login, logout"), - ) - .subcommand( - "db", - db::db::().with_about("Commands to interact with the db i.e. dump, put, apply"), - ) - .subcommand( - "ssh", - ssh::ssh::() - .with_about("Commands for interacting with ssh keys i.e. add, delete, list"), + auth::auth::().with_about("about.commands-authentication"), ) + .subcommand("db", db::db::().with_about("about.commands-db")) + .subcommand("ssh", ssh::ssh::().with_about("about.commands-ssh-keys")) .subcommand( "wifi", - net::wifi::wifi::() - .with_about("Commands related to wifi networks i.e. add, connect, delete"), - ) - .subcommand( - "disk", - disk::disk::().with_about("Commands for listing disk info and repairing"), + net::wifi::wifi::().with_about("about.commands-wifi"), ) + .subcommand("disk", disk::disk::().with_about("about.commands-disk")) .subcommand( "notification", - notifications::notification::().with_about("Create, delete, or list notifications"), + notifications::notification::().with_about("about.commands-notifications"), ) .subcommand( "backup", - backup::backup::() - .with_about("Commands related to backup creation and backup targets"), + backup::backup::().with_about("about.commands-backup"), ) .subcommand( "registry", @@ -219,7 +202,7 @@ pub fn main_api() -> ParentHandler { ) .subcommand( "registry", - registry::registry_api::().with_about("Commands related to the registry"), + registry::registry_api::().with_about("about.commands-registry"), ) .subcommand( "tunnel", @@ -228,31 +211,26 @@ pub fn main_api() -> ParentHandler { ) .subcommand( "tunnel", - tunnel::api::tunnel_api::().with_about("Commands related to StartTunnel"), - ) - .subcommand( - "s9pk", - s9pk::rpc::s9pk().with_about("Commands for interacting with s9pk files"), + tunnel::api::tunnel_api::().with_about("about.commands-tunnel"), ) + .subcommand("s9pk", s9pk::rpc::s9pk().with_about("about.commands-s9pk")) .subcommand( "util", - util::rpc::util::().with_about("Command for calculating the blake3 hash of a file"), + util::rpc::util::().with_about("about.command-blake3-hash"), ) .subcommand( "init-key", from_fn_async(developer::init) .no_display() - .with_about("Create developer key if it doesn't exist"), + .with_about("about.create-developer-key"), ) .subcommand( "pubkey", - from_fn_blocking(developer::pubkey) - .with_about("Get public key for developer private key"), + from_fn_blocking(developer::pubkey).with_about("about.get-developer-pubkey"), ) .subcommand( "diagnostic", - diagnostic::diagnostic::() - .with_about("Commands to display logs, restart the server, etc"), + diagnostic::diagnostic::().with_about("about.commands-diagnostic"), ) .subcommand("init", init::init_api::()) .subcommand("setup", setup::setup::()); @@ -265,7 +243,7 @@ pub fn main_api() -> ParentHandler { "flash-os", from_fn_async(os_install::cli_install_os) .no_display() - .with_about("Flash StartOS to a disk from a squashfs"), + .with_about("about.flash-startos"), ); } api @@ -280,29 +258,32 @@ pub fn server() -> ParentHandler { .with_custom_display_fn(|handle, result| { system::display_time(handle.params, result) }) - .with_about("Display current time and server uptime") - .with_call_remote::() + .with_about("about.display-time-uptime") + .with_call_remote::(), ) .subcommand( "experimental", - system::experimental::() - .with_about("Commands related to configuring experimental options such as zram and cpu governor"), + system::experimental::().with_about("about.commands-experimental"), ) .subcommand( "logs", - system::logs::().with_about("Display OS logs"), + system::logs::().with_about("about.display-os-logs"), ) .subcommand( "logs", - from_fn_async(logs::cli_logs::).no_display().with_about("Display OS logs"), + from_fn_async(logs::cli_logs::) + .no_display() + .with_about("about.display-os-logs"), ) .subcommand( "kernel-logs", - system::kernel_logs::().with_about("Display Kernel logs"), + system::kernel_logs::().with_about("about.display-kernel-logs"), ) .subcommand( "kernel-logs", - from_fn_async(logs::cli_logs::).no_display().with_about("Display Kernel logs"), + from_fn_async(logs::cli_logs::) + .no_display() + .with_about("about.display-kernel-logs"), ) .subcommand( "metrics", @@ -310,35 +291,31 @@ pub fn server() -> ParentHandler { .root_handler( from_fn_async(system::metrics) .with_display_serializable() - .with_about("Display information about the server i.e. temperature, RAM, CPU, and disk usage") - .with_call_remote::() - ) - .subcommand( - "follow", - from_fn_async(system::metrics_follow) - .no_cli() + .with_about("about.display-server-metrics") + .with_call_remote::(), ) + .subcommand("follow", from_fn_async(system::metrics_follow).no_cli()), ) .subcommand( "shutdown", from_fn_async(shutdown::shutdown) .no_display() - .with_about("Shutdown the server") - .with_call_remote::() + .with_about("about.shutdown-server") + .with_call_remote::(), ) .subcommand( "restart", from_fn_async(shutdown::restart) .no_display() - .with_about("Restart the server") - .with_call_remote::() + .with_about("about.restart-server") + .with_call_remote::(), ) .subcommand( "rebuild", from_fn_async(shutdown::rebuild) .no_display() - .with_about("Teardown and rebuild service containers") - .with_call_remote::() + .with_about("about.teardown-rebuild-containers") + .with_call_remote::(), ) .subcommand( "update", @@ -348,7 +325,9 @@ pub fn server() -> ParentHandler { ) .subcommand( "update", - from_fn_async(update::cli_update_system).no_display().with_about("Check a given registry for StartOS updates and update if available"), + from_fn_async(update::cli_update_system) + .no_display() + .with_about("about.check-update-startos"), ) .subcommand( "update-firmware", @@ -363,37 +342,41 @@ pub fn server() -> ParentHandler { .with_custom_display_fn(|_handle, result| { Ok(firmware::display_firmware_update_result(result)) }) - .with_about("Update the mainboard's firmware to the latest firmware available in this version of StartOS if available. Note: This command does not reach out to the Internet") - .with_call_remote::() + .with_about("about.update-firmware") + .with_call_remote::(), ) .subcommand( "set-smtp", from_fn_async(system::set_system_smtp) .no_display() - .with_about("Set system smtp server and credentials") - .with_call_remote::() + .with_about("about.set-smtp") + .with_call_remote::(), ) .subcommand( - "test-smtp", + "test-smtp", from_fn_async(system::test_smtp) .no_display() - .with_about("Send test email using provided smtp server and credentials") - .with_call_remote::() + .with_about("about.test-smtp") + .with_call_remote::(), ) .subcommand( "clear-smtp", from_fn_async(system::clear_system_smtp) .no_display() - .with_about("Remove system smtp server and credentials") - .with_call_remote::() - ).subcommand("host", net::host::server_host_api::().with_about("Commands for modifying the host for the system ui")) + .with_about("about.clear-smtp") + .with_call_remote::(), + ) + .subcommand( + "host", + net::host::server_host_api::().with_about("about.commands-host-system-ui"), + ) } pub fn package() -> ParentHandler { ParentHandler::new() .subcommand( "action", - action::action_api::().with_about("Commands to get action input or run an action"), + action::action_api::().with_about("about.commands-action"), ) .subcommand( "install", @@ -411,13 +394,13 @@ pub fn package() -> ParentHandler { "install", from_fn_async_local(install::cli_install) .no_display() - .with_about("Install a package from a marketplace or via sideloading"), + .with_about("about.install-package"), ) .subcommand( "cancel-install", from_fn(install::cancel_install) .no_display() - .with_about("Cancel an install of a package") + .with_about("about.cancel-install-package") .with_call_remote::(), ) .subcommand( @@ -425,21 +408,21 @@ pub fn package() -> ParentHandler { from_fn_async(install::uninstall) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Remove a package") + .with_about("about.remove-package") .with_call_remote::(), ) .subcommand( "list", from_fn_async(install::list) .with_display_serializable() - .with_about("List installed packages") + .with_about("about.list-installed-packages") .with_call_remote::(), ) .subcommand( "installed-version", from_fn_async(install::installed_version) .with_display_serializable() - .with_about("Display installed version for a PackageId") + .with_about("about.display-installed-version") .with_call_remote::(), ) .subcommand( @@ -447,7 +430,7 @@ pub fn package() -> ParentHandler { from_fn_async(control::start) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Start a service") + .with_about("about.start-service") .with_call_remote::(), ) .subcommand( @@ -455,7 +438,7 @@ pub fn package() -> ParentHandler { from_fn_async(control::stop) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Stop a service") + .with_about("about.stop-service") .with_call_remote::(), ) .subcommand( @@ -463,7 +446,7 @@ pub fn package() -> ParentHandler { from_fn_async(control::restart) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Restart a service") + .with_about("about.restart-service") .with_call_remote::(), ) .subcommand( @@ -471,7 +454,7 @@ pub fn package() -> ParentHandler { from_fn_async(service::rebuild) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Rebuild service container") + .with_about("about.rebuild-service-container") .with_call_remote::(), ) .subcommand( @@ -511,35 +494,34 @@ pub fn package() -> ParentHandler { table.print_tty(false)?; Ok(()) }) - .with_about("List information related to the lxc containers i.e. CPU, Memory, Disk") + .with_about("about.list-lxc-container-info") .with_call_remote::(), ) .subcommand("logs", logs::package_logs()) .subcommand( "logs", - logs::package_logs().with_about("Display package logs"), + logs::package_logs().with_about("about.display-package-logs"), ) .subcommand( "logs", from_fn_async(logs::cli_logs::) .no_display() - .with_about("Display package logs"), + .with_about("about.display-package-logs"), ) .subcommand( "backup", - backup::package_backup::() - .with_about("Commands for restoring package(s) from backup"), + backup::package_backup::().with_about("about.commands-restore-backup"), ) .subcommand( "attach", from_fn_async(service::attach) .with_metadata("get_session", Value::Bool(true)) - .with_about("Execute commands within a service container") + .with_about("about.execute-commands-container") .no_cli(), ) .subcommand("attach", from_fn_async(service::cli_attach).no_display()) .subcommand( "host", - net::host::host_api::().with_about("Manage network hosts for a package"), + net::host::host_api::().with_about("about.manage-network-hosts-package"), ) } diff --git a/core/src/logs.rs b/core/src/logs.rs index d4c7c97da..2367dc522 100644 --- a/core/src/logs.rs +++ b/core/src/logs.rs @@ -554,7 +554,7 @@ pub async fn journalctl( let mut child = follow_cmd.stdout(Stdio::piped()).spawn()?; let out = BufReader::new(child.stdout.take().ok_or_else(|| { - Error::new(eyre!("No stdout available"), crate::ErrorKind::Journald) + Error::new(eyre!("{}", t!("logs.no-stdout-available")), crate::ErrorKind::Journald) })?); let journalctl_entries = LinesStream::new(out.lines()); @@ -700,7 +700,7 @@ pub async fn follow_logs>( RpcContinuation::ws( move |socket| async move { if let Err(e) = ws_handler(first_entry, stream, socket).await { - tracing::error!("Error in log stream: {}", e); + tracing::error!("{}", t!("logs.error-in-log-stream", error = e.to_string())); } }, Duration::from_secs(30), diff --git a/core/src/lxc/mod.rs b/core/src/lxc/mod.rs index bdca44ebe..baffb041e 100644 --- a/core/src/lxc/mod.rs +++ b/core/src/lxc/mod.rs @@ -141,7 +141,7 @@ impl LxcManager { > 0 { return Err(Error::new( - eyre!("rootfs is not empty, refusing to delete"), + eyre!("{}", t!("lxc.mod.rootfs-not-empty")), ErrorKind::InvalidRequest, )); } @@ -249,6 +249,7 @@ impl LxcContainer { .arg("-R") .arg("+C") .arg(&log_mount_point) + .env("LANG", "C.UTF-8") .invoke(ErrorKind::Filesystem) .await { @@ -381,7 +382,7 @@ impl LxcContainer { } if start.elapsed() > CONTAINER_DHCP_TIMEOUT { return Err(Error::new( - eyre!("Timed out waiting for container to acquire DHCP lease"), + eyre!("{}", t!("lxc.mod.dhcp-timeout")), ErrorKind::Timeout, )); } @@ -407,9 +408,12 @@ impl LxcContainer { if !output.status.success() { return Err(Error::new( eyre!( - "Command failed with exit code: {:?} \n Message: {:?}", - output.status.code(), - String::from_utf8(output.stderr) + "{}", + t!( + "lxc.mod.command-failed", + code = format!("{:?}", output.status.code()), + message = format!("{:?}", String::from_utf8(output.stderr)) + ) ), ErrorKind::Docker, )); @@ -437,7 +441,7 @@ impl LxcContainer { > 0 { return Err(Error::new( - eyre!("rootfs is not empty, refusing to delete"), + eyre!("{}", t!("lxc.mod.rootfs-not-empty")), ErrorKind::InvalidRequest, )); } @@ -473,13 +477,19 @@ impl LxcContainer { .await ); return Err(Error::new( - eyre!("timed out waiting for socket"), + eyre!("{}", t!("lxc.mod.socket-timeout")), ErrorKind::Timeout, )); } tokio::time::sleep(Duration::from_millis(100)).await; } - tracing::info!("Connected to socket in {:?}", started.elapsed()); + tracing::info!( + "{}", + t!( + "lxc.mod.connected-to-socket", + elapsed = format!("{:?}", started.elapsed()) + ) + ); Ok(UnixRpcClient::new(sock_path)) } @@ -569,8 +579,11 @@ impl Drop for LxcContainer { fn drop(&mut self) { if !self.exited { tracing::warn!( - "Container {} was ungracefully dropped. Cleaning up dangling containers...", - &**self.guid + "{}", + t!( + "lxc.mod.container-ungracefully-dropped", + container = &**self.guid + ) ); let rootfs = self.rootfs.take(); let guid = std::mem::take(&mut self.guid); @@ -589,16 +602,25 @@ impl Drop for LxcContainer { } .await { - tracing::error!("Error reading logs from crashed container: {e}"); + tracing::error!( + "{}", + t!("lxc.mod.error-reading-crashed-logs", error = e.to_string()) + ); tracing::debug!("{e:?}") } rootfs.unmount(true).await.log_err(); drop(guid); if let Err(e) = manager.gc().await { - tracing::error!("Error cleaning up dangling LXC containers: {e}"); + tracing::error!( + "{}", + t!( + "lxc.mod.error-cleaning-up-containers", + error = e.to_string() + ) + ); tracing::debug!("{e:?}") } else { - tracing::info!("Successfully cleaned up dangling LXC containers"); + tracing::info!("{}", t!("lxc.mod.cleaned-up-containers")); } }); } diff --git a/core/src/middleware/auth/local.rs b/core/src/middleware/auth/local.rs index 342e1a882..b723608d3 100644 --- a/core/src/middleware/auth/local.rs +++ b/core/src/middleware/auth/local.rs @@ -40,7 +40,7 @@ impl LocalAuthContext for RpcContext { } fn unauthorized() -> Error { - Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization) + Error::new(eyre!("{}", t!("middleware.auth.unauthorized")), crate::ErrorKind::Authorization) } async fn check_from_header(header: Option<&HeaderValue>) -> Result<(), Error> { diff --git a/core/src/middleware/auth/session.rs b/core/src/middleware/auth/session.rs index 375351ce8..712298b10 100644 --- a/core/src/middleware/auth/session.rs +++ b/core/src/middleware/auth/session.rs @@ -146,7 +146,7 @@ impl HashSessionToken { } } Err(Error::new( - eyre!("UNAUTHORIZED"), + eyre!("{}", t!("middleware.auth.unauthorized")), crate::ErrorKind::Authorization, )) } @@ -221,7 +221,7 @@ impl ValidSessionToken { } } Err(Error::new( - eyre!("UNAUTHORIZED"), + eyre!("{}", t!("middleware.auth.unauthorized")), crate::ErrorKind::Authorization, )) } @@ -244,7 +244,7 @@ impl ValidSessionToken { C::access_sessions(db) .as_idx_mut(session_hash) .ok_or_else(|| { - Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization) + Error::new(eyre!("{}", t!("middleware.auth.unauthorized")), crate::ErrorKind::Authorization) })? .mutate(|s| { s.last_active = Utc::now(); @@ -305,7 +305,7 @@ impl Middleware for SessionAuth { self.rate_limiter.mutate(|(count, time)| { if time.elapsed() < Duration::from_secs(20) && *count >= 3 { Err(Error::new( - eyre!("Please limit login attempts to 3 per 20 seconds."), + eyre!("{}", t!("middleware.auth.rate-limited-login")), crate::ErrorKind::RateLimited, )) } else { diff --git a/core/src/middleware/auth/signature.rs b/core/src/middleware/auth/signature.rs index 996a4d6a5..ff32b4ddb 100644 --- a/core/src/middleware/auth/signature.rs +++ b/core/src/middleware/auth/signature.rs @@ -90,7 +90,7 @@ impl SignatureAuthContext for RpcContext { } Err(Error::new( - eyre!("Key is not authorized"), + eyre!("{}", t!("middleware.auth.key-not-authorized")), ErrorKind::IncorrectPassword, )) } @@ -141,7 +141,7 @@ impl SignatureAuth { let mut cache = self.nonce_cache.lock().await; if cache.values().any(|n| *n == nonce) { return Err(Error::new( - eyre!("replay attack detected"), + eyre!("{}", t!("middleware.auth.replay-attack-detected")), ErrorKind::Authorization, )); } @@ -226,7 +226,7 @@ impl Middleware for SignatureAuth { context.sig_context().await.into_iter().fold( Err(Error::new( - eyre!("no valid signature context available to verify"), + eyre!("{}", t!("middleware.auth.no-valid-sig-context")), ErrorKind::Authorization, )), |acc, x| { @@ -249,7 +249,7 @@ impl Middleware for SignatureAuth { .unwrap_or_else(|e| e.duration().as_secs() as i64 * -1); if (now - commitment.timestamp).abs() > 30 { return Err(Error::new( - eyre!("timestamp not within 30s of now"), + eyre!("{}", t!("middleware.auth.timestamp-not-within-30s")), ErrorKind::InvalidSignature, )); } @@ -347,6 +347,6 @@ pub async fn call_remote>( .with_kind(ErrorKind::Deserialization)? .result } - _ => Err(Error::new(eyre!("unknown content type"), ErrorKind::Network).into()), + _ => Err(Error::new(eyre!("{}", t!("middleware.auth.unknown-content-type")), ErrorKind::Network).into()), } } diff --git a/core/src/middleware/db.rs b/core/src/middleware/db.rs index ec0b94821..f573dbeb5 100644 --- a/core/src/middleware/db.rs +++ b/core/src/middleware/db.rs @@ -2,6 +2,7 @@ use axum::response::Response; use http::HeaderValue; use http::header::InvalidHeaderValue; use rpc_toolkit::{Middleware, RpcRequest, RpcResponse}; +use rust_i18n::t; use serde::Deserialize; use crate::context::RpcContext; @@ -46,7 +47,7 @@ impl Middleware for SyncDb { } .await { - tracing::error!("error writing X-Patch-Sequence header: {e}"); + tracing::error!("{}", t!("middleware.db.error-writing-patch-sequence-header", error = e)); tracing::debug!("{e:?}"); } } diff --git a/core/src/net/acme.rs b/core/src/net/acme.rs index 1d0874c81..28ba68d96 100644 --- a/core/src/net/acme.rs +++ b/core/src/net/acme.rs @@ -395,7 +395,7 @@ pub fn acme_api() -> ParentHandler { from_fn_async(init) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Setup ACME certificate acquisition") + .with_about("about.setup-acme-certificate-acquisition") .with_call_remote::(), ) .subcommand( @@ -403,7 +403,7 @@ pub fn acme_api() -> ParentHandler { from_fn_async(remove) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Remove ACME certificate acquisition configuration") + .with_about("about.remove-acme-certificate-acquisition-configuration") .with_call_remote::(), ) } diff --git a/core/src/net/dns.rs b/core/src/net/dns.rs index d2a3729d9..0d78ca472 100644 --- a/core/src/net/dns.rs +++ b/core/src/net/dns.rs @@ -54,13 +54,13 @@ pub fn dns_api() -> ParentHandler { Ok(()) }) - .with_about("Test the DNS configuration for a domain"), + .with_about("about.test-dns-configuration-for-domain"), ) .subcommand( "set-static", from_fn_async(set_static_dns) .no_display() - .with_about("Set static DNS servers") + .with_about("about.set-static-dns-servers") .with_call_remote::(), ) .subcommand( @@ -88,7 +88,7 @@ pub fn dns_api() -> ParentHandler { Ok(()) }) - .with_about("Dump address resolution table") + .with_about("about.dump-address-resolution-table") .with_call_remote::(), ) } @@ -292,7 +292,7 @@ impl Resolver { .await .map_err(|_| { Error::new( - eyre!("timed out waiting to update dns catalog"), + eyre!("{}", t!("net.dns.timeout-updating-catalog")), ErrorKind::Timeout, ) })?; @@ -348,7 +348,13 @@ impl Resolver { }) { return Some(res); } else { - tracing::warn!("Could not determine source interface of {src}"); + tracing::warn!( + "{}", + t!( + "net.dns.could-not-determine-source-interface", + src = src.to_string() + ) + ); } } if STARTOS.zone_of(name) || EMBASSY.zone_of(name) { @@ -473,7 +479,10 @@ impl RequestHandler for Resolver { Ok(Some(a)) => return a, Ok(None) => (), Err(e) => { - tracing::error!("Error resolving internal DNS: {e}"); + tracing::error!( + "{}", + t!("net.dns.error-resolving-internal", error = e.to_string()) + ); tracing::debug!("{e:?}"); let mut header = Header::response_from_request(request.header()); header.set_recursion_available(true); @@ -557,7 +566,7 @@ impl DnsController { }) } else { Err(Error::new( - eyre!("DNS Server Thread has exited"), + eyre!("{}", t!("net.dns.server-thread-exited")), crate::ErrorKind::Network, )) } @@ -577,7 +586,7 @@ impl DnsController { }) } else { Err(Error::new( - eyre!("DNS Server Thread has exited"), + eyre!("{}", t!("net.dns.server-thread-exited")), crate::ErrorKind::Network, )) } @@ -598,7 +607,7 @@ impl DnsController { }) } else { Err(Error::new( - eyre!("DNS Server Thread has exited"), + eyre!("{}", t!("net.dns.server-thread-exited")), crate::ErrorKind::Network, )) } @@ -624,7 +633,7 @@ impl DnsController { }) } else { Err(Error::new( - eyre!("DNS Server Thread has exited"), + eyre!("{}", t!("net.dns.server-thread-exited")), crate::ErrorKind::Network, )) } diff --git a/core/src/net/forward.rs b/core/src/net/forward.rs index 25f0b2116..77a8171af 100644 --- a/core/src/net/forward.rs +++ b/core/src/net/forward.rs @@ -34,7 +34,7 @@ impl AvailablePorts { pub fn alloc(&mut self) -> Result { self.0.request_id().ok_or_else(|| { Error::new( - eyre!("No more dynamic ports available!"), + eyre!("{}", t!("net.forward.no-dynamic-ports-available")), ErrorKind::Network, ) }) @@ -240,7 +240,7 @@ impl PortForwardController { } .await { - tracing::error!("error initializing PortForwardController: {e:#}"); + tracing::error!("{}", t!("net.forward.error-initializing-controller", error = format!("{e:#}"))); tracing::debug!("{e:?}"); tokio::time::sleep(Duration::from_secs(5)).await; } @@ -400,7 +400,7 @@ impl InterfaceForwardEntry { ) -> Result, Error> { if external != self.external { return Err(Error::new( - eyre!("Mismatched external port in InterfaceForwardEntry"), + eyre!("{}", t!("net.forward.mismatched-external-port")), ErrorKind::InvalidRequest, )); } @@ -477,7 +477,7 @@ impl InterfaceForwardState { fn err_has_exited(_: T) -> Error { Error::new( - eyre!("PortForwardController thread has exited"), + eyre!("{}", t!("net.forward.controller-thread-exited")), ErrorKind::Unknown, ) } diff --git a/core/src/net/gateway.rs b/core/src/net/gateway.rs index 7c7685ff8..68d6835e0 100644 --- a/core/src/net/gateway.rs +++ b/core/src/net/gateway.rs @@ -95,7 +95,7 @@ pub fn gateway_api() -> ParentHandler { Ok(()) }) - .with_about("Show gateways StartOS can listen on") + .with_about("about.show-gateways-startos-can-listen-on") .with_call_remote::(), ) .subcommand( @@ -103,7 +103,7 @@ pub fn gateway_api() -> ParentHandler { from_fn_async(set_public) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Indicate whether this gateway has inbound access from the WAN") + .with_about("about.indicate-gateway-inbound-access-from-wan") .with_call_remote::(), ) .subcommand( @@ -111,10 +111,7 @@ pub fn gateway_api() -> ParentHandler { from_fn_async(unset_public) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about(concat!( - "Allow this gateway to infer whether it has", - " inbound access from the WAN based on its IPv4 address" - )) + .with_about("about.allow-gateway-infer-inbound-access-from-wan") .with_call_remote::(), ) .subcommand( @@ -122,7 +119,7 @@ pub fn gateway_api() -> ParentHandler { from_fn_async(forget_iface) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Forget a disconnected gateway") + .with_about("about.forget-disconnected-gateway") .with_call_remote::(), ) .subcommand( @@ -130,7 +127,7 @@ pub fn gateway_api() -> ParentHandler { from_fn_async(set_name) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Rename a gateway") + .with_about("about.rename-gateway") .with_call_remote::(), ) } @@ -464,7 +461,8 @@ async fn watcher( ensure_code!( !devices.is_empty(), ErrorKind::Network, - "NetworkManager returned no devices. Trying again..." + "{}", + t!("net.gateway.no-devices-returned") ); let mut ifaces = BTreeSet::new(); let mut jobs = Vec::new(); @@ -731,7 +729,8 @@ async fn watch_ip( Ok(a) => a, Err(e) => { tracing::error!( - "Failed to determine WAN IP for {iface}: {e}" + "{}", + t!("net.gateway.failed-to-determine-wan-ip", iface = iface.to_string(), error = e.to_string()) ); tracing::debug!("{e:?}"); None @@ -1021,7 +1020,13 @@ impl NetworkInterfaceController { info } Err(e) => { - tracing::error!("Error loading network interface info: {e}"); + tracing::error!( + "{}", + t!( + "net.gateway.error-loading-interface-info", + error = e.to_string() + ) + ); tracing::debug!("{e:?}"); OrdMap::new() } @@ -1050,7 +1055,10 @@ impl NetworkInterfaceController { } .await { - tracing::error!("Error syncing ip info to db: {e}"); + tracing::error!( + "{}", + t!("net.gateway.error-syncing-ip-info", error = e.to_string()) + ); tracing::debug!("{e:?}"); } @@ -1060,7 +1068,10 @@ impl NetworkInterfaceController { } .await; if let Err(e) = res { - tracing::error!("Error syncing ip info to db: {e}"); + tracing::error!( + "{}", + t!("net.gateway.error-syncing-ip-info", error = e.to_string()) + ); tracing::debug!("{e:?}"); } }) @@ -1121,7 +1132,7 @@ impl NetworkInterfaceController { .map_or(false, |i| i.ip_info.is_some()) { err = Some(Error::new( - eyre!("Cannot forget currently connected interface"), + eyre!("{}", t!("net.gateway.cannot-forget-connected-interface")), ErrorKind::InvalidRequest, )); return false; @@ -1167,7 +1178,7 @@ impl NetworkInterfaceController { if &*ac == "/" { return Err(Error::new( - eyre!("Cannot delete device without active connection"), + eyre!("{}", t!("net.gateway.cannot-delete-without-connection")), ErrorKind::InvalidRequest, )); } diff --git a/core/src/net/host/address.rs b/core/src/net/host/address.rs index 296b8a934..5f358a920 100644 --- a/core/src/net/host/address.rs +++ b/core/src/net/host/address.rs @@ -120,7 +120,7 @@ pub fn address_api() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() - .with_about("Add a public domain to this host") + .with_about("about.add-public-domain-to-host") .with_call_remote::(), ) .subcommand( @@ -129,7 +129,7 @@ pub fn address_api() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() - .with_about("Remove a public domain from this host") + .with_about("about.remove-public-domain-from-host") .with_call_remote::(), ) .with_inherited(|_, a| a), @@ -143,7 +143,7 @@ pub fn address_api() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() - .with_about("Add a private domain to this host") + .with_about("about.add-private-domain-to-host") .with_call_remote::(), ) .subcommand( @@ -152,7 +152,7 @@ pub fn address_api() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() - .with_about("Remove a private domain from this host") + .with_about("about.remove-private-domain-from-host") .with_call_remote::(), ) .with_inherited(|_, a| a), @@ -168,7 +168,7 @@ pub fn address_api() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() - .with_about("Add an address to this host") + .with_about("about.add-address-to-host") .with_call_remote::(), ) .subcommand( @@ -177,7 +177,7 @@ pub fn address_api() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() - .with_about("Remove an address from this host") + .with_about("about.remove-address-from-host") .with_call_remote::(), ) .with_inherited(Kind::inheritance), @@ -230,7 +230,7 @@ pub fn address_api() Ok(()) }) - .with_about("List addresses for this host") + .with_about("about.list-addresses-for-host") .with_call_remote::(), ) } diff --git a/core/src/net/host/binding.rs b/core/src/net/host/binding.rs index a74d351aa..9012ac272 100644 --- a/core/src/net/host/binding.rs +++ b/core/src/net/host/binding.rs @@ -209,7 +209,7 @@ pub fn binding() Ok(()) }) - .with_about("List bindinges for this host") + .with_about("about.list-bindings-for-host") .with_call_remote::(), ) .subcommand( @@ -218,7 +218,7 @@ pub fn binding() .with_metadata("sync_db", Value::Bool(true)) .with_inherited(Kind::inheritance) .no_display() - .with_about("Set whether this gateway should be enabled for this binding") + .with_about("about.set-gateway-enabled-for-binding") .with_call_remote::(), ) } diff --git a/core/src/net/host/mod.rs b/core/src/net/host/mod.rs index 8f9ab15e2..555cdb825 100644 --- a/core/src/net/host/mod.rs +++ b/core/src/net/host/mod.rs @@ -243,7 +243,7 @@ pub fn host_api() -> ParentHandler { } Ok(()) }) - .with_about("List host IDs available for this service"), + .with_about("about.list-host-ids-for-service"), ) .subcommand( "address", diff --git a/core/src/net/mod.rs b/core/src/net/mod.rs index e59f41a56..f30c1383b 100644 --- a/core/src/net/mod.rs +++ b/core/src/net/mod.rs @@ -23,32 +23,29 @@ pub mod wifi; pub fn net_api() -> ParentHandler { ParentHandler::new() - .subcommand( - "tor", - tor::tor_api::().with_about("Tor commands such as list-services, logs, and reset"), - ) + .subcommand("tor", tor::tor_api::().with_about("about.tor-commands")) .subcommand( "acme", - acme::acme_api::().with_about("Setup automatic clearnet certificate acquisition"), + acme::acme_api::().with_about("about.setup-acme-certificate"), ) .subcommand( "dns", - dns::dns_api::().with_about("Manage and query DNS"), + dns::dns_api::().with_about("about.manage-query-dns"), ) .subcommand( "forward", - forward::forward_api::().with_about("Manage port forwards"), + forward::forward_api::().with_about("about.manage-port-forwards"), ) .subcommand( "gateway", - gateway::gateway_api::().with_about("View and edit gateway configurations"), + gateway::gateway_api::().with_about("about.view-edit-gateway-configs"), ) .subcommand( "tunnel", - tunnel::tunnel_api::().with_about("Manage tunnels"), + tunnel::tunnel_api::().with_about("about.manage-tunnels"), ) .subcommand( "vhost", - vhost::vhost_api::().with_about("Manage ssl virtual host proxy"), + vhost::vhost_api::().with_about("about.manage-ssl-vhost-proxy"), ) } diff --git a/core/src/net/ssl.rs b/core/src/net/ssl.rs index 2e5110a18..748abb493 100644 --- a/core/src/net/ssl.rs +++ b/core/src/net/ssl.rs @@ -170,7 +170,7 @@ impl FullchainCertData { ] .into_iter() .min() - .ok_or_else(|| Error::new(eyre!("unreachable"), ErrorKind::Unknown)) + .ok_or_else(|| Error::new(eyre!("{}", t!("net.ssl.unreachable")), ErrorKind::Unknown)) } } diff --git a/core/src/net/tor/arti.rs b/core/src/net/tor/arti.rs index 8c9f54c4f..d3919c3c6 100644 --- a/core/src/net/tor/arti.rs +++ b/core/src/net/tor/arti.rs @@ -107,7 +107,10 @@ impl TorSecretKey { Ok(Self( tor_llcrypto::pk::ed25519::ExpandedKeypair::from_secret_key_bytes(bytes) .ok_or_else(|| { - Error::new(eyre!("invalid ed25519 expanded secret key"), ErrorKind::Tor) + Error::new( + eyre!("{}", t!("net.tor.invalid-ed25519-key")), + ErrorKind::Tor, + ) })? .into(), )) @@ -226,19 +229,19 @@ pub fn tor_api() -> ParentHandler { from_fn_async(list_services) .with_display_serializable() .with_custom_display_fn(|handle, result| display_services(handle.params, result)) - .with_about("Display Tor V3 Onion Addresses") + .with_about("about.display-tor-v3-onion-addresses") .with_call_remote::(), ) .subcommand( "reset", from_fn_async(reset) .no_display() - .with_about("Reset Tor daemon") + .with_about("about.reset-tor-daemon") .with_call_remote::(), ) .subcommand( "key", - key::().with_about("Manage the onion service key store"), + key::().with_about("about.manage-onion-service-key-store"), ) } @@ -247,13 +250,13 @@ pub fn key() -> ParentHandler { .subcommand( "generate", from_fn_async(generate_key) - .with_about("Generate an onion service key and add it to the key store") + .with_about("about.generate-onion-service-key-add-to-store") .with_call_remote::(), ) .subcommand( "add", from_fn_async(add_key) - .with_about("Add an onion service key to the key store") + .with_about("about.add-onion-service-key-to-store") .with_call_remote::(), ) .subcommand( @@ -265,7 +268,7 @@ pub fn key() -> ParentHandler { } Ok(()) }) - .with_about("List onion services with keys in the key store") + .with_about("about.list-onion-services-with-keys-in-store") .with_call_remote::(), ) } @@ -447,9 +450,13 @@ impl TorController { if prev_inst.elapsed() > BOOTSTRAP_PROGRESS_TIMEOUT { return Err(Error::new( eyre!( - "Bootstrap has not made progress for {}", - crate::util::serde::Duration::from( - BOOTSTRAP_PROGRESS_TIMEOUT + "{}", + t!( + "net.tor.bootstrap-no-progress", + duration = crate::util::serde::Duration::from( + BOOTSTRAP_PROGRESS_TIMEOUT + ) + .to_string() ) ), ErrorKind::Tor, @@ -466,7 +473,10 @@ impl TorController { res = bootstrap_fut => res, res = failure_fut => res, } { - tracing::error!("Tor Bootstrap Error: {e}"); + tracing::error!( + "{}", + t!("net.tor.bootstrap-error", error = e.to_string()) + ); tracing::debug!("{e:?}"); } else { bootstrapper_client.send_modify(|_| ()); @@ -516,7 +526,13 @@ impl TorController { } .await { - tracing::error!("Tor Health Error: {e}"); + tracing::error!( + "{}", + t!( + "net.tor.health-error", + error = e.to_string() + ) + ); tracing::debug!("{e:?}"); } }); @@ -529,7 +545,10 @@ impl TorController { } } Err(Error::new( - eyre!("status event stream ended"), + eyre!( + "{}", + t!("net.tor.status-stream-ended") + ), ErrorKind::Tor, )) }) @@ -560,13 +579,19 @@ impl TorController { } .await { - tracing::error!("Tor Client Health Error: {e}"); + tracing::error!( + "{}", + t!("net.tor.client-health-error", error = e.to_string()) + ); tracing::debug!("{e:?}"); } } tracing::error!( - "Client failed health check {} times, recycling", - HEALTH_CHECK_FAILURE_ALLOWANCE + "{}", + t!( + "net.tor.health-check-failed-recycling", + count = HEALTH_CHECK_FAILURE_ALLOWANCE + ) ); } @@ -574,7 +599,10 @@ impl TorController { }) .await { - tracing::error!("Tor Bootstrapper Error: {e}"); + tracing::error!( + "{}", + t!("net.tor.bootstrapper-error", error = e.to_string()) + ); tracing::debug!("{e:?}"); } if let Err::<(), Error>(e) = async { @@ -592,7 +620,10 @@ impl TorController { } .await { - tracing::error!("Tor Client Creation Error: {e}"); + tracing::error!( + "{}", + t!("net.tor.client-creation-error", error = e.to_string()) + ); tracing::debug!("{e:?}"); } } @@ -684,7 +715,10 @@ impl TorController { .await .with_kind(ErrorKind::Network)?; if let Err(e) = socket2::SockRef::from(&tcp_stream).set_keepalive(true) { - tracing::error!("Failed to set tcp keepalive: {e}"); + tracing::error!( + "{}", + t!("net.tor.failed-to-set-tcp-keepalive", error = e.to_string()) + ); tracing::debug!("{e:?}"); } Ok(Box::new(tcp_stream)) @@ -812,7 +846,7 @@ impl OnionService { .await .with_kind(ErrorKind::Network)?; if let Err(e) = socket2::SockRef::from(&outgoing).set_keepalive(true) { - tracing::error!("Failed to set tcp keepalive: {e}"); + tracing::error!("{}", t!("net.tor.failed-to-set-tcp-keepalive", error = e.to_string())); tracing::debug!("{e:?}"); } let mut incoming = req @@ -852,7 +886,7 @@ impl OnionService { } .await { - tracing::error!("Tor Client Error: {e}"); + tracing::error!("{}", t!("net.tor.client-error", error = e.to_string())); tracing::debug!("{e:?}"); } } diff --git a/core/src/net/tor/ctor.rs b/core/src/net/tor/ctor.rs index 726e0a9f9..f6c34305d 100644 --- a/core/src/net/tor/ctor.rs +++ b/core/src/net/tor/ctor.rs @@ -249,26 +249,26 @@ pub fn tor_api() -> ParentHandler { from_fn_async(list_services) .with_display_serializable() .with_custom_display_fn(|handle, result| display_services(handle.params, result)) - .with_about("Display Tor V3 Onion Addresses") + .with_about("about.display-tor-v3-onion-addresses") .with_call_remote::(), ) - .subcommand("logs", logs().with_about("Display Tor logs")) + .subcommand("logs", logs().with_about("about.display-tor-logs")) .subcommand( "logs", from_fn_async(crate::logs::cli_logs::) .no_display() - .with_about("Display Tor logs"), + .with_about("about.display-tor-logs"), ) .subcommand( "reset", from_fn_async(reset) .no_display() - .with_about("Reset Tor daemon") + .with_about("about.reset-tor-daemon") .with_call_remote::(), ) .subcommand( "key", - key::().with_about("Manage the onion service key store"), + key::().with_about("about.manage-onion-service-key-store"), ) } @@ -277,13 +277,13 @@ pub fn key() -> ParentHandler { .subcommand( "generate", from_fn_async(generate_key) - .with_about("Generate an onion service key and add it to the key store") + .with_about("about.generate-onion-service-key-add-to-store") .with_call_remote::(), ) .subcommand( "add", from_fn_async(add_key) - .with_about("Add an onion service key to the key store") + .with_about("about.add-onion-service-key-to-store") .with_call_remote::(), ) .subcommand( @@ -295,7 +295,7 @@ pub fn key() -> ParentHandler { } Ok(()) }) - .with_about("List onion services with keys in the key store") + .with_about("about.list-onion-services-with-keys-in-store") .with_call_remote::(), ) } diff --git a/core/src/net/tunnel.rs b/core/src/net/tunnel.rs index 64e1ee284..6a9c864e1 100644 --- a/core/src/net/tunnel.rs +++ b/core/src/net/tunnel.rs @@ -19,14 +19,14 @@ pub fn tunnel_api() -> ParentHandler { .subcommand( "add", from_fn_async(add_tunnel) - .with_about("Add a new tunnel") + .with_about("about.add-new-tunnel") .with_call_remote::(), ) .subcommand( "remove", from_fn_async(remove_tunnel) .no_display() - .with_about("Remove a tunnel") + .with_about("about.remove-tunnel") .with_call_remote::(), ) } diff --git a/core/src/net/wifi.rs b/core/src/net/wifi.rs index 6d6774998..47b02ed35 100644 --- a/core/src/net/wifi.rs +++ b/core/src/net/wifi.rs @@ -30,7 +30,7 @@ type WifiManager = Arc>>; // Ok(wifi_manager) // } else { // Err(Error::new( -// color_eyre::eyre::eyre!("No WiFi interface available"), +// color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), // ErrorKind::Wifi, // )) // } @@ -42,28 +42,28 @@ pub fn wifi() -> ParentHandler { "set-enabled", from_fn_async(set_enabled) .no_display() - .with_about("Enable or disable wifi") + .with_about("about.enable-disable-wifi") .with_call_remote::(), ) .subcommand( "add", from_fn_async(add) .no_display() - .with_about("Add wifi ssid and password") + .with_about("about.add-wifi-ssid-password") .with_call_remote::(), ) .subcommand( "connect", from_fn_async(connect) .no_display() - .with_about("Connect to wifi network") + .with_about("about.connect-wifi-network") .with_call_remote::(), ) .subcommand( "remove", from_fn_async(remove) .no_display() - .with_about("Remove a wifi network") + .with_about("about.remove-wifi-network") .with_call_remote::(), ) .subcommand( @@ -71,16 +71,16 @@ pub fn wifi() -> ParentHandler { from_fn_async(get) .with_display_serializable() .with_custom_display_fn(|handle, result| display_wifi_info(handle.params, result)) - .with_about("List wifi info") + .with_about("about.list-wifi-info") .with_call_remote::(), ) .subcommand( "country", - country::().with_about("Command to set country"), + country::().with_about("about.command-set-country"), ) .subcommand( "available", - available::().with_about("Command to list available wifi networks"), + available::().with_about("about.command-list-available-wifi"), ) } @@ -133,7 +133,7 @@ pub fn available() -> ParentHandler { from_fn_async(get_available) .with_display_serializable() .with_custom_display_fn(|handle, result| display_wifi_list(handle.params, result)) - .with_about("List available wifi networks") + .with_about("about.list-available-wifi-networks") .with_call_remote::(), ) } @@ -143,7 +143,7 @@ pub fn country() -> ParentHandler { "set", from_fn_async(set_country) .no_display() - .with_about("Set Country") + .with_about("about.set-country") .with_call_remote::(), ) } @@ -160,13 +160,13 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re let wifi_manager = ctx.wifi_manager.clone(); if !ssid.is_ascii() { return Err(Error::new( - color_eyre::eyre::eyre!("SSID may not have special characters"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")), ErrorKind::Wifi, )); } if !password.is_ascii() { return Err(Error::new( - color_eyre::eyre::eyre!("WiFi Password may not have special characters"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.password-no-special-characters")), ErrorKind::Wifi, )); } @@ -176,11 +176,11 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re ssid: &Ssid, password: &Psk, ) -> Result<(), Error> { - tracing::info!("Adding new WiFi network: '{}'", ssid.0); + tracing::info!("{}", t!("net.wifi.adding-network", ssid = &ssid.0)); let mut wpa_supplicant = wifi_manager.write_owned().await; let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("No WiFi interface available"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), ErrorKind::Wifi, ) })?; @@ -195,10 +195,17 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re ) .await { - tracing::error!("Failed to add new WiFi network '{}': {}", ssid, err); + tracing::error!( + "{}", + t!( + "net.wifi.failed-to-add-network", + ssid = &ssid, + error = err.to_string() + ) + ); tracing::debug!("{:?}", err); return Err(Error::new( - color_eyre::eyre::eyre!("Failed adding {}", ssid), + color_eyre::eyre::eyre!("{}", t!("net.wifi.failed-adding", ssid = &ssid)), ErrorKind::Wifi, )); } @@ -230,7 +237,7 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result let wifi_manager = ctx.wifi_manager.clone(); if !ssid.is_ascii() { return Err(Error::new( - color_eyre::eyre::eyre!("SSID may not have special characters"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")), ErrorKind::Wifi, )); } @@ -242,19 +249,19 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result let mut wpa_supplicant = wifi_manager.write_owned().await; let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("No WiFi interface available"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), ErrorKind::Wifi, ) })?; let current = wpa_supplicant.get_current_network().await?; let connected = wpa_supplicant.select_network(db.clone(), ssid).await?; if connected { - tracing::info!("Successfully connected to WiFi: '{}'", ssid.0); + tracing::info!("{}", t!("net.wifi.connected-successfully", ssid = &ssid.0)); } else { - tracing::info!("Failed to connect to WiFi: '{}'", ssid.0); + tracing::info!("{}", t!("net.wifi.connection-failed", ssid = &ssid.0)); match current { None => { - tracing::info!("No WiFi to revert to!"); + tracing::info!("{}", t!("net.wifi.no-wifi-to-revert")); } Some(current) => { wpa_supplicant.select_network(db, ¤t).await?; @@ -267,9 +274,16 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result if let Err(err) = connect_procedure(ctx.db.clone(), wifi_manager.clone(), &Ssid(ssid.clone())).await { - tracing::error!("Failed to connect to WiFi network '{}': {}", &ssid, err); + tracing::error!( + "{}", + t!( + "net.wifi.failed-to-connect", + ssid = &ssid, + error = err.to_string() + ) + ); return Err(Error::new( - color_eyre::eyre::eyre!("Can't connect to {}", ssid), + color_eyre::eyre::eyre!("{}", t!("net.wifi.cant-connect", ssid = &ssid)), ErrorKind::Wifi, )); } @@ -297,7 +311,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result< let wifi_manager = ctx.wifi_manager.clone(); if !ssid.is_ascii() { return Err(Error::new( - color_eyre::eyre::eyre!("SSID may not have special characters"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")), ErrorKind::Wifi, )); } @@ -305,7 +319,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result< let mut wpa_supplicant = wifi_manager.write_owned().await; let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("No WiFi interface available"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), ErrorKind::Wifi, ) })?; @@ -316,9 +330,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result< is_current_being_removed && !interface_connected(&ctx.ethernet_interface).await?; if is_current_removed_and_no_hardwire { return Err(Error::new( - color_eyre::eyre::eyre!( - "Forbidden: Deleting this network would make your server unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this." - ), + color_eyre::eyre::eyre!("{}", t!("net.wifi.forbidden-delete-would-disconnect")), ErrorKind::Wifi, )); } @@ -463,7 +475,7 @@ pub async fn get(ctx: RpcContext, _: Empty) -> Result { let wpa_supplicant = wifi_manager.read_owned().await; let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("No WiFi interface available"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), ErrorKind::Wifi, ) })?; @@ -517,7 +529,7 @@ pub async fn get_available(ctx: RpcContext, _: Empty) -> Result let wpa_supplicant = wifi_manager.read_owned().await; let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("No WiFi interface available"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), ErrorKind::Wifi, ) })?; @@ -558,14 +570,14 @@ pub async fn set_country( let wifi_manager = ctx.wifi_manager.clone(); if !interface_connected(&ctx.ethernet_interface).await? { return Err(Error::new( - color_eyre::eyre::eyre!("Won't change country without hardwire connection"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.wont-change-country-without-ethernet")), crate::ErrorKind::Wifi, )); } let mut wpa_supplicant = wifi_manager.write_owned().await; let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("No WiFi interface available"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), ErrorKind::Wifi, ) })?; @@ -684,7 +696,14 @@ impl WpaCli { .await .map(|_| ()) .unwrap_or_else(|e| { - tracing::warn!("Failed to set interface {} for {}", self.interface, ssid.0); + tracing::warn!( + "{}", + t!( + "net.wifi.failed-to-set-interface", + interface = &self.interface, + ssid = &ssid.0 + ) + ); tracing::debug!("{:?}", e); }); Command::new("nmcli") @@ -719,13 +738,13 @@ impl WpaCli { } let first_country = r.lines().find(|s| s.contains("country")).ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("Could not find a country config lines"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.could-not-find-country-config")), ErrorKind::Wifi, ) })?; let country = &RE.captures(first_country).ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("Could not find a country config with regex"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.could-not-parse-country-config")), ErrorKind::Wifi, ) })?[1]; @@ -734,7 +753,10 @@ impl WpaCli { } else { Ok(Some(CountryCode::for_alpha2(country).map_err(|_| { Error::new( - color_eyre::eyre::eyre!("Invalid Country Code: {}", country), + color_eyre::eyre::eyre!( + "{}", + t!("net.wifi.invalid-country-code", country = country) + ), ErrorKind::Wifi, ) })?)) @@ -877,7 +899,7 @@ impl WpaCli { let m_id = self.check_active_network(ssid).await?; match m_id { None => Err(Error::new( - color_eyre::eyre::eyre!("SSID Not Found"), + color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-not-found")), ErrorKind::Wifi, )), Some(x) => { @@ -1058,7 +1080,7 @@ pub async fn synchronize_network_manager>( .invoke(ErrorKind::Wifi) .await?; if let Some(last_country_code) = wifi.last_region { - tracing::info!("Setting the region"); + tracing::info!("{}", t!("net.wifi.setting-region")); let _ = Command::new("iw") .arg("reg") .arg("set") @@ -1066,7 +1088,7 @@ pub async fn synchronize_network_manager>( .invoke(ErrorKind::Wifi) .await?; } else { - tracing::info!("Setting the region fallback"); + tracing::info!("{}", t!("net.wifi.setting-region-fallback")); let _ = Command::new("iw") .arg("reg") .arg("set") diff --git a/core/src/notifications.rs b/core/src/notifications.rs index 4f440792c..291259b58 100644 --- a/core/src/notifications.rs +++ b/core/src/notifications.rs @@ -27,49 +27,49 @@ pub fn notification() -> ParentHandler { "list", from_fn_async(list) .with_display_serializable() - .with_about("List notifications") + .with_about("about.list-notifications") .with_call_remote::(), ) .subcommand( "remove", from_fn_async(remove) .no_display() - .with_about("Remove notification for given ids") + .with_about("about.remove-notification-for-ids") .with_call_remote::(), ) .subcommand( "remove-before", from_fn_async(remove_before) .no_display() - .with_about("Remove notifications preceding a given id") + .with_about("about.remove-notifications-before-id") .with_call_remote::(), ) .subcommand( "mark-seen", from_fn_async(mark_seen) .no_display() - .with_about("Mark given notifications as seen") + .with_about("about.mark-notifications-seen") .with_call_remote::(), ) .subcommand( "mark-seen-before", from_fn_async(mark_seen_before) .no_display() - .with_about("Mark notifications preceding a given id as seen") + .with_about("about.mark-notifications-seen-before-id") .with_call_remote::(), ) .subcommand( "mark-unseen", from_fn_async(mark_unseen) .no_display() - .with_about("Mark given notifications as unseen") + .with_about("about.mark-notifications-unseen") .with_call_remote::(), ) .subcommand( "create", from_fn_async(create) .no_display() - .with_about("Persist a newly created notification") + .with_about("about.persist-new-notification") .with_call_remote::(), ) } @@ -346,7 +346,7 @@ pub struct InvalidNotificationLevel(String); impl From for crate::Error { fn from(val: InvalidNotificationLevel) -> Self { Error::new( - eyre!("Invalid Notification Level: {}", val.0), + eyre!("{}", t!("notifications.invalid-level", level = val.0)), ErrorKind::ParseDbField, ) } diff --git a/core/src/prelude.rs b/core/src/prelude.rs index 369890500..e2adf9eeb 100644 --- a/core/src/prelude.rs +++ b/core/src/prelude.rs @@ -1,6 +1,7 @@ pub use color_eyre::eyre::eyre; pub use imbl_value::InternedString; pub use lazy_format::lazy_format; +pub use rust_i18n::t; pub use tracing::instrument; pub use crate::db::prelude::*; diff --git a/core/src/registry/admin.rs b/core/src/registry/admin.rs index 21a1f5e51..c223ef0ca 100644 --- a/core/src/registry/admin.rs +++ b/core/src/registry/admin.rs @@ -21,7 +21,7 @@ pub fn admin_api() -> ParentHandler { ParentHandler::new() .subcommand( "signer", - signers_api::().with_about("Commands to add or list signers"), + signers_api::().with_about("about.commands-add-list-signers"), ) .subcommand( "add", @@ -33,14 +33,14 @@ pub fn admin_api() -> ParentHandler { "add", from_fn_async(cli_add_admin) .no_display() - .with_about("Add admin signer"), + .with_about("about.add-admin-signer"), ) .subcommand( "remove", from_fn_async(remove_admin) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Remove an admin signer") + .with_about("about.remove-admin-signer") .with_call_remote::(), ) .subcommand( @@ -49,7 +49,7 @@ pub fn admin_api() -> ParentHandler { .with_metadata("admin", Value::Bool(true)) .with_display_serializable() .with_custom_display_fn(|handle, result| display_signers(handle.params, result)) - .with_about("List admin signers") + .with_about("about.list-admin-signers") .with_call_remote::(), ) } @@ -62,7 +62,7 @@ fn signers_api() -> ParentHandler { .with_metadata("admin", Value::Bool(true)) .with_display_serializable() .with_custom_display_fn(|handle, result| display_signers(handle.params, result)) - .with_about("List signers") + .with_about("about.list-signers") .with_call_remote::(), ) .subcommand( @@ -73,7 +73,7 @@ fn signers_api() -> ParentHandler { ) .subcommand( "add", - from_fn_async(cli_add_signer).with_about("Add signer"), + from_fn_async(cli_add_signer).with_about("about.add-signer"), ) .subcommand( "edit", @@ -93,7 +93,7 @@ impl Model> { .next() .transpose()? .map(|(a, _)| a) - .ok_or_else(|| Error::new(eyre!("unknown signer"), ErrorKind::Authorization)) + .ok_or_else(|| Error::new(eyre!("{}", t!("registry.admin.unknown-signer")), ErrorKind::Authorization)) } pub fn get_signer_info(&self, key: &AnyVerifyingKey) -> Result<(Guid, SignerInfo), Error> { @@ -103,7 +103,7 @@ impl Model> { .filter_ok(|(_, s)| s.keys.contains(key)) .next() .transpose()? - .ok_or_else(|| Error::new(eyre!("unknown signer"), ErrorKind::Authorization)) + .ok_or_else(|| Error::new(eyre!("{}", t!("registry.admin.unknown-signer")), ErrorKind::Authorization)) } pub fn add_signer(&mut self, signer: &SignerInfo) -> Result { @@ -117,9 +117,8 @@ impl Model> { { return Err(Error::new( eyre!( - "A signer {} ({}) already exists with a matching key", - guid, - s.name + "{}", + t!("registry.admin.signer-already-exists", guid = guid, name = s.name) ), ErrorKind::InvalidRequest, )); diff --git a/core/src/registry/asset.rs b/core/src/registry/asset.rs index 03f320c41..936e29d81 100644 --- a/core/src/registry/asset.rs +++ b/core/src/registry/asset.rs @@ -49,7 +49,7 @@ impl RegistryAsset { } } Err(Error::new( - eyre!("Failed to load any http url"), + eyre!("{}", t!("registry.asset.failed-to-load-http-url")), ErrorKind::Network, )) } @@ -64,7 +64,7 @@ impl RegistryAsset { } } Err(Error::new( - eyre!("Failed to load any http url"), + eyre!("{}", t!("registry.asset.failed-to-load-http-url")), ErrorKind::Network, )) } @@ -80,7 +80,7 @@ impl RegistryAsset { } } Err(Error::new( - eyre!("Failed to load any http url"), + eyre!("{}", t!("registry.asset.failed-to-load-http-url")), ErrorKind::Network, )) } diff --git a/core/src/registry/context.rs b/core/src/registry/context.rs index dd598b27d..cbd974e0d 100644 --- a/core/src/registry/context.rs +++ b/core/src/registry/context.rs @@ -124,7 +124,7 @@ impl RegistryContext { }; if config.registry_hostname.is_empty() { return Err(Error::new( - eyre!("missing required configuration: registry-hostname"), + eyre!("{}", t!("registry.context.missing-hostname")), ErrorKind::NotFound, )); } @@ -195,7 +195,7 @@ impl CallRemote for CliContext { url } else { return Err( - Error::new(eyre!("`--registry` required"), ErrorKind::InvalidRequest).into(), + Error::new(eyre!("{}", t!("registry.context.registry-required")), ErrorKind::InvalidRequest).into(), ); }; @@ -330,7 +330,7 @@ impl SignatureAuthContext for RegistryContext { } } - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.context.unauthorized")), ErrorKind::Authorization)) } async fn post_auth_hook( &self, diff --git a/core/src/registry/db.rs b/core/src/registry/db.rs index 3c3da4c12..4a507bba0 100644 --- a/core/src/registry/db.rs +++ b/core/src/registry/db.rs @@ -22,7 +22,7 @@ pub fn db_api() -> ParentHandler { "dump", from_fn_async(cli_dump) .with_display_serializable() - .with_about("Filter/query db to display tables and records"), + .with_about("about.filter-query-db"), ) .subcommand( "dump", @@ -34,7 +34,7 @@ pub fn db_api() -> ParentHandler { "apply", from_fn_async(cli_apply) .no_display() - .with_about("Update a db record"), + .with_about("about.update-db-record"), ) .subcommand( "apply", diff --git a/core/src/registry/info.rs b/core/src/registry/info.rs index 5d18885e4..296397478 100644 --- a/core/src/registry/info.rs +++ b/core/src/registry/info.rs @@ -21,7 +21,7 @@ pub fn info_api() -> ParentHandler> { from_fn_async(get_info) .with_metadata("authenticated", Value::Bool(false)) .with_display_serializable() - .with_about("Display registry name, icon, and package categories") + .with_about("about.display-registry-info") .with_call_remote::(), ) .subcommand( @@ -29,7 +29,7 @@ pub fn info_api() -> ParentHandler> { from_fn_async(set_name) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Set the name for the registry") + .with_about("about.set-registry-name") .with_call_remote::(), ) .subcommand( @@ -42,7 +42,7 @@ pub fn info_api() -> ParentHandler> { "set-icon", from_fn_async(cli_set_icon) .no_display() - .with_about("Set the icon for the registry"), + .with_about("about.set-registry-icon"), ) } diff --git a/core/src/registry/mod.rs b/core/src/registry/mod.rs index bed299586..b0cbb1326 100644 --- a/core/src/registry/mod.rs +++ b/core/src/registry/mod.rs @@ -76,26 +76,26 @@ pub fn registry_api() -> ParentHandler { "index", from_fn_async(get_full_index) .with_display_serializable() - .with_about("List info including registry name and packages") + .with_about("about.list-registry-info-packages") .with_call_remote::(), ) .subcommand("info", info::info_api::()) // set info and categories .subcommand( "os", - os::os_api::().with_about("Commands related to OS assets and versions"), + os::os_api::().with_about("about.commands-os-assets-versions"), ) .subcommand( "package", - package::package_api::().with_about("Commands to index, add, or get packages"), + package::package_api::().with_about("about.commands-index-add-get-packages"), ) .subcommand( "admin", - admin::admin_api::().with_about("Commands to add or list admins or signers"), + admin::admin_api::().with_about("about.commands-add-list-admins-signers"), ) .subcommand( "db", - db::db_api::().with_about("Commands to interact with the db i.e. dump and apply"), + db::db_api::().with_about("about.commands-registry-db"), ) } diff --git a/core/src/registry/os/asset/add.rs b/core/src/registry/os/asset/add.rs index 0f1d2f061..2aa5cdb4b 100644 --- a/core/src/registry/os/asset/add.rs +++ b/core/src/registry/os/asset/add.rs @@ -141,7 +141,7 @@ async fn add_asset( .mutate(|s| { if s.commitment != commitment { Err(Error::new( - eyre!("commitment does not match"), + eyre!("{}", t!("registry.os.asset.commitment-mismatch")), ErrorKind::InvalidSignature, )) } else { @@ -154,7 +154,7 @@ async fn add_asset( })?; Ok(()) } else { - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.os.asset.unauthorized")), ErrorKind::Authorization)) } }) .await @@ -208,7 +208,7 @@ pub async fn cli_add_asset( Some("squashfs") => "squashfs", _ => { return Err(Error::new( - eyre!("Unknown extension"), + eyre!("{}", t!("registry.os.asset.unknown-extension")), ErrorKind::InvalidRequest, )); } @@ -232,7 +232,7 @@ pub async fn cli_add_asset( let size = file .size() .await - .ok_or_else(|| Error::new(eyre!("failed to read file metadata"), ErrorKind::Filesystem))?; + .ok_or_else(|| Error::new(eyre!("{}", t!("registry.os.asset.failed-read-metadata")), ErrorKind::Filesystem))?; let commitment = Blake3Commitment { hash: Base64(*blake3.as_bytes()), size, @@ -334,7 +334,7 @@ async fn remove_asset( .remove(&platform)?; Ok(()) } else { - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.os.asset.unauthorized")), ErrorKind::Authorization)) } }) .await diff --git a/core/src/registry/os/asset/get.rs b/core/src/registry/os/asset/get.rs index 5904e9b0d..15d41e93c 100644 --- a/core/src/registry/os/asset/get.rs +++ b/core/src/registry/os/asset/get.rs @@ -34,7 +34,7 @@ pub fn get_api() -> ParentHandler { "iso", from_fn_async(cli_get_os_asset) .no_display() - .with_about("Download iso"), + .with_about("about.download-iso"), ) .subcommand( "img", @@ -46,7 +46,7 @@ pub fn get_api() -> ParentHandler { "img", from_fn_async(cli_get_os_asset) .no_display() - .with_about("Download img"), + .with_about("about.download-img"), ) .subcommand( "squashfs", @@ -58,7 +58,7 @@ pub fn get_api() -> ParentHandler { "squashfs", from_fn_async(cli_get_os_asset) .no_display() - .with_about("Download squashfs"), + .with_about("about.download-squashfs"), ) } diff --git a/core/src/registry/os/asset/mod.rs b/core/src/registry/os/asset/mod.rs index 9fbb193f2..a2fcd72a8 100644 --- a/core/src/registry/os/asset/mod.rs +++ b/core/src/registry/os/asset/mod.rs @@ -11,7 +11,7 @@ pub fn asset_api() -> ParentHandler { "add", from_fn_async(add::cli_add_asset) .no_display() - .with_about("Add asset to registry"), + .with_about("about.add-asset-registry"), ) .subcommand("remove", add::remove_api::()) .subcommand("sign", sign::sign_api::()) @@ -19,10 +19,10 @@ pub fn asset_api() -> ParentHandler { "sign", from_fn_async(sign::cli_sign_asset) .no_display() - .with_about("Sign file and add to registry index"), + .with_about("about.sign-file-add-registry"), ) .subcommand( "get", - get::get_api::().with_about("Commands to download image, iso, or squashfs files"), + get::get_api::().with_about("about.commands-download-assets"), ) } diff --git a/core/src/registry/os/asset/sign.rs b/core/src/registry/os/asset/sign.rs index 68c0f571c..91d9ecf40 100644 --- a/core/src/registry/os/asset/sign.rs +++ b/core/src/registry/os/asset/sign.rs @@ -89,7 +89,7 @@ async fn sign_asset( .contains(&guid) { return Err(Error::new( - eyre!("signer {guid} is not authorized"), + eyre!("{}", t!("registry.os.asset.signer-not-authorized", guid = guid)), ErrorKind::Authorization, )); } @@ -163,7 +163,7 @@ pub async fn cli_sign_asset( Some("squashfs") => "squashfs", _ => { return Err(Error::new( - eyre!("Unknown extension"), + eyre!("{}", t!("registry.os.asset.unknown-extension")), ErrorKind::InvalidRequest, )); } @@ -186,7 +186,7 @@ pub async fn cli_sign_asset( let size = file .size() .await - .ok_or_else(|| Error::new(eyre!("failed to read file metadata"), ErrorKind::Filesystem))?; + .ok_or_else(|| Error::new(eyre!("{}", t!("registry.os.asset.failed-read-metadata")), ErrorKind::Filesystem))?; let commitment = Blake3Commitment { hash: Base64(*blake3.as_bytes()), size, diff --git a/core/src/registry/os/mod.rs b/core/src/registry/os/mod.rs index d4d308281..e3bb0b863 100644 --- a/core/src/registry/os/mod.rs +++ b/core/src/registry/os/mod.rs @@ -17,16 +17,16 @@ pub fn os_api() -> ParentHandler { from_fn_async(index::get_os_index) .with_metadata("authenticated", Value::Bool(false)) .with_display_serializable() - .with_about("List index of OS versions") + .with_about("about.list-os-versions-index") .with_call_remote::(), ) .subcommand( "asset", - asset::asset_api::().with_about("Commands to add, sign, or get registry assets"), + asset::asset_api::().with_about("about.commands-add-sign-get-assets"), ) .subcommand( "version", version::version_api::() - .with_about("Commands to add, remove, or list versions or version signers"), + .with_about("about.commands-add-remove-list-versions"), ) } diff --git a/core/src/registry/os/version/mod.rs b/core/src/registry/os/version/mod.rs index 292be863c..ce2d58c69 100644 --- a/core/src/registry/os/version/mod.rs +++ b/core/src/registry/os/version/mod.rs @@ -27,7 +27,7 @@ pub fn version_api() -> ParentHandler { .with_metadata("admin", Value::Bool(true)) .with_metadata("get_signer", Value::Bool(true)) .no_display() - .with_about("Add OS version") + .with_about("about.add-os-version") .with_call_remote::(), ) .subcommand( @@ -35,12 +35,12 @@ pub fn version_api() -> ParentHandler { from_fn_async(remove_version) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Remove OS version") + .with_about("about.remove-os-version") .with_call_remote::(), ) .subcommand( "signer", - signer::signer_api::().with_about("Add, remove, and list version signers"), + signer::signer_api::().with_about("about.add-remove-list-version-signers"), ) .subcommand( "get", @@ -51,7 +51,7 @@ pub fn version_api() -> ParentHandler { .with_custom_display_fn(|handle, result| { display_version_info(handle.params, result) }) - .with_about("Get OS versions and related version info") + .with_about("about.get-os-versions-info") .with_call_remote::(), ) } diff --git a/core/src/registry/os/version/signer.rs b/core/src/registry/os/version/signer.rs index a668e1c17..1d1755ffd 100644 --- a/core/src/registry/os/version/signer.rs +++ b/core/src/registry/os/version/signer.rs @@ -21,7 +21,7 @@ pub fn signer_api() -> ParentHandler { from_fn_async(add_version_signer) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Add version signer") + .with_about("about.add-version-signer") .with_call_remote::(), ) .subcommand( @@ -29,7 +29,7 @@ pub fn signer_api() -> ParentHandler { from_fn_async(remove_version_signer) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Remove version signer") + .with_about("about.remove-version-signer") .with_call_remote::(), ) .subcommand( @@ -38,7 +38,7 @@ pub fn signer_api() -> ParentHandler { .with_metadata("authenticated", Value::Bool(false)) .with_display_serializable() .with_custom_display_fn(|handle, result| display_signers(handle.params, result)) - .with_about("List version signers and related signer info") + .with_about("about.list-version-signers") .with_call_remote::(), ) } @@ -95,7 +95,7 @@ pub async fn remove_version_signer( .mutate(|s| Ok(s.remove(&signer)))? { return Err(Error::new( - eyre!("signer {signer} is not authorized to sign for v{version}"), + eyre!("{}", t!("registry.os.version.signer-not-authorized", signer = signer, version = version)), ErrorKind::NotFound, )); } diff --git a/core/src/registry/package/add.rs b/core/src/registry/package/add.rs index 938b5e649..d81a415f3 100644 --- a/core/src/registry/package/add.rs +++ b/core/src/registry/package/add.rs @@ -56,7 +56,7 @@ pub async fn add_package( let Some(([url], rest)) = urls.split_at_checked(1) else { return Err(Error::new( - eyre!("must specify at least 1 url"), + eyre!("{}", t!("registry.package.add.must-specify-url")), ErrorKind::InvalidRequest, )); }; @@ -112,7 +112,7 @@ pub async fn add_package( Ok(()) } else { - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.package.add.unauthorized")), ErrorKind::Authorization)) } }) .await @@ -226,7 +226,7 @@ pub async fn remove_package( ) -> Result { let peek = ctx.db.peek().await; let signer = - signer.ok_or_else(|| Error::new(eyre!("missing signer"), ErrorKind::InvalidRequest))?; + signer.ok_or_else(|| Error::new(eyre!("{}", t!("registry.package.missing-signer")), ErrorKind::InvalidRequest))?; let signer_guid = peek.as_index().as_signers().get_signer(&signer)?; let rev = ctx @@ -267,7 +267,7 @@ pub async fn remove_package( } Ok(()) } else { - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.package.unauthorized")), ErrorKind::Authorization)) } }) .await; @@ -342,7 +342,7 @@ pub async fn add_mirror( Ok(()) } else { - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.package.add-mirror.unauthorized")), ErrorKind::Authorization)) } }) .await @@ -454,7 +454,7 @@ pub async fn remove_mirror( ) -> Result<(), Error> { let peek = ctx.db.peek().await; let signer = - signer.ok_or_else(|| Error::new(eyre!("missing signer"), ErrorKind::InvalidRequest))?; + signer.ok_or_else(|| Error::new(eyre!("{}", t!("registry.package.missing-signer")), ErrorKind::InvalidRequest))?; let signer_guid = peek.as_index().as_signers().get_signer(&signer)?; ctx.db @@ -483,7 +483,7 @@ pub async fn remove_mirror( .for_each(|(_, asset)| asset.urls.retain(|u| u != &url)); if s.iter().any(|(_, asset)| asset.urls.is_empty()) { Err(Error::new( - eyre!("cannot remove last mirror from an s9pk"), + eyre!("{}", t!("registry.package.cannot-remove-last-mirror")), ErrorKind::InvalidRequest, )) } else { @@ -493,7 +493,7 @@ pub async fn remove_mirror( } Ok(()) } else { - Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) + Err(Error::new(eyre!("{}", t!("registry.package.remove-mirror.unauthorized")), ErrorKind::Authorization)) } }) .await diff --git a/core/src/registry/package/category.rs b/core/src/registry/package/category.rs index 131ab42f2..369084c69 100644 --- a/core/src/registry/package/category.rs +++ b/core/src/registry/package/category.rs @@ -20,7 +20,7 @@ pub fn category_api() -> ParentHandler { from_fn_async(add_category) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Add a category to the registry") + .with_about("about.add-category-registry") .with_call_remote::(), ) .subcommand( @@ -28,7 +28,7 @@ pub fn category_api() -> ParentHandler { from_fn_async(remove_category) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Remove a category from the registry") + .with_about("about.remove-category-registry") .with_call_remote::(), ) .subcommand( @@ -36,7 +36,7 @@ pub fn category_api() -> ParentHandler { from_fn_async(add_package) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Add a package to a category") + .with_about("about.add-package-category") .with_call_remote::(), ) .subcommand( @@ -44,7 +44,7 @@ pub fn category_api() -> ParentHandler { from_fn_async(remove_package) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Remove a package from a category") + .with_about("about.remove-package-category") .with_call_remote::(), ) .subcommand( diff --git a/core/src/registry/package/get.rs b/core/src/registry/package/get.rs index f796622e1..1db56aa82 100644 --- a/core/src/registry/package/get.rs +++ b/core/src/registry/package/get.rs @@ -441,8 +441,8 @@ pub async fn cli_download( 0 => { return Err(Error::new( eyre!( - "Could not find a version of {id} that satisfies {}", - target_version.unwrap_or(VersionRange::Any) + "{}", + t!("registry.package.get.version-not-found", id = id, version = target_version.unwrap_or(VersionRange::Any)) ), ErrorKind::NotFound, )); @@ -462,8 +462,8 @@ pub async fn cli_download( 0 => { return Err(Error::new( eyre!( - "Could not find a version of {id} that satisfies {}", - target_version.unwrap_or(VersionRange::Any) + "{}", + t!("registry.package.get.version-not-found", id = id, version = target_version.unwrap_or(VersionRange::Any)) ), ErrorKind::NotFound, )); @@ -551,7 +551,7 @@ pub async fn cli_download( progress_tracker.complete(); progress.await.unwrap(); - println!("Download Complete"); + println!("{}", t!("registry.package.get.download-complete")); Ok(()) } diff --git a/core/src/registry/package/mod.rs b/core/src/registry/package/mod.rs index e09dbbb9a..e9de53ac9 100644 --- a/core/src/registry/package/mod.rs +++ b/core/src/registry/package/mod.rs @@ -17,7 +17,7 @@ pub fn package_api() -> ParentHandler { from_fn_async(index::get_package_index) .with_metadata("authenticated", Value::Bool(false)) .with_display_serializable() - .with_about("List packages and categories") + .with_about("about.list-packages-categories") .with_call_remote::(), ) .subcommand( @@ -30,7 +30,7 @@ pub fn package_api() -> ParentHandler { "add", from_fn_async(add::cli_add_package) .no_display() - .with_about("Add package to registry index"), + .with_about("about.add-package-registry"), ) .subcommand( "add-mirror", @@ -42,7 +42,7 @@ pub fn package_api() -> ParentHandler { "add-mirror", from_fn_async(add::cli_add_mirror) .no_display() - .with_about("Add a mirror for an s9pk"), + .with_about("about.add-mirror-s9pk"), ) .subcommand( "remove", @@ -51,17 +51,17 @@ pub fn package_api() -> ParentHandler { .with_custom_display_fn(|args, changed| { if !changed { tracing::warn!( - "{}@{}{} does not exist, so not removed", - args.params.id, - args.params.version, - args.params - .sighash - .map_or(String::new(), |h| format!("#{h}")) + "{}", + t!("registry.package.remove-not-exist", + id = args.params.id, + version = args.params.version, + sighash = args.params.sighash.map_or(String::new(), |h| format!("#{h}")) + ) ); } Ok(()) }) - .with_about("Remove package from registry index") + .with_about("about.remove-package-registry") .with_call_remote::(), ) .subcommand( @@ -69,12 +69,12 @@ pub fn package_api() -> ParentHandler { from_fn_async(add::remove_mirror) .with_metadata("get_signer", Value::Bool(true)) .no_display() - .with_about("Remove a mirror from a package") + .with_about("about.remove-mirror-package") .with_call_remote::(), ) .subcommand( "signer", - signer::signer_api::().with_about("Add, remove, and list package signers"), + signer::signer_api::().with_about("about.add-remove-list-package-signers"), ) .subcommand( "get", @@ -85,18 +85,18 @@ pub fn package_api() -> ParentHandler { .with_custom_display_fn(|handle, result| { get::display_package_info(handle.params, result) }) - .with_about("List installation candidate package(s)") + .with_about("about.list-installation-candidates") .with_call_remote::(), ) .subcommand( "download", from_fn_async_local(get::cli_download) .no_display() - .with_about("Download an s9pk"), + .with_about("about.download-s9pk"), ) .subcommand( "category", category::category_api::() - .with_about("Update the categories for packages on the registry"), + .with_about("about.update-categories-registry"), ) } diff --git a/core/src/registry/package/signer.rs b/core/src/registry/package/signer.rs index 3ab73d521..a95524542 100644 --- a/core/src/registry/package/signer.rs +++ b/core/src/registry/package/signer.rs @@ -22,7 +22,7 @@ pub fn signer_api() -> ParentHandler { from_fn_async(add_package_signer) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Add package signer") + .with_about("about.add-package-signer") .with_call_remote::(), ) .subcommand( @@ -30,7 +30,7 @@ pub fn signer_api() -> ParentHandler { from_fn_async(remove_package_signer) .with_metadata("admin", Value::Bool(true)) .no_display() - .with_about("Remove package signer") + .with_about("about.remove-package-signer") .with_call_remote::(), ) .subcommand( @@ -41,7 +41,7 @@ pub fn signer_api() -> ParentHandler { .with_custom_display_fn(|handle, result| { display_package_signers(handle.params, result) }) - .with_about("List package signers and related signer info") + .with_about("about.list-package-signers") .with_call_remote::(), ) } @@ -114,7 +114,7 @@ pub async fn remove_package_signer( .is_some() { return Err(Error::new( - eyre!("signer {signer} is not authorized to sign for {id}"), + eyre!("{}", t!("registry.package.signer.not-authorized", signer = signer, id = id)), ErrorKind::NotFound, )); } diff --git a/core/src/registry/signer.rs b/core/src/registry/signer.rs index 3a4fc2d0d..8501f3f16 100644 --- a/core/src/registry/signer.rs +++ b/core/src/registry/signer.rs @@ -90,7 +90,7 @@ impl AcceptSigners { Ok(()) } else { Err(Error::new( - eyre!("signer(s) not accepted"), + eyre!("{}", t!("registry.signer.not-accepted")), ErrorKind::InvalidSignature, )) } diff --git a/core/src/s9pk/rpc.rs b/core/src/s9pk/rpc.rs index ec7dd3696..d648d71ae 100644 --- a/core/src/s9pk/rpc.rs +++ b/core/src/s9pk/rpc.rs @@ -25,7 +25,7 @@ pub fn s9pk() -> ParentHandler { "pack", from_fn_async(super::v2::pack::pack) .no_display() - .with_about("Package s9pk input files into valid s9pk"), + .with_about("about.package-s9pk-input-files-into-valid-s9pk"), ) .subcommand( "list-ingredients", @@ -45,21 +45,21 @@ pub fn s9pk() -> ParentHandler { println!(); Ok(()) }) - .with_about("List paths of package ingredients"), + .with_about("about.list-paths-of-package-ingredients"), ) .subcommand( "edit", - edit().with_about("Commands to add an image to an s9pk or edit the manifest"), + edit().with_about("about.commands-add-image-or-edit-manifest"), ) .subcommand( "inspect", - inspect().with_about("Commands to display file paths, file contents, or manifest"), + inspect().with_about("about.commands-display-file-paths-contents-manifest"), ) .subcommand( "convert", from_fn_async(convert) .no_display() - .with_about("Convert s9pk from v1 to v2"), + .with_about("about.convert-s9pk-v1-to-v2"), ) } @@ -76,14 +76,14 @@ fn edit() -> ParentHandler { from_fn_async(add_image) .with_inherited(only_parent) .no_display() - .with_about("Add image to s9pk"), + .with_about("about.add-image-to-s9pk"), ) .subcommand( "manifest", from_fn_async(edit_manifest) .with_inherited(only_parent) .with_display_serializable() - .with_about("Edit s9pk manifest"), + .with_about("about.edit-s9pk-manifest"), ) } @@ -95,21 +95,21 @@ fn inspect() -> ParentHandler { from_fn_async(file_tree) .with_inherited(only_parent) .with_display_serializable() - .with_about("Display list of paths"), + .with_about("about.display-list-of-paths"), ) .subcommand( "cat", from_fn_async(cat) .with_inherited(only_parent) .no_display() - .with_about("Display file contents"), + .with_about("about.display-file-contents"), ) .subcommand( "manifest", from_fn_async(inspect_manifest) .with_inherited(only_parent) .with_display_serializable() - .with_about("Display s9pk manifest"), + .with_about("about.display-s9pk-manifest"), ) } diff --git a/core/src/service/action.rs b/core/src/service/action.rs index 0e75e1c3c..2d149741a 100644 --- a/core/src/service/action.rs +++ b/core/src/service/action.rs @@ -102,7 +102,7 @@ pub fn update_tasks( } } None => { - tracing::error!("action request exists in an invalid state {:?}", v.task); + tracing::error!("{}", t!("service.action.action-request-invalid-state", task = format!("{:?}", v.task))); } }, } @@ -151,7 +151,7 @@ impl Handler for ServiceActor { .de()?; if matches!(&action.visibility, ActionVisibility::Disabled(_)) { return Err(Error::new( - eyre!("action {action_id} is disabled"), + eyre!("{}", t!("service.action.action-is-disabled", action_id = action_id)), ErrorKind::Action, )); } @@ -162,7 +162,7 @@ impl Handler for ServiceActor { _ => false, } { return Err(Error::new( - eyre!("service is not in allowed status for {action_id}"), + eyre!("{}", t!("service.action.service-not-in-allowed-status", action_id = action_id)), ErrorKind::Action, )); } diff --git a/core/src/service/effects/action.rs b/core/src/service/effects/action.rs index ff98dfd13..706a27fe6 100644 --- a/core/src/service/effects/action.rs +++ b/core/src/service/effects/action.rs @@ -1,6 +1,7 @@ use std::collections::BTreeSet; use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async}; +use rust_i18n::t; use crate::action::{ActionInput, ActionResult, display_action_result}; use crate::db::model::package::{ @@ -175,7 +176,7 @@ async fn run_action( if package_id != &context.seed.id { return Err(Error::new( - eyre!("calling actions on other packages is unsupported at this time"), + eyre!("{}", t!("service.effects.action.calling-actions-on-other-packages-unsupported")), ErrorKind::InvalidRequest, )); context @@ -220,7 +221,7 @@ async fn create_task( TaskCondition::InputNotMatches => { let Some(input) = task.input.as_ref() else { return Err(Error::new( - eyre!("input-not-matches trigger requires input to be specified"), + eyre!("{}", t!("service.effects.action.input-not-matches-requires-input")), ErrorKind::InvalidRequest, )); }; @@ -238,9 +239,7 @@ async fn create_task( else { return Err(Error::new( eyre!( - "action {} of {} has no input", - task.action_id, - task.package_id + "{}", t!("service.effects.action.action-has-no-input", action_id = task.action_id, package_id = task.package_id) ), ErrorKind::InvalidRequest, )); diff --git a/core/src/service/effects/dependency.rs b/core/src/service/effects/dependency.rs index 41ca110b4..e725cd27c 100644 --- a/core/src/service/effects/dependency.rs +++ b/core/src/service/effects/dependency.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use clap::builder::ValueParserFactory; use exver::VersionRange; use imbl_value::InternedString; +use rust_i18n::t; use crate::db::model::package::{ CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference, @@ -148,13 +149,13 @@ impl FromStr for DependencyRequirement { .map(|id| id.parse().map_err(Error::from)) .collect(), Some((kind, _)) => Err(Error::new( - eyre!("unknown dependency kind {kind}"), + eyre!("{}", t!("service.effects.dependency.unknown-dependency-kind", kind = kind)), ErrorKind::InvalidRequest, )), None => match rest { "r" | "running" => Ok(BTreeSet::new()), kind => Err(Error::new( - eyre!("unknown dependency kind {kind}"), + eyre!("{}", t!("service.effects.dependency.unknown-dependency-kind", kind = kind)), ErrorKind::InvalidRequest, )), }, diff --git a/core/src/service/mod.rs b/core/src/service/mod.rs index 575d4d619..2213f5090 100644 --- a/core/src/service/mod.rs +++ b/core/src/service/mod.rs @@ -28,6 +28,7 @@ use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode; use ts_rs::TS; use url::Url; + use crate::context::{CliContext, RpcContext}; use crate::db::model::package::{ InstalledState, ManifestPreference, PackageState, PackageStateMatchModelRef, TaskSeverity, @@ -172,7 +173,7 @@ impl ServiceRef { } let service = Arc::try_unwrap(self.0).map_err(|_| { Error::new( - eyre!("ServiceActor held somewhere after actor shutdown"), + eyre!("{}", t!("service.mod.service-actor-held-after-shutdown")), ErrorKind::Unknown, ) })?; @@ -183,7 +184,7 @@ impl ServiceRef { Arc::try_unwrap(service.seed) .map_err(|_| { Error::new( - eyre!("ServiceActorSeed held somewhere after actor shutdown"), + eyre!("{}", t!("service.mod.service-actor-seed-held-after-shutdown")), ErrorKind::Unknown, ) })? @@ -375,7 +376,7 @@ impl Service { { Ok(PackageState::Installed(InstalledState { manifest })) } else { - Err(Error::new(eyre!("Race condition detected - package state changed during load"), ErrorKind::Database)) + Err(Error::new(eyre!("{}", t!("service.mod.race-condition-detected")), ErrorKind::Database)) } }) } @@ -446,7 +447,7 @@ impl Service { handle_installed(S9pk::open(s9pk_path, Some(id)).await?).await } PackageStateMatchModelRef::Error(e) => Err(Error::new( - eyre!("Failed to parse PackageDataEntry, found {e:?}"), + eyre!("{}", t!("service.mod.failed-to-parse-package-data-entry", error = format!("{e:?}"))), ErrorKind::Deserialization, )), } @@ -552,7 +553,7 @@ impl Service { true } else { tracing::warn!( - "Deleting task {id} because action no longer exists" + "{}", t!("service.mod.deleting-task-action-no-longer-exists", id = id) ); false } @@ -789,7 +790,7 @@ pub async fn attach( .join("\n"); return Err(Error::new( eyre!( - "no matching subcontainers are running for {id}; some possible choices are:\n{subcontainers}" + "{}", t!("service.mod.no-matching-subcontainers", id = id, subcontainers = subcontainers) ), ErrorKind::NotFound, )); @@ -828,7 +829,7 @@ pub async fn attach( .map(format_subcontainer_pair) .join("\n"); return Err(Error::new( - eyre!("multiple subcontainers found for {id}: \n{subcontainer_ids}"), + eyre!("{}", t!("service.mod.multiple-subcontainers-found", id = id, subcontainer_ids = subcontainer_ids)), ErrorKind::InvalidRequest, )); } @@ -985,7 +986,7 @@ pub async fn attach( "signal" => { if data.len() != 4 { return Err(Error::new( - eyre!("invalid byte length for signal: {}", data.len()), + eyre!("{}", t!("service.mod.invalid-byte-length-for-signal", length = data.len())), ErrorKind::InvalidRequest )); } @@ -1118,14 +1119,14 @@ async fn get_passwd_command(etc_passwd_path: PathBuf, user: &str) -> RootCommand } } Err(Error::new( - eyre!("Could not parse /etc/passwd for shell: {}", contents), + eyre!("{}", t!("service.mod.could-not-parse-etc-passwd", contents = contents)), ErrorKind::Filesystem, )) } .await .map(RootCommand) .unwrap_or_else(|e| { - tracing::error!("Could not get the /etc/passwd: {e}"); + tracing::error!("{}", t!("service.mod.could-not-get-etc-passwd", error = e)); tracing::debug!("{e:?}"); RootCommand("/bin/sh".to_string()) }) @@ -1287,7 +1288,7 @@ pub async fn cli_attach( "exit" => { if data.len() != 4 { return Err(Error::new( - eyre!("invalid byte length for exit code: {}", data.len()), + eyre!("{}", t!("service.mod.invalid-byte-length-for-exit-code", length = data.len())), ErrorKind::InvalidRequest )); } diff --git a/core/src/service/persistent_container.rs b/core/src/service/persistent_container.rs index e71b5fd5b..16f14047d 100644 --- a/core/src/service/persistent_container.rs +++ b/core/src/service/persistent_container.rs @@ -318,7 +318,7 @@ impl PersistentContainer { .get() .ok_or_else(|| { Error::new( - eyre!("PersistentContainer has been destroyed"), + eyre!("{}", t!("service.persistent-container.container-destroyed")), ErrorKind::Incoherent, ) })? @@ -354,7 +354,7 @@ impl PersistentContainer { .get() .ok_or_else(|| { Error::new( - eyre!("PersistentContainer has been destroyed"), + eyre!("{}", t!("service.persistent-container.container-destroyed")), ErrorKind::Incoherent, ) })? @@ -364,7 +364,7 @@ impl PersistentContainer { let handle = NonDetachingJoinHandle::from(tokio::spawn(async move { let chown_status = async { let res = server.run_unix(&path, |err| { - tracing::error!("error on unix socket {}: {err}", path.display()) + tracing::error!("{}", t!("service.persistent-container.error-on-unix-socket", path = path.display(), error = err)) })?; Command::new("chown") .arg("100000:100000") @@ -386,7 +386,7 @@ impl PersistentContainer { })); let shutdown = recv.await.map_err(|_| { Error::new( - eyre!("unix socket server thread panicked"), + eyre!("{}", t!("service.persistent-container.unix-socket-server-panicked")), ErrorKind::Unknown, ) })??; @@ -396,7 +396,7 @@ impl PersistentContainer { .is_some() { return Err(Error::new( - eyre!("PersistentContainer already initialized"), + eyre!("{}", t!("service.persistent-container.already-initialized")), ErrorKind::InvalidRequest, )); } @@ -473,7 +473,7 @@ impl PersistentContainer { if let Some(destroy) = self.destroy(uninit) { destroy.await?; } - tracing::info!("Service for {} exited", self.s9pk.as_manifest().id); + tracing::info!("{}", t!("service.persistent-container.service-exited", id = self.s9pk.as_manifest().id)); Ok(()) } diff --git a/core/src/service/service_actor.rs b/core/src/service/service_actor.rs index 724be7f16..ae728e8a5 100644 --- a/core/src/service/service_actor.rs +++ b/core/src/service/service_actor.rs @@ -47,9 +47,9 @@ impl Actor for ServiceActor { } .await { - tracing::error!("error synchronizing state of service: {e}"); + tracing::error!("{}", t!("service.service-actor.error-synchronizing-state", error = e)); tracing::debug!("{e:?}"); - tracing::error!("Retrying in {}s...", SYNC_RETRY_COOLDOWN_SECONDS); + tracing::error!("{}", t!("service.service-actor.retrying-in-seconds", seconds = SYNC_RETRY_COOLDOWN_SECONDS)); tokio::time::timeout( Duration::from_secs(SYNC_RETRY_COOLDOWN_SECONDS), async { diff --git a/core/src/service/transition/backup.rs b/core/src/service/transition/backup.rs index 30787311e..089d9f127 100644 --- a/core/src/service/transition/backup.rs +++ b/core/src/service/transition/backup.rs @@ -25,7 +25,7 @@ impl ServiceActorSeed { fut.await.map_err(Error::from) } else { Err(Error::new( - eyre!("No backup to resume"), + eyre!("{}", t!("service.transition.backup.no-backup-to-resume")), ErrorKind::Cancelled, )) }; diff --git a/core/src/service/uninstall.rs b/core/src/service/uninstall.rs index 90fc5eade..598e02cb9 100644 --- a/core/src/service/uninstall.rs +++ b/core/src/service/uninstall.rs @@ -62,7 +62,7 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, soft: bool) -> Result<(), | PackageState::Removing(InstalledState { manifest }) => manifest, s => { return Err(Error::new( - eyre!("Invalid package state for cleanup: {s:?}"), + eyre!("{}", t!("service.uninstall.invalid-package-state-for-cleanup", state = format!("{s:?}"))), ErrorKind::InvalidRequest, )); } diff --git a/core/src/setup.rs b/core/src/setup.rs index e19620e35..076a3aefa 100644 --- a/core/src/setup.rs +++ b/core/src/setup.rs @@ -154,7 +154,7 @@ pub async fn attach( let setup_ctx = ctx.clone(); ctx.run_setup(move || async move { let progress = &setup_ctx.progress; - let mut disk_phase = progress.add_phase("Opening data drive".into(), Some(10)); + let mut disk_phase = progress.add_phase(t!("setup.opening-data-drive").into(), Some(10)); let init_phases = InitPhases::new(&progress); let rpc_ctx_phases = InitRpcContextPhases::new(&progress); @@ -163,7 +163,7 @@ pub async fn attach( a @ Some(_) => a, None => { return Err(Error::new( - color_eyre::eyre::eyre!("Couldn't decode password"), + color_eyre::eyre::eyre!("{}", t!("setup.couldnt-decode-password")), crate::ErrorKind::Unknown, )); } @@ -192,9 +192,7 @@ pub async fn attach( if requires_reboot.0 { crate::disk::main::export(&*disk_guid, DATA_DIR).await?; return Err(Error::new( - eyre!( - "Errors were corrected with your disk, but the server must be restarted in order to proceed" - ), + eyre!("{}", t!("setup.disk-errors-corrected-restart-required")), ErrorKind::DiskManagement, )); } @@ -313,7 +311,7 @@ pub async fn verify_cifs( guard.unmount().await?; if start_os.is_empty() { return Err(Error::new( - eyre!("No Backup Found"), + eyre!("{}", t!("setup.no-backup-found")), crate::ErrorKind::NotFound, )); } @@ -382,7 +380,7 @@ pub async fn execute( Some(a) => a, None => { return Err(Error::new( - color_eyre::eyre::eyre!("Couldn't decode startOsPassword"), + color_eyre::eyre::eyre!("{}", t!("setup.couldnt-decode-startos-password")), crate::ErrorKind::Unknown, )); } @@ -396,7 +394,7 @@ pub async fn execute( target, password: password.decrypt(&ctx).ok_or_else(|| { Error::new( - color_eyre::eyre::eyre!("Couldn't decode recoveryPassword"), + color_eyre::eyre::eyre!("{}", t!("setup.couldnt-decode-recovery-password")), crate::ErrorKind::Unknown, ) })?, @@ -439,7 +437,7 @@ pub async fn complete(ctx: SetupContext) -> Result { } Some(Err(e)) => Err(e.clone_output()), None => Err(Error::new( - eyre!("setup.execute has not completed successfully"), + eyre!("{}", t!("setup.execute-not-completed")), crate::ErrorKind::InvalidRequest, )), } @@ -471,13 +469,13 @@ pub async fn execute_inner( kiosk: Option, ) -> Result<(SetupResult, RpcContext), Error> { let progress = &ctx.progress; - let mut disk_phase = progress.add_phase("Formatting data drive".into(), Some(10)); + let mut disk_phase = progress.add_phase(t!("setup.formatting-data-drive").into(), Some(10)); let restore_phase = match recovery_source.as_ref() { Some(RecoverySource::Backup { .. }) => { - Some(progress.add_phase("Restoring backup".into(), Some(100))) + Some(progress.add_phase(t!("setup.restoring-backup").into(), Some(100))) } Some(RecoverySource::Migrate { .. }) => { - Some(progress.add_phase("Transferring data".into(), Some(100))) + Some(progress.add_phase(t!("setup.transferring-data").into(), Some(100))) } None => None, }; diff --git a/core/src/shutdown.rs b/core/src/shutdown.rs index 9d5640001..2b25ad9f9 100644 --- a/core/src/shutdown.rs +++ b/core/src/shutdown.rs @@ -1,3 +1,4 @@ + use crate::PLATFORM; use crate::context::RpcContext; use crate::disk::main::export; @@ -17,9 +18,9 @@ impl Shutdown { use std::process::Command; if self.restart { - tracing::info!("Beginning server restart"); + tracing::info!("{}", t!("shutdown.beginning-restart")); } else { - tracing::info!("Beginning server shutdown"); + tracing::info!("{}", t!("shutdown.beginning-shutdown")); } let rt = tokio::runtime::Builder::new_current_thread() @@ -35,18 +36,18 @@ impl Shutdown { .invoke(crate::ErrorKind::Journald) .await { - tracing::error!("Error Stopping Journald: {}", e); + tracing::error!("{}", t!("shutdown.error-stopping-journald", error = e.to_string())); tracing::debug!("{:?}", e); } if let Some(guid) = &self.disk_guid { if let Err(e) = export(guid, crate::DATA_DIR).await { - tracing::error!("Error Exporting Volume Group: {}", e); + tracing::error!("{}", t!("shutdown.error-exporting-volume-group", error = e.to_string())); tracing::debug!("{:?}", e); } } if &*PLATFORM != "raspberrypi" || self.restart { if let Err(e) = SHUTDOWN.play().await { - tracing::error!("Error Playing Shutdown Song: {}", e); + tracing::error!("{}", t!("shutdown.error-playing-shutdown-song", error = e.to_string())); tracing::debug!("{:?}", e); } } diff --git a/core/src/ssh.rs b/core/src/ssh.rs index cec4b0daf..a94a6c483 100644 --- a/core/src/ssh.rs +++ b/core/src/ssh.rs @@ -92,14 +92,14 @@ pub fn ssh() -> ParentHandler { "add", from_fn_async(add) .no_display() - .with_about("Add ssh key") + .with_about("about.add-ssh-key") .with_call_remote::(), ) .subcommand( "remove", from_fn_async(remove) .no_display() - .with_about("Remove ssh key") + .with_about("about.remove-ssh-key") .with_call_remote::(), ) .subcommand( @@ -109,7 +109,7 @@ pub fn ssh() -> ParentHandler { .with_custom_display_fn(|handle, result| { display_all_ssh_keys(handle.params, result) }) - .with_about("List ssh keys") + .with_about("about.list-ssh-keys") .with_call_remote::(), ) } @@ -168,7 +168,10 @@ pub async fn remove( if keys_ref.remove(&fingerprint)?.is_some() { keys_ref.de() } else { - Err(Error::new(eyre!("SSH Key Not Found"), ErrorKind::NotFound)) + Err(Error::new( + eyre!("{}", t!("ssh.key-not-found")), + ErrorKind::NotFound, + )) } }) .await diff --git a/core/src/system.rs b/core/src/system.rs index 333d7fa99..c6ad0a962 100644 --- a/core/src/system.rs +++ b/core/src/system.rs @@ -34,7 +34,7 @@ pub fn experimental() -> ParentHandler { "zram", from_fn_async(zram) .no_display() - .with_about("Enable zram") + .with_about("about.enable-zram") .with_call_remote::(), ) .subcommand( @@ -44,7 +44,7 @@ pub fn experimental() -> ParentHandler { .with_custom_display_fn(|handle, result| { display_governor_info(handle.params, result) }) - .with_about("Show current and available CPU governors") + .with_about("about.show-cpu-governors") .with_call_remote::(), ) } @@ -159,7 +159,10 @@ pub async fn governor( if let Some(set) = set { if !available.contains(&set) { return Err(Error::new( - eyre!("Governor {set} not available"), + eyre!( + "{}", + t!("system.governor-not-available", governor = set.to_string()) + ), ErrorKind::InvalidRequest, )); } @@ -295,7 +298,7 @@ pub fn kiosk() -> ParentHandler { enable_kiosk().await }) .no_display() - .with_about("Enable kiosk mode") + .with_about("about.enable-kiosk-mode") .with_call_remote::(), ) .subcommand( @@ -313,7 +316,7 @@ pub fn kiosk() -> ParentHandler { disable_kiosk().await }) .no_display() - .with_about("Disable kiosk mode") + .with_about("about.disable-kiosk-mode") .with_call_remote::(), ) } @@ -553,7 +556,13 @@ pub async fn launch_metrics_task Receiver>>( let init_temp = match get_temp().await { Ok(a) => Some(a), Err(e) => { - tracing::error!("Could not get initial temperature: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-initial-temperature", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); None } @@ -570,12 +579,24 @@ pub async fn launch_metrics_task Receiver>>( break; } Err(e) => { - tracing::error!("Could not get initial cpu info: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-initial-cpu-info", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } }, Err(e) => { - tracing::error!("Could not get initial proc stat: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-initial-proc-stat", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -590,7 +611,13 @@ pub async fn launch_metrics_task Receiver>>( break; } Err(e) => { - tracing::error!("Could not get initial mem info: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-initial-mem-info", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -605,7 +632,13 @@ pub async fn launch_metrics_task Receiver>>( break; } Err(e) => { - tracing::error!("Could not get initial disk info: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-initial-disk-info", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -650,7 +683,13 @@ async fn launch_temp_task( }); } Err(e) => { - tracing::error!("Could not get new temperature: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-new-temperature", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -673,7 +712,13 @@ async fn launch_cpu_task( cache.send_modify(|c| c.as_mut().unwrap().cpu = info); } Err(e) => { - tracing::error!("Could not get new CPU Metrics: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-new-cpu-metrics", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -692,7 +737,13 @@ async fn launch_mem_task(cache: &Watch>, mut shutdown: Receiver< cache.send_modify(|c| c.as_mut().unwrap().memory = a); } Err(e) => { - tracing::error!("Could not get new Memory Metrics: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-new-memory-metrics", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -721,7 +772,13 @@ async fn launch_disk_task( }); } Err(e) => { - tracing::error!("Could not get new Disk Metrics: {}", e); + tracing::error!( + "{}", + t!( + "system.could-not-get-new-disk-metrics", + error = e.to_string() + ) + ); tracing::debug!("{:?}", e); } } @@ -757,7 +814,12 @@ async fn get_temp() -> Result { } }) .reduce(f64::max) - .ok_or_else(|| Error::new(eyre!("No temperatures available"), ErrorKind::Filesystem))?; + .ok_or_else(|| { + Error::new( + eyre!("{}", t!("system.no-temperatures-available")), + ErrorKind::Filesystem, + ) + })?; Ok(Celsius(temp)) } @@ -803,7 +865,10 @@ async fn get_proc_stat() -> Result { .map(|s| { s.parse::().map_err(|e| { Error::new( - color_eyre::eyre::eyre!("Invalid /proc/stat column value: {}", e), + color_eyre::eyre::eyre!( + "{}", + t!("system.invalid-proc-stat-column", error = e.to_string()) + ), ErrorKind::ParseSysInfo, ) }) @@ -813,8 +878,8 @@ async fn get_proc_stat() -> Result { if stats.len() < 10 { Err(Error::new( eyre!( - "Columns missing from /proc/stat. Need 10, found {}", - stats.len() + "{}", + t!("system.columns-missing-from-proc-stat", count = stats.len()) ), ErrorKind::ParseSysInfo, )) @@ -872,16 +937,22 @@ pub async fn get_mem_info() -> Result { zram_free: None, }; fn get_num_kb(l: &str) -> Result { - let e = Error::new( - color_eyre::eyre::eyre!("Invalid meminfo line: {}", l), - ErrorKind::ParseSysInfo, - ); + let line = l.to_string(); + let e = || { + Error::new( + color_eyre::eyre::eyre!( + "{}", + t!("system.invalid-meminfo-line", line = line.clone()) + ), + ErrorKind::ParseSysInfo, + ) + }; match l.split_whitespace().skip(1).next() { Some(x) => match x.parse() { Ok(y) => Ok(y), - Err(_) => Err(e), + Err(_) => Err(e()), }, - None => Err(e), + None => Err(e()), } } for entry in contents.lines() { @@ -900,10 +971,19 @@ pub async fn get_mem_info() -> Result { } } fn ensure_present(a: Option, field: &str) -> Result { - a.ok_or(Error::new( - color_eyre::eyre::eyre!("{} missing from /proc/meminfo", field), - ErrorKind::ParseSysInfo, - )) + let field_str = field.to_string(); + a.ok_or_else(|| { + Error::new( + color_eyre::eyre::eyre!( + "{}", + t!( + "system.field-missing-from-meminfo", + field = field_str.clone() + ) + ), + ErrorKind::ParseSysInfo, + ) + }) } let mem_total = ensure_present(mem_info.mem_total, "MemTotal")?; let mem_free = ensure_present(mem_info.mem_free, "MemFree")?; @@ -1136,7 +1216,7 @@ pub async fn set_language( .await?; write_file_atomic( "/media/startos/config/overlay/etc/default/locale", - format!("{language}.UTF-8\n").as_bytes(), + format!("LANG={language}.UTF-8\n").as_bytes(), ) .await?; ctx.db diff --git a/core/src/tunnel/api.rs b/core/src/tunnel/api.rs index 39cd10a38..5d7f53ff2 100644 --- a/core/src/tunnel/api.rs +++ b/core/src/tunnel/api.rs @@ -20,19 +20,19 @@ pub fn tunnel_api() -> ParentHandler { .subcommand( "db", super::db::db_api::() - .with_about("Commands to interact with the db i.e. dump and apply"), + .with_about("about.commands-interact-with-db-dump-apply"), ) .subcommand( "auth", - super::auth::auth_api::().with_about("Add or remove authorized clients"), + super::auth::auth_api::().with_about("about.add-or-remove-authorized-clients"), ) .subcommand( "subnet", - subnet_api::().with_about("Add, remove, or modify subnets"), + subnet_api::().with_about("about.add-remove-or-modify-subnets"), ) .subcommand( "device", - device_api::().with_about("Add, remove, or list devices in subnets"), + device_api::().with_about("about.add-remove-or-list-devices-in-subnets"), ) .subcommand( "port-forward", @@ -42,7 +42,7 @@ pub fn tunnel_api() -> ParentHandler { from_fn_async(add_forward) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Add a new port forward") + .with_about("about.add-new-port-forward") .with_call_remote::(), ) .subcommand( @@ -50,7 +50,7 @@ pub fn tunnel_api() -> ParentHandler { from_fn_async(remove_forward) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Remove a port forward") + .with_about("about.remove-port-forward") .with_call_remote::(), ), ) @@ -70,7 +70,7 @@ pub fn subnet_api() -> ParentHandler { .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|a, _| a) .no_display() - .with_about("Add a new subnet") + .with_about("about.add-new-subnet") .with_call_remote::(), ) .subcommand( @@ -79,7 +79,7 @@ pub fn subnet_api() -> ParentHandler { .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|a, _| a) .no_display() - .with_about("Remove a subnet") + .with_about("about.remove-subnet") .with_call_remote::(), ) } @@ -91,7 +91,7 @@ pub fn device_api() -> ParentHandler { from_fn_async(add_device) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Add a device to a subnet") + .with_about("about.add-device-to-subnet") .with_call_remote::(), ) .subcommand( @@ -99,7 +99,7 @@ pub fn device_api() -> ParentHandler { from_fn_async(remove_device) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Remove a device from a subnet") + .with_about("about.remove-device-from-subnet") .with_call_remote::(), ) .subcommand( @@ -123,13 +123,13 @@ pub fn device_api() -> ParentHandler { Ok(()) }) - .with_about("List devices in a subnet") + .with_about("about.list-devices-in-subnet") .with_call_remote::(), ) .subcommand( "show-config", from_fn_async(show_config) - .with_about("Show the WireGuard configuration for a device") + .with_about("about.show-wireguard-configuration-for-device") .with_call_remote::(), ) } diff --git a/core/src/tunnel/auth.rs b/core/src/tunnel/auth.rs index 713d038a3..dddbf0d95 100644 --- a/core/src/tunnel/auth.rs +++ b/core/src/tunnel/auth.rs @@ -129,13 +129,13 @@ pub fn auth_api() -> ParentHandler { .subcommand( "set-password", from_fn_async(set_password_cli) - .with_about("Set user interface password") + .with_about("about.set-user-interface-password") .no_display(), ) .subcommand( "reset-password", from_fn_async(reset_password) - .with_about("Reset user interface password") + .with_about("about.reset-user-interface-password") .no_display(), ) .subcommand( @@ -146,7 +146,7 @@ pub fn auth_api() -> ParentHandler { from_fn_async(add_key) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Add a new authorized key") + .with_about("about.add-new-authorized-key") .with_call_remote::(), ) .subcommand( @@ -154,7 +154,7 @@ pub fn auth_api() -> ParentHandler { from_fn_async(remove_key) .with_metadata("sync_db", Value::Bool(true)) .no_display() - .with_about("Remove an authorized key") + .with_about("about.remove-authorized-key") .with_call_remote::(), ) .subcommand( @@ -175,7 +175,7 @@ pub fn auth_api() -> ParentHandler { table.print_tty(false)?; Ok(()) }) - .with_about("List authorized keys") + .with_about("about.list-authorized-keys") .with_call_remote::(), ), ) diff --git a/core/src/tunnel/db.rs b/core/src/tunnel/db.rs index 848e8481a..2e96f3a39 100644 --- a/core/src/tunnel/db.rs +++ b/core/src/tunnel/db.rs @@ -89,7 +89,7 @@ pub fn db_api() -> ParentHandler { "dump", from_fn_async(cli_dump) .with_display_serializable() - .with_about("Filter/query db to display tables and records"), + .with_about("about.filter-query-db-display-tables-records"), ) .subcommand( "dump", @@ -107,7 +107,7 @@ pub fn db_api() -> ParentHandler { "apply", from_fn_async(cli_apply) .no_display() - .with_about("Update a db record"), + .with_about("about.update-db-record"), ) .subcommand( "apply", diff --git a/core/src/tunnel/web.rs b/core/src/tunnel/web.rs index 8d951942f..b6ecbc5d5 100644 --- a/core/src/tunnel/web.rs +++ b/core/src/tunnel/web.rs @@ -98,27 +98,27 @@ pub fn web_api() -> ParentHandler { "init", from_fn_async_local(init_web) .no_display() - .with_about("Initialize the webserver"), + .with_about("about.initialize-webserver"), ) .subcommand( "set-listen", from_fn_async(set_listen) .no_display() - .with_about("Set the listen address for the webserver") + .with_about("about.set-listen-address-for-webserver") .with_call_remote::(), ) .subcommand( "get-listen", from_fn_async(get_listen) .with_display_serializable() - .with_about("Get the listen address for the webserver") + .with_about("about.get-listen-address-for-webserver") .with_call_remote::(), ) .subcommand( "get-available-ips", from_fn_async(get_available_ips) .with_display_serializable() - .with_about("Get available IP addresses to bind to") + .with_about("about.get-available-ip-addresses-to-bind") .with_call_remote::(), ) .subcommand( @@ -129,12 +129,12 @@ pub fn web_api() -> ParentHandler { "import-certificate", from_fn_async_local(import_certificate_cli) .no_display() - .with_about("Import a certificate to use for the webserver"), + .with_about("about.import-certificate-for-webserver"), ) .subcommand( "generate-certificate", from_fn_async(generate_certificate) - .with_about("Generate a certificate to use for the webserver") + .with_about("about.generate-certificate-for-webserver") .with_call_remote::(), ) .subcommand( @@ -150,13 +150,13 @@ pub fn web_api() -> ParentHandler { } Ok(()) }) - .with_about("Get the certificate for the webserver") + .with_about("about.get-certificate-for-webserver") .with_call_remote::(), ) .subcommand( "enable", from_fn_async(enable_web) - .with_about("Enable the webserver") + .with_about("about.enable-webserver") .no_display() .with_call_remote::(), ) @@ -164,14 +164,14 @@ pub fn web_api() -> ParentHandler { "disable", from_fn_async(disable_web) .no_display() - .with_about("Disable the webserver") + .with_about("about.disable-webserver") .with_call_remote::(), ) .subcommand( "reset", from_fn_async(reset_web) .no_display() - .with_about("Reset the webserver") + .with_about("about.reset-webserver") .with_call_remote::(), ) } diff --git a/core/src/tunnel/wg.rs b/core/src/tunnel/wg.rs index 8696ba293..eb4aceba7 100644 --- a/core/src/tunnel/wg.rs +++ b/core/src/tunnel/wg.rs @@ -41,6 +41,7 @@ impl WgServer { Command::new("wg-quick") .arg("down") .arg(WIREGUARD_INTERFACE_NAME) + .env("LANG", "C.UTF-8") .invoke(ErrorKind::Network) .await .or_else(|e| { diff --git a/core/src/update/mod.rs b/core/src/update/mod.rs index 3595cb42a..a05037d9a 100644 --- a/core/src/update/mod.rs +++ b/core/src/update/mod.rs @@ -82,9 +82,7 @@ pub async fn update_system( .de()? { return Err(Error::new( - eyre!( - "Server was already updated. Please restart your device before attempting to update again." - ), + eyre!("{}", t!("update.already-updated-restart-required")), ErrorKind::InvalidRequest, )); } @@ -143,7 +141,7 @@ pub async fn update_system( } .await { - tracing::error!("Error returning progress of update: {e}"); + tracing::error!("{}", t!("update.error-returning-progress", error = e.to_string())); tracing::debug!("{e:?}") } }, @@ -176,11 +174,11 @@ pub async fn cli_update_system( .await?, )?; match res.target { - None => println!("No updates available"), + None => println!("{}", t!("update.no-updates-available")), Some(v) => { if let Some(progress) = res.progress { let mut ws = context.ws_continuation(progress).await?; - let mut progress = PhasedProgressBar::new(&format!("Updating to v{v}...")); + let mut progress = PhasedProgressBar::new(&t!("update.updating-to-version", version = v.to_string())); let mut prev = None; while let Some(msg) = ws.try_next().await.with_kind(ErrorKind::Network)? { if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg { @@ -201,9 +199,9 @@ pub async fn cli_update_system( prev.overall.set_complete(); progress.update(&prev); } - println!("Update complete. Restart your server to apply the update.") + println!("{}", t!("update.complete-restart-to-apply")) } else { - println!("Updating to v{v}...") + println!("{}", t!("update.updating-to-version", version = v.to_string())) } } } @@ -279,7 +277,7 @@ async fn maybe_do_update( let mut status = peeked.as_public().as_server_info().as_status_info().de()?; if status.update_progress.is_some() { return Err(Error::new( - eyre!("Server is already updating!"), + eyre!("{}", t!("update.already-updating")), crate::ErrorKind::InvalidRequest, )); } @@ -296,9 +294,7 @@ async fn maybe_do_update( if status.updated { return Err(Error::new( - eyre!( - "Server was already updated. Please restart your device before attempting to update again." - ), + eyre!("{}", t!("update.already-updated-restart-required")), crate::ErrorKind::InvalidRequest, )); } @@ -343,7 +339,7 @@ async fn maybe_do_update( CIRCLE_OF_5THS_SHORT.play().await.log_err(); } Err(e) => { - let err_string = format!("Update was not successful because of {}", e); + let err_string = t!("update.not-successful", error = e.to_string()).to_string(); ctx.db .mutate(|db| { db.as_public_mut() @@ -355,7 +351,7 @@ async fn maybe_do_update( db, None, NotificationLevel::Error, - "StartOS Update Failed".to_owned(), + t!("update.failed-title").to_string(), err_string, (), ) diff --git a/core/src/util/cpupower.rs b/core/src/util/cpupower.rs index db625f90e..fbd0a6188 100644 --- a/core/src/util/cpupower.rs +++ b/core/src/util/cpupower.rs @@ -67,7 +67,7 @@ pub async fn get_available_governors() -> Result, Error> { for_cpu .entry(current_cpu.ok_or_else(|| { Error::new( - eyre!("governors listed before cpu"), + eyre!("{}", t!("util.cpupower.governors-listed-before-cpu")), ErrorKind::ParseSysInfo, ) })?) @@ -95,6 +95,7 @@ pub async fn current_governor() -> Result, Error> { let Some(raw) = Command::new("cpupower") .arg("frequency-info") .arg("-p") + .env("LANG", "C.UTF-8") .invoke(ErrorKind::CpuSettings) .await .and_then(|s| Ok(Some(String::from_utf8(s)?))) @@ -122,7 +123,10 @@ pub async fn current_governor() -> Result, Error> { } } Err(Error::new( - eyre!("Failed to parse cpupower output:\n{raw}"), + eyre!( + "{}", + t!("util.cpupower.failed-to-parse-output", output = raw) + ), ErrorKind::ParseSysInfo, )) } diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 3a351bd87..6aaaf86dd 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -666,6 +666,27 @@ pub fn new_guid() -> InternedString { )) } +/// A utility for lazily computing a Display value. This is useful for i18n +/// where you want to defer the translation until the value is actually displayed, +/// avoiding allocations in the common case where the message is not rendered. +pub struct LazyDisplay(F); +impl D, D: fmt::Display> LazyDisplay { + pub fn new(f: F) -> Self { + LazyDisplay(f) + } +} +impl D, D: fmt::Display> fmt::Display for LazyDisplay { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", (self.0)()) + } +} + +/// Creates a lazily-evaluated Display implementation. +/// The closure is only called when the value is actually displayed. +pub fn lazy_display D, D: fmt::Display>(f: F) -> LazyDisplay { + LazyDisplay::new(f) +} + #[derive(Debug, Clone, TS)] #[ts(type = "string")] pub enum PathOrUrl { diff --git a/core/src/util/net.rs b/core/src/util/net.rs index 53168d310..70275c735 100644 --- a/core/src/util/net.rs +++ b/core/src/util/net.rs @@ -97,7 +97,7 @@ impl WebSocket { if self.ping_state.is_some() { self.fused = true; break Poll::Ready(Some(Err(axum::Error::new(eyre!( - "Timeout: WebSocket did not respond to ping within {PING_TIMEOUT:?}" + "{}", t!("util.net.websocket-ping-timeout", timeout = format!("{PING_TIMEOUT:?}")) ))))); } self.ping_state = Some((false, rand::random())); diff --git a/core/src/util/rpc.rs b/core/src/util/rpc.rs index 165632c5d..6c79af572 100644 --- a/core/src/util/rpc.rs +++ b/core/src/util/rpc.rs @@ -17,7 +17,7 @@ use crate::util::{Apply, PathOrUrl}; pub fn util() -> ParentHandler { ParentHandler::new().subcommand( "b3sum", - from_fn_async(b3sum).with_about("Calculate blake3 hash for a file"), + from_fn_async(b3sum).with_about("about.calculate-blake3-hash-for-file"), ) } @@ -57,7 +57,7 @@ pub async fn b3sum( .await } else { Err(Error::new( - eyre!("unknown scheme: {}", url.scheme()), + eyre!("{}", t!("util.rpc.unknown-scheme", scheme = url.scheme())), ErrorKind::InvalidRequest, )) } diff --git a/core/src/util/serde.rs b/core/src/util/serde.rs index ace93738e..aadcd3166 100644 --- a/core/src/util/serde.rs +++ b/core/src/util/serde.rs @@ -652,7 +652,7 @@ impl std::str::FromStr for Duration { fn from_str(s: &str) -> Result { let units_idx = s.find(|c: char| c.is_alphabetic()).ok_or_else(|| { Error::new( - eyre!("Must specify units for duration"), + eyre!("{}", t!("util.serde.must-specify-units-for-duration")), crate::ErrorKind::Deserialization, ) })?; @@ -691,7 +691,7 @@ impl std::str::FromStr for Duration { } _ => { return Err(Error::new( - eyre!("Invalid units for duration"), + eyre!("{}", t!("util.serde.invalid-units-for-duration")), crate::ErrorKind::Deserialization, )); } @@ -1050,7 +1050,7 @@ impl>> FromStr for Base64 { .map(Self) .map_err(|_| { Error::new( - eyre!("failed to create from buffer"), + eyre!("{}", t!("util.serde.failed-to-create-from-buffer")), ErrorKind::Deserialization, ) }) @@ -1151,7 +1151,7 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result Result Result Error { } fn noninteractive_err() -> Error { Error::new( - eyre!("Terminal must be in interactive mode for this wizard"), + eyre!("{}", t!("util.tui.terminal-must-be-interactive")), ErrorKind::Filesystem, ) } @@ -21,7 +21,7 @@ where { move |s| { s.parse::() - .map_err(|_| format!("Please enter a valid {what}.")) + .map_err(|_| t!("util.tui.enter-valid-value", what = what).to_string()) } } @@ -50,7 +50,7 @@ pub async fn prompt Result> } } ReadlineEvent::Eof | ReadlineEvent::Interrupted => { - return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled)); + return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled)); } _ => (), } @@ -83,7 +83,7 @@ pub async fn prompt_multiline< Err(e) => writeln!(&mut rl_ctx.shared_writer, "{e}")?, }, ReadlineEvent::Eof | ReadlineEvent::Interrupted => { - return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled)); + return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled)); } _ => (), } @@ -119,7 +119,7 @@ pub async fn choose_custom_display<'t, T>( .await .map_err(map_miette)?; if choice.len() < 1 { - return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled)); + return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled)); } let (idx, choice_str) = string_choices .iter() @@ -127,7 +127,7 @@ pub async fn choose_custom_display<'t, T>( .find(|(_, s)| s.as_str() == choice[0].as_str()) .ok_or_else(|| { Error::new( - eyre!("selected choice does not appear in input"), + eyre!("{}", t!("util.tui.selected-choice-not-in-input")), ErrorKind::Incoherent, ) })?;