From 1b56aa4c9be2190fedd2696a52a22aa2fe9f56c0 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 18:26:14 +0100 Subject: [PATCH 01/38] feat(i18n): add Paraglide internationalization with en, de and nb Set up Paraglide JS 3 with the locale resolved from a one-off ?lang= override, then the PARAGLIDE_LOCALE cookie, then the browser language, then English. The ?lang= override is never written to the cookie; picking a language in the footer switcher saves it and drops the parameter. Migrate the app shell (header, sidebar, footer), the auth pages and the root error page to messages. Sentences with embedded links use markup messages rendered through ParaglideMessage, so link targets stay in code. Compile messages before svelte-check in `check` and the pre-commit hook, since the generated code is gitignored. --- .gitignore | 3 + .husky/pre-commit | 1 + .prettierignore | 3 + eslint.config.js | 1 + messages/de.json | 160 +++++ messages/en.json | 160 +++++ messages/nb.json | 160 +++++ package.json | 7 +- pnpm-lock.yaml | 557 ++++++++++++++++++ project.inlang/paraglide.config.ts | 12 + project.inlang/settings.json | 12 + src/app.html | 2 +- src/hooks.client.ts | 2 + src/hooks.server.ts | 25 +- src/lib/i18n/open-graph.ts | 12 + src/lib/i18n/query-locale.ts | 54 ++ src/routes/(auth)/+layout.svelte | 5 +- src/routes/(auth)/activate/+page.svelte | 18 +- .../(auth)/forgot-password/+page.svelte | 33 +- src/routes/(auth)/login/+page.svelte | 55 +- .../oauth/[provider]/create/+page.svelte | 44 +- src/routes/(auth)/oauth/error/+page.svelte | 7 +- .../[secret]/+page.svelte | 25 +- src/routes/(auth)/signup/+page.svelte | 74 ++- src/routes/(auth)/verify-email/+page.svelte | 18 +- src/routes/+error.svelte | 19 +- src/routes/Footer.svelte | 31 +- src/routes/Header.svelte | 24 +- src/routes/LanguageSwitcher.svelte | 47 ++ src/routes/Sidebar.svelte | 55 +- vite.config.ts | 3 + 31 files changed, 1455 insertions(+), 174 deletions(-) create mode 100644 messages/de.json create mode 100644 messages/en.json create mode 100644 messages/nb.json create mode 100644 project.inlang/paraglide.config.ts create mode 100644 project.inlang/settings.json create mode 100644 src/lib/i18n/open-graph.ts create mode 100644 src/lib/i18n/query-locale.ts create mode 100644 src/routes/LanguageSwitcher.svelte diff --git a/.gitignore b/.gitignore index aebe8d14..4e72d95f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ yarn.lock .claude .playwright-mcp + +# Paraglide (src/lib/paraglide ignores itself) +project.inlang/cache diff --git a/.husky/pre-commit b/.husky/pre-commit index f743e08d..b0940458 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,5 @@ pnpm exec svelte-kit sync +pnpm exec paraglide-js compile --silent pnpm exec svelte-check --tsconfig ./tsconfig.json & pid1=$! diff --git a/.prettierignore b/.prettierignore index 83f5d41c..5c7f0e4d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -22,3 +22,6 @@ src/worker-configuration.d.ts # Cloudflare headers file — no prettier parser _headers + +# Generated by Paraglide +src/lib/paraglide diff --git a/eslint.config.js b/eslint.config.js index 85bda1a8..46791481 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -64,6 +64,7 @@ export default defineConfig( 'yarn.lock', 'src/lib/api/internal', 'src/lib/components/ui', + 'src/lib/paraglide', // Workspace packages (e.g. svelte-core submodule) own their own linting 'packages/**', ], diff --git a/messages/de.json b/messages/de.json new file mode 100644 index 00000000..725bc8d4 --- /dev/null +++ b/messages/de.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Seitenleiste umschalten", + "header_avatar_alt": "Avatar von {name}", + "header_menu_profile": "Profil", + "header_menu_settings": "Einstellungen", + "header_menu_logout": "Abmelden", + "header_login": "Anmelden", + "header_signup": "Registrieren", + "header_github_title": "Projekt auf GitHub", + "header_discord_title": "Community-Discord", + + "footer_made_with": "Mit {#heart/} gemacht vom {#team}OpenShock-Team{/team}", + "footer_connection_status_label": "Verbindungsstatus zum Server", + "footer_signalr_state": "SignalR-Status", + "footer_frontend_version": "Frontend-Version", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Backend-Version", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Sprache", + + "sidebar_home_link_label": "OpenShock-Startseite", + "sidebar_group_general": "Allgemein", + "sidebar_home": "Startseite", + "sidebar_shockers": "Shocker", + "sidebar_shared_shockers": "Geteilte Shocker", + "sidebar_hubs": "Hubs", + "sidebar_user_shares": "Benutzerfreigaben", + "sidebar_public_shares": "Öffentliche Freigaben", + "sidebar_serial_terminal": "Serielles Terminal", + "sidebar_group_admin": "Admin", + "sidebar_admin_monitoring": "Überwachung", + "sidebar_admin_online_hubs": "Online-Hubs", + "sidebar_admin_online_gateways": "Online-Gateways", + "sidebar_admin_management": "Verwaltung", + "sidebar_admin_users": "Benutzer", + "sidebar_admin_blacklists": "Sperrlisten", + "sidebar_admin_webhooks": "Webhooks", + "sidebar_admin_mail": "E-Mail", + "sidebar_admin_automation_tokens": "Automatisierungstokens", + "sidebar_admin_configuration": "Konfiguration", + "sidebar_admin_hangfire": "Hangfire", + "sidebar_group_settings": "Einstellungen", + "sidebar_settings_api_tokens": "API-Tokens", + "sidebar_settings_sessions": "Sitzungen", + "sidebar_settings_audit_log": "Aktivitätsprotokoll", + "sidebar_settings_connections": "Verknüpfungen", + "sidebar_settings_account": "Kontoeinstellungen", + + "error_meta_title": "Dienst nicht verfügbar", + "error_meta_description": "OpenShock ist derzeit nicht verfügbar", + "error_meta_logo_alt": "OpenShock-Logo", + "error_fallback_message": "Etwas ist schiefgelaufen.", + "error_not_found_description": "Die gesuchte Seite existiert nicht.", + "error_unexpected_description": "Ein unerwarteter Fehler ist aufgetreten.", + "error_go_back": "Zurück", + + "auth_layout_logo_alt": "OpenShock-Logo", + "auth_layout_invalid_redirect_removed": "Eine ungültige Weiterleitungs-URL wurde zu deiner Sicherheit entfernt.", + + "auth_activate_breadcrumb": "Konto aktivieren", + "auth_activate_title": "Konto aktivieren", + "auth_activate_activating": "Dein Konto wird aktiviert...", + "auth_activate_success": "Dein Konto wurde aktiviert", + "auth_activate_failed": "Dieser Aktivierungslink ist ungültig oder abgelaufen", + "auth_activate_missing_token": "Aktivierungstoken fehlt", + "auth_activate_continue": "Weiter zur Anmeldung", + "auth_activate_toast_success": "Konto aktiviert", + + "auth_forgot_breadcrumb": "Passwort vergessen", + "auth_forgot_title": "Passwort vergessen", + "auth_forgot_description": "Gib deine E-Mail-Adresse ein, um dein Passwort zurückzusetzen", + "auth_forgot_email_label": "E-Mail", + "auth_forgot_email_placeholder": "E-Mail", + "auth_forgot_submit": "Passwort zurücksetzen", + "auth_forgot_signin_prompt": "Passwort wieder eingefallen? {#signin}Anmelden{/signin}", + "auth_forgot_mail_disabled": "Auf dieser Instanz ist das Zurücksetzen von Passwörtern nicht möglich, da kein E-Mail-Anbieter eingerichtet ist. Wende dich an deine Administration, um wieder Zugriff auf dein Konto zu erhalten.", + "auth_forgot_toast_sent": "Die Anfrage zum Zurücksetzen des Passworts wurde an die angegebene E-Mail-Adresse gesendet", + + "auth_login_breadcrumb": "Anmelden", + "auth_login_title": "Willkommen zurück", + "auth_login_loading_methods": "Verfügbare Anmeldemethoden werden geladen", + "auth_login_choose_method": "Melde dich mit einer dieser Methoden an", + "auth_login_with_account": "Melde dich mit deinem OpenShock-Konto an", + "auth_login_activation_after_signup": "Dein Konto wurde erstellt. Wir haben dir einen Aktivierungslink per E-Mail geschickt. Sieh in deinem Posteingang und im Spam-Ordner nach.", + "auth_login_activation_pending": "Dein Konto ist noch nicht aktiviert. Wir haben dir einen Aktivierungslink per E-Mail geschickt. Sieh in deinem Posteingang und im Spam-Ordner nach. Falls nichts ankommt, melde dich später erneut an, um einen neuen Link zu erhalten.", + "auth_login_oauth_only": "Dieses Konto hat kein Passwort. Melde dich über den Anbieter an, mit dem du dich registriert hast.", + "auth_login_oauth_only_with_buttons": "Dieses Konto hat kein Passwort. Melde dich über die Schaltflächen oben mit dem Anbieter an, mit dem du dich registriert hast.", + "auth_login_or_continue_with": "Oder weiter mit", + "auth_login_username_label": "Benutzername oder E-Mail", + "auth_login_password_label": "Passwort", + "auth_login_submit": "Anmelden", + "auth_login_signup_prompt": "Noch kein Konto? {#signup}Registrieren{/signup}", + "auth_login_legal": "Mit einem Klick auf „Anmelden“ stimmst du unseren {#tos}Nutzungsbedingungen{/tos} und unserer {#privacy}Datenschutzerklärung{/privacy} zu.", + + "auth_oauth_create_breadcrumb": "Mit {provider} registrieren", + "auth_oauth_create_title": "Mit {#provider}{provider}{/provider} registrieren", + "auth_oauth_create_description": "Vervollständige deine Kontodaten", + "auth_oauth_create_username_label": "Benutzername", + "auth_oauth_create_username_placeholder": "Benutzername", + "auth_oauth_create_email_label": "E-Mail", + "auth_oauth_create_email_placeholder": "E-Mail", + "auth_oauth_create_submit": "Registrieren", + "auth_oauth_create_legal": "Mit einem Klick auf „Registrieren“ stimmst du unseren {#tos}Nutzungsbedingungen{/tos} und unserer {#privacy}Datenschutzerklärung{/privacy} zu.", + + "auth_oauth_error_breadcrumb": "Authentifizierungsfehler", + "auth_oauth_error_title": "Authentifizierungsfehler", + "auth_oauth_error_back": "Zurück zur Anmeldung", + + "auth_reset_breadcrumb": "Passwort zurücksetzen", + "auth_reset_title": "Passwort zurücksetzen", + "auth_reset_checking": "Link zum Zurücksetzen wird geprüft...", + "auth_reset_choose_password": "Wähle ein neues Passwort für dein Konto", + "auth_reset_invalid": "Dieser Link zum Zurücksetzen ist ungültig oder abgelaufen", + "auth_reset_password_label": "Neues Passwort", + "auth_reset_password_placeholder": "Neues Passwort", + "auth_reset_confirm_label": "Neues Passwort bestätigen", + "auth_reset_confirm_placeholder": "Neues Passwort bestätigen", + "auth_reset_submit": "Passwort zurücksetzen", + "auth_reset_request_new": "Neuen Link anfordern", + "auth_reset_toast_success": "Passwort wurde zurückgesetzt", + + "auth_signup_breadcrumb": "Registrieren", + "auth_signup_toast_created_mail": "Konto erfolgreich erstellt. Bitte sieh in deinen E-Mails nach, um dein Konto zu bestätigen.", + "auth_signup_toast_created_no_mail": "Konto erfolgreich erstellt. Du kannst dich jetzt anmelden.", + "auth_signup_dialog_title": "Willkommen! Danke für deine Registrierung! ❤️", + "auth_signup_dialog_created_mail": "Dein Konto wurde erstellt. 🎉 Bitte sieh in deinen E-Mails nach, um dein Konto zu bestätigen.", + "auth_signup_dialog_verify_then_login": "Nachdem du deine E-Mail-Adresse bestätigt hast, kannst du dich anmelden.", + "auth_signup_dialog_created_no_mail": "Dein Konto wurde erstellt und ist einsatzbereit. 🎉", + "auth_signup_dialog_login_now": "Du kannst dich jetzt anmelden.", + "auth_signup_dialog_ok": "OK", + "auth_signup_title": "Konto erstellen", + "auth_signup_loading_methods": "Verfügbare Registrierungsmethoden werden geladen", + "auth_signup_using_email": "Registrierung per E-Mail", + "auth_signup_choose_method": "Wähle deine bevorzugte Registrierungsmethode", + "auth_signup_back": "Zurück", + "auth_signup_username_label": "Benutzername", + "auth_signup_username_placeholder": "Max OpenShock", + "auth_signup_email_label": "E-Mail", + "auth_signup_email_placeholder": "max@example.com", + "auth_signup_password_label": "Passwort", + "auth_signup_password_placeholder": "Passwort", + "auth_signup_confirm_label": "Passwort bestätigen", + "auth_signup_confirm_placeholder": "Passwort bestätigen", + "auth_signup_submit": "Konto erstellen", + "auth_signup_signin_prompt": "Du hast schon ein Konto? {#signin}Anmelden{/signin}", + "auth_signup_or": "Oder", + "auth_signup_with_email": "Mit E-Mail registrieren", + "auth_signup_legal": "Mit einem Klick auf „Konto erstellen“ stimmst du unseren {#tos}Nutzungsbedingungen{/tos} und unserer {#privacy}Datenschutzerklärung{/privacy} zu.", + + "auth_verify_breadcrumb": "E-Mail bestätigen", + "auth_verify_title": "E-Mail bestätigen", + "auth_verify_verifying": "Deine E-Mail-Adresse wird bestätigt...", + "auth_verify_success": "Deine E-Mail-Adresse wurde bestätigt", + "auth_verify_failed": "Dieser Bestätigungslink ist ungültig oder abgelaufen", + "auth_verify_missing_token": "Bestätigungstoken fehlt", + "auth_verify_continue": "Weiter zur Anmeldung", + "auth_verify_toast_success": "E-Mail-Adresse bestätigt" +} diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 00000000..391589d0 --- /dev/null +++ b/messages/en.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Toggle Sidebar", + "header_avatar_alt": "{name}'s avatar", + "header_menu_profile": "Profile", + "header_menu_settings": "Settings", + "header_menu_logout": "Logout", + "header_login": "Login", + "header_signup": "Sign Up", + "header_github_title": "Project GitHub", + "header_discord_title": "Community Discord", + + "footer_made_with": "Made with {#heart/} by the {#team}OpenShock Team{/team}", + "footer_connection_status_label": "Server connection status", + "footer_signalr_state": "SignalR State", + "footer_frontend_version": "Frontend Version", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Backend Version", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Language", + + "sidebar_home_link_label": "OpenShock home", + "sidebar_group_general": "General", + "sidebar_home": "Home", + "sidebar_shockers": "Shockers", + "sidebar_shared_shockers": "Shared Shockers", + "sidebar_hubs": "Hubs", + "sidebar_user_shares": "User Shares", + "sidebar_public_shares": "Public Shares", + "sidebar_serial_terminal": "Serial Terminal", + "sidebar_group_admin": "Admin", + "sidebar_admin_monitoring": "Monitoring", + "sidebar_admin_online_hubs": "Online Hubs", + "sidebar_admin_online_gateways": "Online Gateways", + "sidebar_admin_management": "Management", + "sidebar_admin_users": "Users", + "sidebar_admin_blacklists": "Blacklists", + "sidebar_admin_webhooks": "Webhooks", + "sidebar_admin_mail": "Mail", + "sidebar_admin_automation_tokens": "Automation Tokens", + "sidebar_admin_configuration": "Configuration", + "sidebar_admin_hangfire": "Hangfire", + "sidebar_group_settings": "Settings", + "sidebar_settings_api_tokens": "API Tokens", + "sidebar_settings_sessions": "Sessions", + "sidebar_settings_audit_log": "Audit Log", + "sidebar_settings_connections": "Connections", + "sidebar_settings_account": "Account Settings", + + "error_meta_title": "Service Unavailable", + "error_meta_description": "OpenShock is currently unavailable", + "error_meta_logo_alt": "OpenShock Logo", + "error_fallback_message": "Something went wrong.", + "error_not_found_description": "The page you're looking for doesn't exist.", + "error_unexpected_description": "An unexpected error occurred.", + "error_go_back": "Go back", + + "auth_layout_logo_alt": "OpenShock Logo", + "auth_layout_invalid_redirect_removed": "An invalid redirect URL was removed for your safety.", + + "auth_activate_breadcrumb": "Activate Account", + "auth_activate_title": "Activate Account", + "auth_activate_activating": "Activating your account...", + "auth_activate_success": "Your account has been activated", + "auth_activate_failed": "This activation link is invalid or has expired", + "auth_activate_missing_token": "Missing activation token", + "auth_activate_continue": "Continue to login", + "auth_activate_toast_success": "Account activated", + + "auth_forgot_breadcrumb": "Forgot Password", + "auth_forgot_title": "Forgot Password", + "auth_forgot_description": "Enter your email to reset your password", + "auth_forgot_email_label": "Email", + "auth_forgot_email_placeholder": "Email", + "auth_forgot_submit": "Reset Password", + "auth_forgot_signin_prompt": "Remember your password? {#signin}Sign in{/signin}", + "auth_forgot_mail_disabled": "Password resets are unavailable on this instance because it has no mail provider configured. Please contact your administrator for help recovering your account.", + "auth_forgot_toast_sent": "Reset password request has been sent to the given email", + + "auth_login_breadcrumb": "Login", + "auth_login_title": "Welcome back", + "auth_login_loading_methods": "Loading available login methods", + "auth_login_choose_method": "Login with one of these methods", + "auth_login_with_account": "Login with your OpenShock Account", + "auth_login_activation_after_signup": "Your account has been created. We've sent an activation link to your email address. Check your inbox and spam folder.", + "auth_login_activation_pending": "Your account isn't activated yet. We've sent an activation link to your email address. Check your inbox and spam folder. If nothing arrives, try logging in again later to get a new link.", + "auth_login_oauth_only": "This account has no password. Sign in with the provider you signed up with.", + "auth_login_oauth_only_with_buttons": "This account has no password. Sign in with the provider you signed up with using the buttons above.", + "auth_login_or_continue_with": "Or continue with", + "auth_login_username_label": "Username or Email", + "auth_login_password_label": "Password", + "auth_login_submit": "Login", + "auth_login_signup_prompt": "Don't have an account? {#signup}Sign up{/signup}", + "auth_login_legal": "By clicking Login, you agree to our {#tos}Terms of Service{/tos} and {#privacy}Privacy Policy{/privacy}.", + + "auth_oauth_create_breadcrumb": "Sign Up With {provider}", + "auth_oauth_create_title": "Sign Up With {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Complete your account details", + "auth_oauth_create_username_label": "Username", + "auth_oauth_create_username_placeholder": "Username", + "auth_oauth_create_email_label": "Email", + "auth_oauth_create_email_placeholder": "Email", + "auth_oauth_create_submit": "Sign Up", + "auth_oauth_create_legal": "By clicking Sign Up, you agree to our {#tos}Terms of Service{/tos} and {#privacy}Privacy Policy{/privacy}.", + + "auth_oauth_error_breadcrumb": "Authentication Error", + "auth_oauth_error_title": "Authentication Error", + "auth_oauth_error_back": "Back to login", + + "auth_reset_breadcrumb": "Reset Password", + "auth_reset_title": "Reset Password", + "auth_reset_checking": "Verifying reset link...", + "auth_reset_choose_password": "Choose a new password for your account", + "auth_reset_invalid": "This reset link is invalid or has expired", + "auth_reset_password_label": "New Password", + "auth_reset_password_placeholder": "New Password", + "auth_reset_confirm_label": "Confirm New Password", + "auth_reset_confirm_placeholder": "Confirm New Password", + "auth_reset_submit": "Reset Password", + "auth_reset_request_new": "Request a new reset link", + "auth_reset_toast_success": "Password has been reset", + + "auth_signup_breadcrumb": "Sign Up", + "auth_signup_toast_created_mail": "Account created successfully. Please check your email to verify your account.", + "auth_signup_toast_created_no_mail": "Account created successfully. You can log in now.", + "auth_signup_dialog_title": "Welcome! Thank you for signing up! ❤️", + "auth_signup_dialog_created_mail": "Your account has been created. 🎉 Please check your email to verify your account.", + "auth_signup_dialog_verify_then_login": "After verifying your email, you can log in to your account.", + "auth_signup_dialog_created_no_mail": "Your account has been created and is ready to use. 🎉", + "auth_signup_dialog_login_now": "You can log in to your account now.", + "auth_signup_dialog_ok": "Ok", + "auth_signup_title": "Create your account", + "auth_signup_loading_methods": "Loading available sign-up methods", + "auth_signup_using_email": "Signing up using email", + "auth_signup_choose_method": "Choose your preferred sign-up method", + "auth_signup_back": "Back", + "auth_signup_username_label": "Username", + "auth_signup_username_placeholder": "John OpenShock", + "auth_signup_email_label": "Email", + "auth_signup_email_placeholder": "john@example.com", + "auth_signup_password_label": "Password", + "auth_signup_password_placeholder": "Password", + "auth_signup_confirm_label": "Confirm Password", + "auth_signup_confirm_placeholder": "Confirm Password", + "auth_signup_submit": "Create Account", + "auth_signup_signin_prompt": "Already have an account? {#signin}Sign in{/signin}", + "auth_signup_or": "Or", + "auth_signup_with_email": "Signup with Email", + "auth_signup_legal": "By clicking Create Account, you agree to our {#tos}Terms of Service{/tos} and {#privacy}Privacy Policy{/privacy}.", + + "auth_verify_breadcrumb": "Verify Email", + "auth_verify_title": "Verify Email", + "auth_verify_verifying": "Verifying your email address...", + "auth_verify_success": "Your email has been verified", + "auth_verify_failed": "This verification link is invalid or has expired", + "auth_verify_missing_token": "Missing verification token", + "auth_verify_continue": "Continue to login", + "auth_verify_toast_success": "Email verified" +} diff --git a/messages/nb.json b/messages/nb.json new file mode 100644 index 00000000..cda025c6 --- /dev/null +++ b/messages/nb.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Vis/skjul sidepanel", + "header_avatar_alt": "Avataren til {name}", + "header_menu_profile": "Profil", + "header_menu_settings": "Innstillinger", + "header_menu_logout": "Logg ut", + "header_login": "Logg inn", + "header_signup": "Registrer deg", + "header_github_title": "Prosjektet på GitHub", + "header_discord_title": "Discord-fellesskapet", + + "footer_made_with": "Laget med {#heart/} av {#team}OpenShock-teamet{/team}", + "footer_connection_status_label": "Tilkoblingsstatus til serveren", + "footer_signalr_state": "SignalR-status", + "footer_frontend_version": "Frontend-versjon", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Backend-versjon", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Språk", + + "sidebar_home_link_label": "OpenShock-startsiden", + "sidebar_group_general": "Generelt", + "sidebar_home": "Hjem", + "sidebar_shockers": "Shockere", + "sidebar_shared_shockers": "Delte shockere", + "sidebar_hubs": "Huber", + "sidebar_user_shares": "Brukerdelinger", + "sidebar_public_shares": "Offentlige delinger", + "sidebar_serial_terminal": "Seriell terminal", + "sidebar_group_admin": "Admin", + "sidebar_admin_monitoring": "Overvåking", + "sidebar_admin_online_hubs": "Tilkoblede huber", + "sidebar_admin_online_gateways": "Tilkoblede gatewayer", + "sidebar_admin_management": "Administrasjon", + "sidebar_admin_users": "Brukere", + "sidebar_admin_blacklists": "Svartelister", + "sidebar_admin_webhooks": "Webhooks", + "sidebar_admin_mail": "E-post", + "sidebar_admin_automation_tokens": "Automatiseringstokener", + "sidebar_admin_configuration": "Konfigurasjon", + "sidebar_admin_hangfire": "Hangfire", + "sidebar_group_settings": "Innstillinger", + "sidebar_settings_api_tokens": "API-tokener", + "sidebar_settings_sessions": "Økter", + "sidebar_settings_audit_log": "Aktivitetslogg", + "sidebar_settings_connections": "Tilkoblinger", + "sidebar_settings_account": "Kontoinnstillinger", + + "error_meta_title": "Tjenesten er utilgjengelig", + "error_meta_description": "OpenShock er for øyeblikket utilgjengelig", + "error_meta_logo_alt": "OpenShock-logo", + "error_fallback_message": "Noe gikk galt.", + "error_not_found_description": "Siden du leter etter finnes ikke.", + "error_unexpected_description": "Det oppstod en uventet feil.", + "error_go_back": "Gå tilbake", + + "auth_layout_logo_alt": "OpenShock-logo", + "auth_layout_invalid_redirect_removed": "En ugyldig videresendings-URL ble fjernet for din sikkerhet.", + + "auth_activate_breadcrumb": "Aktiver konto", + "auth_activate_title": "Aktiver konto", + "auth_activate_activating": "Aktiverer kontoen din...", + "auth_activate_success": "Kontoen din er aktivert", + "auth_activate_failed": "Denne aktiveringslenken er ugyldig eller utløpt", + "auth_activate_missing_token": "Aktiveringstoken mangler", + "auth_activate_continue": "Fortsett til innlogging", + "auth_activate_toast_success": "Kontoen er aktivert", + + "auth_forgot_breadcrumb": "Glemt passord", + "auth_forgot_title": "Glemt passord", + "auth_forgot_description": "Skriv inn e-postadressen din for å tilbakestille passordet", + "auth_forgot_email_label": "E-post", + "auth_forgot_email_placeholder": "E-post", + "auth_forgot_submit": "Tilbakestill passord", + "auth_forgot_signin_prompt": "Husker du passordet? {#signin}Logg inn{/signin}", + "auth_forgot_mail_disabled": "Tilbakestilling av passord er ikke tilgjengelig på denne instansen fordi ingen e-postleverandør er satt opp. Kontakt administratoren din for hjelp til å få tilbake tilgangen til kontoen.", + "auth_forgot_toast_sent": "En forespørsel om å tilbakestille passordet er sendt til den oppgitte e-postadressen", + + "auth_login_breadcrumb": "Logg inn", + "auth_login_title": "Velkommen tilbake", + "auth_login_loading_methods": "Laster inn tilgjengelige innloggingsmetoder", + "auth_login_choose_method": "Logg inn med en av disse metodene", + "auth_login_with_account": "Logg inn med OpenShock-kontoen din", + "auth_login_activation_after_signup": "Kontoen din er opprettet. Vi har sendt en aktiveringslenke til e-postadressen din. Sjekk innboksen og søppelpostmappen.", + "auth_login_activation_pending": "Kontoen din er ikke aktivert ennå. Vi har sendt en aktiveringslenke til e-postadressen din. Sjekk innboksen og søppelpostmappen. Hvis ingenting kommer, kan du prøve å logge inn igjen senere for å få en ny lenke.", + "auth_login_oauth_only": "Denne kontoen har ikke noe passord. Logg inn med leverandøren du registrerte deg med.", + "auth_login_oauth_only_with_buttons": "Denne kontoen har ikke noe passord. Logg inn med leverandøren du registrerte deg med ved å bruke knappene over.", + "auth_login_or_continue_with": "Eller fortsett med", + "auth_login_username_label": "Brukernavn eller e-post", + "auth_login_password_label": "Passord", + "auth_login_submit": "Logg inn", + "auth_login_signup_prompt": "Har du ikke en konto? {#signup}Registrer deg{/signup}", + "auth_login_legal": "Ved å klikke på «Logg inn» godtar du {#tos}vilkårene for bruk{/tos} og {#privacy}personvernerklæringen{/privacy} vår.", + + "auth_oauth_create_breadcrumb": "Registrer deg med {provider}", + "auth_oauth_create_title": "Registrer deg med {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Fyll ut kontoopplysningene dine", + "auth_oauth_create_username_label": "Brukernavn", + "auth_oauth_create_username_placeholder": "Brukernavn", + "auth_oauth_create_email_label": "E-post", + "auth_oauth_create_email_placeholder": "E-post", + "auth_oauth_create_submit": "Registrer deg", + "auth_oauth_create_legal": "Ved å klikke på «Registrer deg» godtar du {#tos}vilkårene for bruk{/tos} og {#privacy}personvernerklæringen{/privacy} vår.", + + "auth_oauth_error_breadcrumb": "Autentiseringsfeil", + "auth_oauth_error_title": "Autentiseringsfeil", + "auth_oauth_error_back": "Tilbake til innlogging", + + "auth_reset_breadcrumb": "Tilbakestill passord", + "auth_reset_title": "Tilbakestill passord", + "auth_reset_checking": "Kontrollerer lenken...", + "auth_reset_choose_password": "Velg et nytt passord for kontoen din", + "auth_reset_invalid": "Denne lenken for tilbakestilling er ugyldig eller utløpt", + "auth_reset_password_label": "Nytt passord", + "auth_reset_password_placeholder": "Nytt passord", + "auth_reset_confirm_label": "Bekreft nytt passord", + "auth_reset_confirm_placeholder": "Bekreft nytt passord", + "auth_reset_submit": "Tilbakestill passord", + "auth_reset_request_new": "Be om en ny lenke", + "auth_reset_toast_success": "Passordet er tilbakestilt", + + "auth_signup_breadcrumb": "Registrer deg", + "auth_signup_toast_created_mail": "Kontoen er opprettet. Sjekk e-posten din for å bekrefte kontoen.", + "auth_signup_toast_created_no_mail": "Kontoen er opprettet. Du kan logge inn nå.", + "auth_signup_dialog_title": "Velkommen! Takk for at du registrerte deg! ❤️", + "auth_signup_dialog_created_mail": "Kontoen din er opprettet. 🎉 Sjekk e-posten din for å bekrefte kontoen.", + "auth_signup_dialog_verify_then_login": "Når du har bekreftet e-postadressen, kan du logge inn på kontoen din.", + "auth_signup_dialog_created_no_mail": "Kontoen din er opprettet og klar til bruk. 🎉", + "auth_signup_dialog_login_now": "Du kan logge inn på kontoen din nå.", + "auth_signup_dialog_ok": "OK", + "auth_signup_title": "Opprett konto", + "auth_signup_loading_methods": "Laster inn tilgjengelige registreringsmetoder", + "auth_signup_using_email": "Registrering med e-post", + "auth_signup_choose_method": "Velg hvordan du vil registrere deg", + "auth_signup_back": "Tilbake", + "auth_signup_username_label": "Brukernavn", + "auth_signup_username_placeholder": "Ola OpenShock", + "auth_signup_email_label": "E-post", + "auth_signup_email_placeholder": "ola@example.com", + "auth_signup_password_label": "Passord", + "auth_signup_password_placeholder": "Passord", + "auth_signup_confirm_label": "Bekreft passord", + "auth_signup_confirm_placeholder": "Bekreft passord", + "auth_signup_submit": "Opprett konto", + "auth_signup_signin_prompt": "Har du allerede en konto? {#signin}Logg inn{/signin}", + "auth_signup_or": "Eller", + "auth_signup_with_email": "Registrer deg med e-post", + "auth_signup_legal": "Ved å klikke på «Opprett konto» godtar du {#tos}vilkårene for bruk{/tos} og {#privacy}personvernerklæringen{/privacy} vår.", + + "auth_verify_breadcrumb": "Bekreft e-post", + "auth_verify_title": "Bekreft e-post", + "auth_verify_verifying": "Bekrefter e-postadressen din...", + "auth_verify_success": "E-postadressen din er bekreftet", + "auth_verify_failed": "Denne bekreftelseslenken er ugyldig eller utløpt", + "auth_verify_missing_token": "Bekreftelsestoken mangler", + "auth_verify_continue": "Fortsett til innlogging", + "auth_verify_toast_success": "E-postadressen er bekreftet" +} diff --git a/package.json b/package.json index 6a3402c6..3ed249e7 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "build": "cross-env BUILD_ENV=production vite build", "preview": "cross-env BUILD_ENV=production vite preview", "prepare": "svelte-kit sync || echo '' && husky", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "check": "svelte-kit sync && pnpm run i18n:compile && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && pnpm run i18n:compile && svelte-check --tsconfig ./tsconfig.json --watch", "lint": "eslint .", "format:check": "prettier --check .", "format": "prettier --write .", @@ -17,12 +17,15 @@ "test:e2e": "playwright test", "test:unit": "vitest", "regen-api": "node scripts/regenerate-api.js", + "i18n:compile": "paraglide-js compile --silent", "check:problem-types": "node scripts/check-problem-types.js --strict" }, "devDependencies": { "@eslint/compat": "^2.1.1", "@eslint/js": "latest", "@hey-api/openapi-ts": "^0.99.0", + "@inlang/paraglide-js": "^3.2.0", + "@inlang/paraglide-js-svelte": "^1.0.5", "@internationalized/date": "^3.12.4", "@lucide/svelte": "^1.61.0", "@microsoft/signalr": "^10.0.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8940896f..4775c6f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -249,6 +249,12 @@ importers: '@hey-api/openapi-ts': specifier: ^0.99.0 version: 0.99.0(typescript@6.0.3) + '@inlang/paraglide-js': + specifier: ^3.2.0 + version: 3.2.0(eslint@10.13.0(jiti@2.7.0)(supports-color@10.2.2))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)) + '@inlang/paraglide-js-svelte': + specifier: ^1.0.5 + version: 1.0.5(@inlang/paraglide-js@3.2.0(eslint@10.13.0(jiti@2.7.0)(supports-color@10.2.2))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)))(svelte@5.57.2(@typescript-eslint/types@8.72.0)) '@internationalized/date': specifier: ^3.12.4 version: 3.12.4 @@ -495,6 +501,74 @@ importers: packages: + '@bytecodealliance/jco-node-fs-darwin-arm64@0.3.2': + resolution: {integrity: sha512-WXQq1tRVUWhbK+ImH0G8Kni2AR0sdokLf/KyXRw5P6n4A9ZbgMHOuPfN/PEsYgw4eosuTAonutK0xILk/hA81A==} + engines: {node: '>=20'} + cpu: [arm64] + os: [darwin] + + '@bytecodealliance/jco-node-fs-darwin-x64@0.3.2': + resolution: {integrity: sha512-Fy/OjHnto7hNE5ksewVkmAMXG/uXyj+RAKFZWQFTzFkp73UyT5vuPjKq3nvx9KolUGWBG37BudEMb4ZAoD9TbQ==} + engines: {node: '>=20'} + cpu: [x64] + os: [darwin] + + '@bytecodealliance/jco-node-fs-linux-arm64-gnu@0.3.2': + resolution: {integrity: sha512-OafRtRu3tfOFQARfkia/LDEqSo2R3yAL1xPwzf4RcRW6JqkOY7qKWsUS5+LPVo/aFaSkMX7F7bdFGpFPmcoZJg==} + engines: {node: '>=20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@bytecodealliance/jco-node-fs-linux-arm64-musl@0.3.2': + resolution: {integrity: sha512-JblDiwD7+Y+w0d/tjaNDX4QdGvh5lyuDc+S/jDeT7sYA1caA9yiwIjLa8PC/lm2xudqSxD+Cuy4AosXvl+qI0g==} + engines: {node: '>=20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@bytecodealliance/jco-node-fs-linux-x64-gnu@0.3.2': + resolution: {integrity: sha512-6FzmHoLOsa6b4X9AuFLsvlpNF66Dt4sRRftZHaT1I1pYzCWrJkkpgNJhRXxf67ZPmOwBZJT4dan/g752Rx4xRw==} + engines: {node: '>=20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@bytecodealliance/jco-node-fs-linux-x64-musl@0.3.2': + resolution: {integrity: sha512-SbAJG2PDV8YAHBESHU9sVSckOitJyvlezdF4CGP5/mOp01JnijVkLVXzGK62yVSfa5TpAybbSd1rk+v36NAW/Q==} + engines: {node: '>=20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@bytecodealliance/jco-node-fs-win32-arm64-msvc@0.3.2': + resolution: {integrity: sha512-nUWYdFysRQJGsIOCk0KAYN2smkNYiHG7rZ7m+0ZXqP+5wqWxKEjkDsORwXiBjdaWFLVww0anZ3SBxrju6P7hXQ==} + engines: {node: '>=20'} + cpu: [arm64] + os: [win32] + + '@bytecodealliance/jco-node-fs-win32-x64-msvc@0.3.2': + resolution: {integrity: sha512-RsPj2t8TvltsMWK7/Z7JcVC/IWPMi07/AJkNLiNTCuGTdT/sseqTThJJwhuz3sjFw7dJdxA+bSGT+23y/KT0fw==} + engines: {node: '>=20'} + cpu: [x64] + os: [win32] + + '@bytecodealliance/jco-node-fs@0.3.2': + resolution: {integrity: sha512-yxvqQreNcOQdMsEKZomkSqThtkxrAz3f1Cmy8BsV0UHi9saa2/D1KDB14IAXPYwDq/maS6lCzS+MuMQx6QkUgA==} + engines: {node: '>=20'} + + '@bytecodealliance/jco-transpile@0.13.0': + resolution: {integrity: sha512-9hZ2GMqOlYP1Bf7qQ8v3sCsmkFoXGbgi0HtjHipzrgyk+9GGDzvvbehTGe0oM54rcbKJacvyjpPDn7wtpgzwYA==} + + '@bytecodealliance/preview2-shim@0.24.1': + resolution: {integrity: sha512-QGZsKD5M76xBhwu3BzLWqYDEpaMX90QWrrfhkQPn2pN9t378koumtdAVH4lr25mqGCZt10YNXkWvyWkw/vJbLQ==} + + '@bytecodealliance/preview2-shim@0.25.0': + resolution: {integrity: sha512-6ZSDRJ8hJc/febZepzd68z3KzLcOXyCOKtRm8LB2rgAU88xV7ykq6dMb5I4a+sBh1cuV2Yb1159gDzI1q8UsoQ==} + + '@bytecodealliance/preview3-shim@0.6.1': + resolution: {integrity: sha512-61mEfjfgGmZhEMA0H3waawPKvYd3za9y0vJ7pmQz/9yvm4FR6Ef+cmqhnxDE7ezsM07MY1btP92ymmgsgFwxmg==} + '@cacheable/memory@2.2.0': resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} @@ -557,9 +631,18 @@ packages: '@dagrejs/graphlib@3.0.4': resolution: {integrity: sha512-HxZ7fCvAwTLCWCO0WjDkzAFQze8LdC6iOpKbetDKHIuDfIgMlIzYzqZ4nxwLlclQX+3ZVeZ1K2OuaOE2WWcyOg==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.3': resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -983,6 +1066,31 @@ packages: cpu: [x64] os: [win32] + '@inlang/paraglide-js-svelte@1.0.5': + resolution: {integrity: sha512-QBIlMX/I/IzsWJLx5O0Xt/aBhQs0cnifozswLxBt1IYGANUyfT7Seiu29jZ1OOHHyfrfa1g4AviO3nXsbrKsYw==} + peerDependencies: + '@inlang/paraglide-js': '>=2.11.0 <4' + svelte: ^5.0.0 + + '@inlang/paraglide-js@3.2.0': + resolution: {integrity: sha512-mjcVM+GTbr6Wr463gMHSGUHF0x7y1W0VizJSPyX8gjyxNFYKW0C0rRf1QEtWWMD8d0NBdzo4i7tnfcagR1FyKg==} + hasBin: true + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + vite: '>=5.0.0' + peerDependenciesMeta: + eslint: + optional: true + vite: + optional: true + + '@inlang/recommend-sherlock@0.2.1': + resolution: {integrity: sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==} + + '@inlang/sdk@4.2.0': + resolution: {integrity: sha512-Cmaqvn34wsxiF27318Zw2rg+017HHNJWV5MH8rfyVKVANZNpzlztxzRbFienE/gASMLD8oluYHHolIb/p6Egrw==} + engines: {node: '>=20.0.0'} + '@internationalized/date@3.12.4': resolution: {integrity: sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ==} @@ -1029,6 +1137,9 @@ packages: '@layerstack/utils@2.0.0-next.19': resolution: {integrity: sha512-HyXCEz9pge4rFWklrSgFr3YIwdE1gP8WM6GPetmnKPDfYLF1/oetRFKI9FqiE+dw4brnULGPKJsVhjM3vQfD4A==} + '@lix-js/sdk@0.20.1': + resolution: {integrity: sha512-oscOFP8Cr1My6Wh2KVYC9+aJ1mHs+d7vnD+7PPJaA4L+Y1FR37Sm99SrFGfvUizaj/XtRi2GlDF2IOFt9iXMNQ==} + '@lucide/svelte@1.61.0': resolution: {integrity: sha512-Vwj4ADoKgVogLFpEbXv2PS2hh2fj+aAqPuiNydI88Zkyb1l7veL4fXgRrOX3tgpESHhIGneNkSSYhHupvCBshg==} peerDependencies: @@ -1054,6 +1165,13 @@ packages: os: [linux] libc: [glibc] + '@napi-rs/wasm-runtime@1.2.7': + resolution: {integrity: sha512-zJccwplOAbgfZju95IUHazLnx9RAPtPN2YGJXI6UbrgeaR9QfPjGrQL6ERkSAtnVCA4Q+83ApGyaX5zdpCaKkQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || 2.0.0-beta.2 || 2.0.0-beta.3 + '@emnapi/runtime': ^1.7.1 || 2.0.0-beta.2 || 2.0.0-beta.3 + '@opentelemetry/api@1.10.0': resolution: {integrity: sha512-lMkyes8iHrG+mk6WH7X70MdCLI2RS0FwNBY1PyQ8UemaijE2m5mv5A0Niy3HtbYTFrgkUEQjFMXmSAk2hQDuPQ==} engines: {node: '>=8.0.0'} @@ -1140,6 +1258,133 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.10.0 <1.11.0' + '@oxc-minify/binding-android-arm-eabi@0.136.0': + resolution: {integrity: sha512-gk1jzky1vLeOcK7w9Ib2RT/UooZ7O21s8YSeXCsy4tzsmguKb3Cbsv/Ra2fKhMp4mKeKCzvC1Xa41OfSBWfWdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-minify/binding-android-arm64@0.136.0': + resolution: {integrity: sha512-5GIYanqud/ohXEcg2dsdn0+eSKXqclw5qlRUkvSM1sKEa4ZQ7hqOk8XPJBNPh3LXXw+Qw85B6jkmOFLKsCHp5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-minify/binding-darwin-arm64@0.136.0': + resolution: {integrity: sha512-QAMpHKXWOoMCTenSZjQjVT5YmMooujlCKAf1T3PHFl2LuaA6ukQFXba0bgSLgAxRrAeX/itkwi4lzVQXtYmnRg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-minify/binding-darwin-x64@0.136.0': + resolution: {integrity: sha512-rDnoPj8hztiU8C1VNKuzujh7SgTJlgiUvUCiWQjTnVSLsABsLW9ZWd1l1Ji0VN3UW39wGpptgvahIWTrHnGH/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-minify/binding-freebsd-x64@0.136.0': + resolution: {integrity: sha512-9fGSgP7uyPLCTjMx9z3AUV4UjnfOVoGTH5JzamLegPwGLJ97Gr1jht1437CRk8ZNabGmxQR6qNn3e+Tox2lswg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-minify/binding-linux-arm-gnueabihf@0.136.0': + resolution: {integrity: sha512-k0QvZS+x6ROI4O5Ikqn9dDU2dmkEJ46Yh6baZJI/eUk/a1k1GOs9/vyXItLZrKlwYrSgdEx2BFXbKgyd1NyRPw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-minify/binding-linux-arm-musleabihf@0.136.0': + resolution: {integrity: sha512-iRFxUc8ubotVfuyq3e4LPGbHrjvWd1/B3499a55GueMLc1EDJ0fHzNXJiCx2eMQGfzHtHodjmvrJ2BSvSXBNhQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-minify/binding-linux-arm64-gnu@0.136.0': + resolution: {integrity: sha512-hbKxNCXJsULrb6EyG5SumzJKuPfP8Y0UQ/TElRgXVUhyyPf6cBPlF8HMq+MAylF98ACBzV8jBp2Mm62lJyTo+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-minify/binding-linux-arm64-musl@0.136.0': + resolution: {integrity: sha512-T+u/CGKW0UXemJeVux4ix6rK+ug3bQCUEbu5hFlX9K61KQNd44rOcrM15mDK99L7USaXwmiwHeIkEDvmmyDMSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-minify/binding-linux-ppc64-gnu@0.136.0': + resolution: {integrity: sha512-WNPTN2F448tb49BoqjV9IMRzZWa0WB8GHzrB+bnxKQPOyzz07T6rmDKrr4Hjl81ADGjDYSsr1fgXcjyLQFdMdw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-minify/binding-linux-riscv64-gnu@0.136.0': + resolution: {integrity: sha512-MvcCfu8OeabrmNN9wqhh8Gqizg68RQ3iOvXBJmlLMy5p1PC6az9bAwqOk98lsCmKI4I5LpULekOlfxRYh3o5Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-minify/binding-linux-riscv64-musl@0.136.0': + resolution: {integrity: sha512-rwJXFUFFA0egAULtOOgfW8YhhLhtWrr2qFxftvZ7/U6Xtxiev8i+eCwSNW1RpHiX74QMsJVpRE3ykoefqb1MtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-minify/binding-linux-s390x-gnu@0.136.0': + resolution: {integrity: sha512-qizAxHmj7VHm5/D5qiIlvzxAS2tlmadDOJ5XFI7zx/wfnk/+fdk5iVaHOv+C4tDE/4wLQyMoBYVIs6R1LeN91w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-minify/binding-linux-x64-gnu@0.136.0': + resolution: {integrity: sha512-gcyLAad0UblkEhGjVfx5Mwq63OSx8r04IbcfwzpZYpHisO/vioMsrs4GkDsQZ2Ejc8cdUk6YMIrf9FwNBzh+Dw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-minify/binding-linux-x64-musl@0.136.0': + resolution: {integrity: sha512-QAK6kwBjljX6luYzeppVdRP3gTaFNFfp9iS/V0cCvU43wUZay5xFXBgmzBHYrzZV2MhzURNnXfEZRo9poAxyvA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-minify/binding-openharmony-arm64@0.136.0': + resolution: {integrity: sha512-nIGxkTOnN9GwslmooxbZ3Ti2RgbX9QLHy0X9uO0pslOT+4dsNVUawBU/6r4M5cWM+ZdRD2tOF4V+n0l8tS59nQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-minify/binding-wasm32-wasi@0.136.0': + resolution: {integrity: sha512-YB3G8AHT6PpDS8/DfjlJrQjywj+/6gCkNBcAJdYzcYbQRIZE6SaTNowiGuZPR1m1jOl1WjcjefniKSmVxzhvRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-minify/binding-win32-arm64-msvc@0.136.0': + resolution: {integrity: sha512-UVRH3BlNc2rmla+sbSd5xTF/u+esCqZS6VSWPolZybcBh3TlVe1Sf5MOuHQWUaFoyv5uFOvee4hOfS/14nPvZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-minify/binding-win32-ia32-msvc@0.136.0': + resolution: {integrity: sha512-zFGkUd82jci0E+isc2O36lVmmXVUDXyEMN2RYIyez+MBNqhdjPLIGx62FCsrJtLKeuiPf/8XkFv977aUtndUng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-minify/binding-win32-x64-msvc@0.136.0': + resolution: {integrity: sha512-VHgPwd5xibC9UYxBeq7nURTjY+wxhdumllij6JaCIUVfD9IRcrA66SVlJ5fPNoUXcRp8ZXhJfoIZwVz9wAEGJw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-project/types@0.155.0': resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==} @@ -1401,6 +1646,9 @@ packages: cpu: [x64] os: [win32] + '@sinclair/typebox@0.31.30': + resolution: {integrity: sha512-MGsM7bVmHg3sUKCphlu3SGQ+T+5JTbygZWYArfKQCW5anJeACHeqLhcnf+R7XlCWZ+QR3C8JTBx3kCKJTN69ow==} + '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} @@ -1574,6 +1822,9 @@ packages: resolution: {integrity: sha512-zQvlxSNi2qGM3L4jh85Z6hqMfdIR5qOfD9KHOU32nuKFJGN0RlJ7UvV1lsPb1fPVhpFYI0VP3jJGxLdLDpTKjQ==} engines: {node: '>=20'} + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1846,6 +2097,9 @@ packages: resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1869,6 +2123,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + binaryen@130.0.0: + resolution: {integrity: sha512-XDrb+zql0RbFPKgj7MuH9zOc78R3Fa/P/VSGnnpdwYsvNZPWjcMYMdAkKCOQEL2A7yqgjSMTDRFp6gfSDW+/QQ==} + hasBin: true + bits-ui@2.20.2: resolution: {integrity: sha512-cb1eQD1wZEhJWb1dTLiAdYWqeUI5v7f+7EAHmetxBuELI9lo4cpAIfGEjald/PR4wTR80Fu5DFH+IaY1OKn+tg==} engines: {node: '>=20'} @@ -1941,6 +2199,10 @@ packages: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@15.0.0: resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} engines: {node: '>=22.12.0'} @@ -1949,6 +2211,10 @@ packages: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} + comment-json@4.6.2: + resolution: {integrity: sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==} + engines: {node: '>= 6'} + commenting@1.1.0: resolution: {integrity: sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==} @@ -1958,6 +2224,10 @@ packages: confbox@0.3.1: resolution: {integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} + cookie@1.1.1: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} @@ -2251,6 +2521,11 @@ packages: resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esptool-js@0.7.1: resolution: {integrity: sha512-vw1isT5ROHgpRmMG3y6B81dceeZ7O1WhF1x05qFbAlH8N0N/pOQqODzJLA+36lCbOXCw06iA4CWctyrQQ7VeSA==} @@ -2317,6 +2592,9 @@ packages: fetch-cookie@2.2.0: resolution: {integrity: sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + file-entry-cache@11.1.5: resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} @@ -2463,6 +2741,11 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} @@ -2473,6 +2756,10 @@ packages: known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + kysely@0.28.19: + resolution: {integrity: sha512-sXoKWuQjtorwxDLiws0ZSPGGne/hs+fGHIII7vXquSBFgHwLXgFIHN9tTv3kNdVkJ5d3xtXC84GjjWMYLqdFWg==} + engines: {node: '>=20.0.0'} + layerchart@2.9.1: resolution: {integrity: sha512-ftS5hcrogs+AhfSnNzTkFZCMTjPh3UYcU7MNMpN+M+SHx1AIm/IKxY2t2cJMmxAmc+JJfbB5Z6BmnqV6v7IOnw==} peerDependencies: @@ -2730,6 +3017,10 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + oxc-minify@0.136.0: + resolution: {integrity: sha512-u9UvPFTYVwQ/i1kpp/c20S/SbvLaLV0Ngzyhgi0YV4B1SlxM7RgnnePl4vY9N3Z9sTRcozL1MRStI3PlUN9RFA==} + engines: {node: ^20.19.0 || >=22.12.0} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -3248,6 +3539,10 @@ packages: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + update-browserslist-db@1.3.4: resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} hasBin: true @@ -3260,6 +3555,9 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + urlpattern-polyfill@10.1.0: + resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -3267,6 +3565,14 @@ packages: resolution: {integrity: sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==} hasBin: true + valibot@1.5.0: + resolution: {integrity: sha512-nil6AkP2TChWL43Z5uJ6GTxX01CUA+g8LWUM+N/rB9NBbkUMaUsi9PUNzlUPgoKASgmx9f7eGOYpJ04/fSa6FQ==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + vaul-svelte@1.0.0-next.7: resolution: {integrity: sha512-7zN7Bi3dFQixvvbUJY9uGDe7Ws/dGZeBQR2pXdXmzQiakjrxBvWo0QrmsX3HK+VH+SZOltz378cmgmCS9f9rSg==} engines: {node: '>=18', pnpm: '>=8.7.0'} @@ -3379,6 +3685,9 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -3466,6 +3775,61 @@ packages: snapshots: + '@bytecodealliance/jco-node-fs-darwin-arm64@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-darwin-x64@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-linux-arm64-gnu@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-linux-arm64-musl@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-linux-x64-gnu@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-linux-x64-musl@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-win32-arm64-msvc@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs-win32-x64-msvc@0.3.2': + optional: true + + '@bytecodealliance/jco-node-fs@0.3.2': + optionalDependencies: + '@bytecodealliance/jco-node-fs-darwin-arm64': 0.3.2 + '@bytecodealliance/jco-node-fs-darwin-x64': 0.3.2 + '@bytecodealliance/jco-node-fs-linux-arm64-gnu': 0.3.2 + '@bytecodealliance/jco-node-fs-linux-arm64-musl': 0.3.2 + '@bytecodealliance/jco-node-fs-linux-x64-gnu': 0.3.2 + '@bytecodealliance/jco-node-fs-linux-x64-musl': 0.3.2 + '@bytecodealliance/jco-node-fs-win32-arm64-msvc': 0.3.2 + '@bytecodealliance/jco-node-fs-win32-x64-msvc': 0.3.2 + + '@bytecodealliance/jco-transpile@0.13.0': + dependencies: + '@bytecodealliance/preview2-shim': 0.24.1 + '@bytecodealliance/preview3-shim': 0.6.1 + binaryen: 130.0.0 + oxc-minify: 0.136.0 + + '@bytecodealliance/preview2-shim@0.24.1': + dependencies: + '@bytecodealliance/jco-node-fs': 0.3.2 + + '@bytecodealliance/preview2-shim@0.25.0': + dependencies: + '@bytecodealliance/jco-node-fs': 0.3.2 + + '@bytecodealliance/preview3-shim@0.6.1': + dependencies: + '@bytecodealliance/jco-node-fs': 0.3.2 + '@bytecodealliance/preview2-shim': 0.25.0 + '@cacheable/memory@2.2.0': dependencies: '@cacheable/utils': 2.5.0 @@ -3513,11 +3877,27 @@ snapshots: '@dagrejs/graphlib@3.0.4': {} + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.3': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@epic-web/invariant@1.0.0': {} '@esbuild/aix-ppc64@0.28.2': @@ -3821,6 +4201,40 @@ snapshots: '@img/sharp-win32-x64@0.35.5': optional: true + '@inlang/paraglide-js-svelte@1.0.5(@inlang/paraglide-js@3.2.0(eslint@10.13.0(jiti@2.7.0)(supports-color@10.2.2))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)))(svelte@5.57.2(@typescript-eslint/types@8.72.0))': + dependencies: + '@inlang/paraglide-js': 3.2.0(eslint@10.13.0(jiti@2.7.0)(supports-color@10.2.2))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)) + svelte: 5.57.2(@typescript-eslint/types@8.72.0) + + '@inlang/paraglide-js@3.2.0(eslint@10.13.0(jiti@2.7.0)(supports-color@10.2.2))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0))': + dependencies: + '@inlang/recommend-sherlock': 0.2.1 + '@inlang/sdk': 4.2.0 + commander: 11.1.0 + consola: 3.4.0 + jiti: 2.7.0 + json5: 2.2.3 + magic-string: 0.30.21 + unplugin: 2.3.11 + urlpattern-polyfill: 10.1.0 + valibot: 1.5.0(typescript@6.0.3) + optionalDependencies: + eslint: 10.13.0(jiti@2.7.0)(supports-color@10.2.2) + vite: 8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0) + transitivePeerDependencies: + - typescript + + '@inlang/recommend-sherlock@0.2.1': + dependencies: + comment-json: 4.6.2 + + '@inlang/sdk@4.2.0': + dependencies: + '@lix-js/sdk': 0.20.1 + '@sinclair/typebox': 0.31.30 + kysely: 0.28.19 + uuid: 14.0.3 + '@internationalized/date@3.12.4': dependencies: '@swc/helpers': 0.5.23 @@ -3882,6 +4296,12 @@ snapshots: d3-time: 3.1.0 d3-time-format: 4.1.0 + '@lix-js/sdk@0.20.1': + dependencies: + '@bytecodealliance/jco-transpile': 0.13.0 + binaryen: 130.0.0 + fflate: 0.8.3 + '@lucide/svelte@1.61.0(svelte@5.57.2(@typescript-eslint/types@8.72.0))': dependencies: svelte: 5.57.2(@typescript-eslint/types@8.72.0) @@ -3907,6 +4327,13 @@ snapshots: '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true + '@napi-rs/wasm-runtime@1.2.7(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.4 + optional: true + '@opentelemetry/api@1.10.0': {} '@opentelemetry/context-async-hooks@3.1.0(@opentelemetry/api@1.10.0)': @@ -4000,6 +4427,70 @@ snapshots: '@opentelemetry/sdk-logs': 3.1.0(@opentelemetry/api@1.10.0) '@opentelemetry/sdk-trace': 3.1.0(@opentelemetry/api@1.10.0) + '@oxc-minify/binding-android-arm-eabi@0.136.0': + optional: true + + '@oxc-minify/binding-android-arm64@0.136.0': + optional: true + + '@oxc-minify/binding-darwin-arm64@0.136.0': + optional: true + + '@oxc-minify/binding-darwin-x64@0.136.0': + optional: true + + '@oxc-minify/binding-freebsd-x64@0.136.0': + optional: true + + '@oxc-minify/binding-linux-arm-gnueabihf@0.136.0': + optional: true + + '@oxc-minify/binding-linux-arm-musleabihf@0.136.0': + optional: true + + '@oxc-minify/binding-linux-arm64-gnu@0.136.0': + optional: true + + '@oxc-minify/binding-linux-arm64-musl@0.136.0': + optional: true + + '@oxc-minify/binding-linux-ppc64-gnu@0.136.0': + optional: true + + '@oxc-minify/binding-linux-riscv64-gnu@0.136.0': + optional: true + + '@oxc-minify/binding-linux-riscv64-musl@0.136.0': + optional: true + + '@oxc-minify/binding-linux-s390x-gnu@0.136.0': + optional: true + + '@oxc-minify/binding-linux-x64-gnu@0.136.0': + optional: true + + '@oxc-minify/binding-linux-x64-musl@0.136.0': + optional: true + + '@oxc-minify/binding-openharmony-arm64@0.136.0': + optional: true + + '@oxc-minify/binding-wasm32-wasi@0.136.0': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.2.7(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@oxc-minify/binding-win32-arm64-msvc@0.136.0': + optional: true + + '@oxc-minify/binding-win32-ia32-msvc@0.136.0': + optional: true + + '@oxc-minify/binding-win32-x64-msvc@0.136.0': + optional: true + '@oxc-project/types@0.155.0': {} '@playwright/test@1.65.0': @@ -4146,6 +4637,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.65.0': optional: true + '@sinclair/typebox@0.31.30': {} + '@sindresorhus/is@7.2.0': {} '@speed-highlight/core@1.2.24': {} @@ -4288,6 +4781,11 @@ snapshots: dependencies: '@tanstack/store': 0.11.2 + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -4515,6 +5013,8 @@ snapshots: array-find-index@1.0.2: {} + array-timsort@1.0.3: {} + assertion-error@2.0.1: {} ast-metadata-inferer@0.8.1: @@ -4529,6 +5029,8 @@ snapshots: baseline-browser-mapping@2.11.32: {} + binaryen@130.0.0: {} + bits-ui@2.20.2(@internationalized/date@3.12.4)(@sveltejs/kit@3.1.0(@opentelemetry/api@1.10.0)(@sveltejs/vite-plugin-svelte@7.3.1(svelte@5.57.2(@typescript-eslint/types@8.72.0))(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)))(svelte@5.57.2(@typescript-eslint/types@8.72.0))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)))(svelte@5.57.2(@typescript-eslint/types@8.72.0)): dependencies: '@floating-ui/core': 1.8.0 @@ -4615,16 +5117,25 @@ snapshots: color-support@1.1.3: {} + commander@11.1.0: {} + commander@15.0.0: {} commander@7.2.0: {} + comment-json@4.6.2: + dependencies: + array-timsort: 1.0.3 + esprima: 4.0.1 + commenting@1.1.0: {} confbox@0.2.4: {} confbox@0.3.1: {} + consola@3.4.0: {} + cookie@1.1.1: {} cookie@2.0.1: {} @@ -4949,6 +5460,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.19.0) eslint-visitor-keys: 5.0.1 + esprima@4.0.1: {} + esptool-js@0.7.1: dependencies: atob-lite: 2.0.0 @@ -4999,6 +5512,8 @@ snapshots: set-cookie-parser: 2.7.3 tough-cookie: 4.1.4 + fflate@0.8.3: {} + file-entry-cache@11.1.5: dependencies: flat-cache: 6.1.23 @@ -5111,6 +5626,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -5119,6 +5636,8 @@ snapshots: known-css-properties@0.37.0: {} + kysely@0.28.19: {} + layerchart@2.9.1(@sveltejs/kit@3.1.0(@opentelemetry/api@1.10.0)(@sveltejs/vite-plugin-svelte@7.3.1(svelte@5.57.2(@typescript-eslint/types@8.72.0))(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)))(svelte@5.57.2(@typescript-eslint/types@8.72.0))(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.28.2)(jiti@2.7.0)))(svelte@5.57.2(@typescript-eslint/types@8.72.0)): dependencies: '@dagrejs/dagre': 2.0.4 @@ -5349,6 +5868,29 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + oxc-minify@0.136.0: + optionalDependencies: + '@oxc-minify/binding-android-arm-eabi': 0.136.0 + '@oxc-minify/binding-android-arm64': 0.136.0 + '@oxc-minify/binding-darwin-arm64': 0.136.0 + '@oxc-minify/binding-darwin-x64': 0.136.0 + '@oxc-minify/binding-freebsd-x64': 0.136.0 + '@oxc-minify/binding-linux-arm-gnueabihf': 0.136.0 + '@oxc-minify/binding-linux-arm-musleabihf': 0.136.0 + '@oxc-minify/binding-linux-arm64-gnu': 0.136.0 + '@oxc-minify/binding-linux-arm64-musl': 0.136.0 + '@oxc-minify/binding-linux-ppc64-gnu': 0.136.0 + '@oxc-minify/binding-linux-riscv64-gnu': 0.136.0 + '@oxc-minify/binding-linux-riscv64-musl': 0.136.0 + '@oxc-minify/binding-linux-s390x-gnu': 0.136.0 + '@oxc-minify/binding-linux-x64-gnu': 0.136.0 + '@oxc-minify/binding-linux-x64-musl': 0.136.0 + '@oxc-minify/binding-openharmony-arm64': 0.136.0 + '@oxc-minify/binding-wasm32-wasi': 0.136.0 + '@oxc-minify/binding-win32-arm64-msvc': 0.136.0 + '@oxc-minify/binding-win32-ia32-msvc': 0.136.0 + '@oxc-minify/binding-win32-x64-msvc': 0.136.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -5868,6 +6410,13 @@ snapshots: universalify@0.2.0: {} + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.19.0 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 + update-browserslist-db@1.3.4(browserslist@4.29.4): dependencies: browserslist: 4.29.4 @@ -5883,10 +6432,16 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + urlpattern-polyfill@10.1.0: {} + util-deprecate@1.0.2: {} uuid@14.0.3: {} + valibot@1.5.0(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + vaul-svelte@1.0.0-next.7(svelte@5.57.2(@typescript-eslint/types@8.72.0)): dependencies: runed: 0.23.4(svelte@5.57.2(@typescript-eslint/types@8.72.0)) @@ -5946,6 +6501,8 @@ snapshots: webidl-conversions@3.0.1: {} + webpack-virtual-modules@0.6.2: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 diff --git a/project.inlang/paraglide.config.ts b/project.inlang/paraglide.config.ts new file mode 100644 index 00000000..e5d9b595 --- /dev/null +++ b/project.inlang/paraglide.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from '@inlang/paraglide-js'; + +export default defineConfig({ + outdir: './src/lib/paraglide', + emitTsDeclarations: true, + // Pinned so the CLI (used by `check`) and the Vite plugin emit the same layout. + outputStructure: 'message-modules', + // `custom-query` is the one-off `?lang=` override, see src/lib/i18n/query-locale.ts. + strategy: ['custom-query', 'cookie', 'preferredLanguage', 'baseLocale'], + cookieSameSite: 'lax', + cookieSecure: 'auto', +}); diff --git a/project.inlang/settings.json b/project.inlang/settings.json new file mode 100644 index 00000000..4fdc710c --- /dev/null +++ b/project.inlang/settings.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "baseLocale": "en", + "locales": ["en", "de", "nb"], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4.5.0/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.3.0/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{locale}.json" + } +} diff --git a/src/app.html b/src/app.html index 1b63d067..8871682b 100644 --- a/src/app.html +++ b/src/app.html @@ -1,5 +1,5 @@ - + diff --git a/src/hooks.client.ts b/src/hooks.client.ts index b658e4f2..4f423552 100644 --- a/src/hooks.client.ts +++ b/src/hooks.client.ts @@ -1,6 +1,8 @@ import type { HandleClientError } from '@sveltejs/kit/hooks'; import 'temporal-polyfill/global'; +import '#lib/i18n/query-locale.js'; + import { versionGetBackendInfo } from '#lib/api/index.js'; import { handleApiError } from '#lib/errorhandling/apiErrorHandling.js'; import { authState, startAuthLifecycle } from '#lib/state/auth-state.svelte.js'; diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 2b4b29e7..4e7756b2 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,6 +1,25 @@ +import '#lib/i18n/query-locale.js'; +import { getTextDirection } from '#lib/paraglide/runtime.js'; +import { paraglideMiddleware } from '#lib/paraglide/server.js'; import { PRIVATE_BACKEND_TLS_INSECURE } from '$app/env/private'; import { PUBLIC_GITHUB_PROJECT_URL } from '$app/env/public'; -import type { Handle, ServerInit } from '@sveltejs/kit/hooks'; +import { type Handle, type ServerInit, sequence } from '@sveltejs/kit/hooks'; + +/** + * Resolves the request's locale and scopes it to this request, so `getLocale()` and message + * functions return it during SSR. + * Also fills the `%lang%` and `%dir%` placeholders in app.html. + */ +const i18nHandle: Handle = ({ event, resolve }) => + paraglideMiddleware(event.request, ({ request, locale }) => + resolve( + { ...event, request }, + { + transformPageChunk: ({ html }) => + html.replace('%lang%', locale).replace('%dir%', getTextDirection(locale)), + } + ) + ); /** * Security + metadata headers for every dynamically rendered response. @@ -9,7 +28,7 @@ import type { Handle, ServerInit } from '@sveltejs/kit/hooks'; * deploy — SSR'd pages, and everything served by the Node/Docker deploy, get * their headers here. Keep the two lists in sync. */ -export const handle: Handle = async ({ event, resolve }) => { +const securityHeadersHandle: Handle = async ({ event, resolve }) => { const response = await resolve(event); const headers: Record = { @@ -35,6 +54,8 @@ export const handle: Handle = async ({ event, resolve }) => { return response; }; +export const handle = sequence(i18nHandle, securityHeadersHandle); + /** * Runs once when the SvelteKit server boots (Node adapter), before any request * is handled. diff --git a/src/lib/i18n/open-graph.ts b/src/lib/i18n/open-graph.ts new file mode 100644 index 00000000..6d467757 --- /dev/null +++ b/src/lib/i18n/open-graph.ts @@ -0,0 +1,12 @@ +import { getLocale, type Locale } from '#lib/paraglide/runtime.js'; + +const OG_LOCALES: Record = { + en: 'en_US', + de: 'de_DE', + nb: 'nb_NO', +}; + +/** The current locale in the `language_TERRITORY` form `og:locale` expects. */ +export function openGraphLocale(): string { + return OG_LOCALES[getLocale()]; +} diff --git a/src/lib/i18n/query-locale.ts b/src/lib/i18n/query-locale.ts new file mode 100644 index 00000000..9c8f1c55 --- /dev/null +++ b/src/lib/i18n/query-locale.ts @@ -0,0 +1,54 @@ +import { + defineCustomClientStrategy, + defineCustomServerStrategy, + isLocale, + overwriteSetLocale, + setLocale, + type Locale, +} from '#lib/paraglide/runtime.js'; + +/** + * One-off locale override, e.g. `?lang=de`. + * It applies while the parameter is in the URL and is never saved to the locale cookie. + */ +export const LOCALE_QUERY_PARAM = 'lang'; + +function localeFromUrl(url: URL): Locale | undefined { + const value = url.searchParams.get(LOCALE_QUERY_PARAM); + return value !== null && isLocale(value) ? value : undefined; +} + +defineCustomServerStrategy('custom-query', { + getLocale: (request) => (request ? localeFromUrl(new URL(request.url)) : undefined), +}); + +// Read once per document, so client-side navigation away from the `?lang=` URL keeps the +// whole page in one language until the next full load. +const override = + typeof window === 'undefined' ? undefined : localeFromUrl(new URL(window.location.href)); + +defineCustomClientStrategy('custom-query', { + getLocale: () => override, + // Persisting is the cookie strategy's job; this one only ever reads the URL. + setLocale: () => {}, +}); + +if (override) { + const originalSetLocale = setLocale; + + overwriteSetLocale((locale, options) => { + // Paraglide calls `setLocale(resolved, { reload: false })` on the first `getLocale()` to + // persist the detected locale. Skip it for the override so the cookie keeps the user's choice. + if (options?.reload === false) { + if (locale === override) return; + return originalSetLocale(locale, options); + } + + // An explicit pick (the language switcher) is saved, then the override is dropped from the + // URL; otherwise the reload would resolve to the override again. + originalSetLocale(locale, { reload: false }); + const url = new URL(window.location.href); + url.searchParams.delete(LOCALE_QUERY_PARAM); + window.location.href = url.href; + }); +} diff --git a/src/routes/(auth)/+layout.svelte b/src/routes/(auth)/+layout.svelte index 2b52c7a2..fa860634 100644 --- a/src/routes/(auth)/+layout.svelte +++ b/src/routes/(auth)/+layout.svelte @@ -5,12 +5,13 @@ import { gotoQueryRedirectOrFallback, sanitizeRedirectSearchParam } from '#lib/utils/url.js'; import { onMount, type Snippet } from 'svelte'; import { toast } from 'svelte-sonner'; + import { m } from '#lib/paraglide/messages.js'; let { children }: { children?: Snippet } = $props(); onMount(() => { if (sanitizeRedirectSearchParam()) { - toast.warning('An invalid redirect URL was removed for your safety.'); + toast.warning(m.auth_layout_invalid_redirect_removed()); } }); @@ -23,7 +24,7 @@ - OpenShock Logo + {m.auth_layout_logo_alt()}
{@render children?.()} diff --git a/src/routes/(auth)/activate/+page.svelte b/src/routes/(auth)/activate/+page.svelte index 03ee67ba..9428fd9f 100644 --- a/src/routes/(auth)/activate/+page.svelte +++ b/src/routes/(auth)/activate/+page.svelte @@ -9,8 +9,9 @@ import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; + import { m } from '#lib/paraglide/messages.js'; - registerBreadcrumbs(() => [{ label: 'Activate Account' }]); + registerBreadcrumbs(() => [{ label: m.auth_activate_breadcrumb() }]); let token = browser ? page.url.searchParams.get('token') : null; @@ -23,7 +24,7 @@ accountActivate({ query: { token } }) .then(() => { status = 'success'; - toast.success('Account activated'); + toast.success(m.auth_activate_toast_success()); }) .catch(async (e: unknown) => { status = 'failed'; @@ -34,22 +35,23 @@ -

Activate Account

+

{m.auth_activate_title()}

{#if status === 'activating'} - Activating your account... + {m.auth_activate_activating()} {:else if status === 'success'} - Your account has been activated + {m.auth_activate_success()} {:else if status === 'failed'} - This activation link is invalid or has expired + {m.auth_activate_failed()} {:else} - Missing activation token + {m.auth_activate_missing_token()} {/if}
{#if status === 'success'} {/if}
diff --git a/src/routes/(auth)/forgot-password/+page.svelte b/src/routes/(auth)/forgot-password/+page.svelte index 868023eb..f4678241 100644 --- a/src/routes/(auth)/forgot-password/+page.svelte +++ b/src/routes/(auth)/forgot-password/+page.svelte @@ -11,8 +11,10 @@ import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { backendMetadata } from '#lib/state/backend-metadata-state.svelte.js'; import { toast } from 'svelte-sonner'; + import { m } from '#lib/paraglide/messages.js'; + import { ParaglideMessage } from '@inlang/paraglide-js-svelte'; - registerBreadcrumbs(() => [{ label: 'Forgot Password' }]); + registerBreadcrumbs(() => [{ label: m.auth_forgot_breadcrumb() }]); // Without a mail provider the reset link can never be delivered, so the flow is unavailable. let mailEnabled = $derived(backendMetadata.state?.isMailEnabled ?? true); @@ -32,7 +34,7 @@ accountPasswordResetInitiateV2({ body: { email, turnstileResponse } }) .then(() => { - toast.success('Reset password request has been sent to the given email'); + toast.success(m.auth_forgot_toast_sent()); goto(resolve('login')); }) .catch((error) => { @@ -46,15 +48,15 @@ -

Forgot Password

- Enter your email to reset your password +

{m.auth_forgot_title()}

+ {m.auth_forgot_description()}
{#if mailEnabled}
- + - Remember your password? - Sign in + + {#snippet signin({ children })} + {@render children?.()} + {/snippet} + {:else}

- Password resets are unavailable on this instance because it has no mail provider - configured. Please contact your administrator for help recovering your account. + {m.auth_forgot_mail_disabled()}

- Remember your password? - Sign in + + {#snippet signin({ children })} + {@render children?.()} + {/snippet} +
diff --git a/src/routes/(auth)/login/+page.svelte b/src/routes/(auth)/login/+page.svelte index 51661fa4..a1570214 100644 --- a/src/routes/(auth)/login/+page.svelte +++ b/src/routes/(auth)/login/+page.svelte @@ -23,8 +23,10 @@ import { getOAuthErrorMessage } from '#lib/auth/oauth-errors.js'; import { MailCheck, KeyRound } from '@lucide/svelte'; import ResendActivationButton from '#lib/components/auth/resend-activation-button.svelte'; + import { m } from '#lib/paraglide/messages.js'; + import { ParaglideMessage } from '@inlang/paraglide-js-svelte'; - registerBreadcrumbs(() => [{ label: 'Login' }]); + registerBreadcrumbs(() => [{ label: m.auth_login_breadcrumb() }]); let oauthError = $state(); @@ -125,14 +127,14 @@ -

Welcome back

+

{m.auth_login_title()}

{#if backendMetadata.state === null} - Loading available login methods + {m.auth_login_loading_methods()} {:else if anyOAuthProviders} - Login with one of these methods + {m.auth_login_choose_method()} {:else} - Login with your OpenShock Account + {m.auth_login_with_account()} {/if}
@@ -145,16 +147,9 @@
{#if activationFromSignup} -

- Your account has been created. We've sent an activation link to your email address. - Check your inbox and spam folder. -

+

{m.auth_login_activation_after_signup()}

{:else} -

- Your account isn't activated yet. We've sent an activation link to your email address. - Check your inbox and spam folder. If nothing arrives, try logging in again later to - get a new link. -

+

{m.auth_login_activation_pending()}

{/if}
@@ -167,8 +162,7 @@ >

- This account has no password. Sign in with the provider you signed up with - {#if anyOAuthProviders}using the buttons above{/if}. + {anyOAuthProviders ? m.auth_login_oauth_only_with_buttons() : m.auth_login_oauth_only()}

{/if} @@ -189,21 +183,21 @@ {#if anyOAuthProviders} Or continue with{m.auth_login_or_continue_with()} {/if}
- + - Don't have an account? - Sign up + + {#snippet signup({ children })} + {@render children?.()} + {/snippet} +
@@ -230,8 +227,14 @@ - By clicking Login, you agree to our - Terms of Service - and - Privacy Policy. + + {#snippet tos({ children })} + {@render children?.()} + {/snippet} + {#snippet privacy({ children })} + {@render children?.()} + {/snippet} + diff --git a/src/routes/(auth)/oauth/[provider]/create/+page.svelte b/src/routes/(auth)/oauth/[provider]/create/+page.svelte index 7705e318..4786a88f 100644 --- a/src/routes/(auth)/oauth/[provider]/create/+page.svelte +++ b/src/routes/(auth)/oauth/[provider]/create/+page.svelte @@ -12,8 +12,12 @@ import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { userState } from '#lib/state/user-state.svelte.js'; import { onMount } from 'svelte'; + import { m } from '#lib/paraglide/messages.js'; + import { ParaglideMessage } from '@inlang/paraglide-js-svelte'; - registerBreadcrumbs(() => [{ label: `Sign Up With ${page.params.provider ?? 'OAuth'}` }]); + registerBreadcrumbs(() => [ + { label: m.auth_oauth_create_breadcrumb({ provider: page.params.provider ?? 'OAuth' }) }, + ]); let username = $state(''); let usernameValid = $state(false); @@ -82,25 +86,47 @@ -

Sign Up With {page.params.provider}

+

+ + {#snippet provider({ children })} + {@render children?.()} + {/snippet} + +

- Complete your account details + {m.auth_oauth_create_description()}
- + - +
- By clicking Sign Up, you agree to our Terms of Service - and Privacy Policy. + + {#snippet tos({ children })} + {@render children?.()} + {/snippet} + {#snippet privacy({ children })} + {@render children?.()} + {/snippet} + diff --git a/src/routes/(auth)/oauth/error/+page.svelte b/src/routes/(auth)/oauth/error/+page.svelte index 7258ebf4..a23940a0 100644 --- a/src/routes/(auth)/oauth/error/+page.svelte +++ b/src/routes/(auth)/oauth/error/+page.svelte @@ -6,8 +6,9 @@ import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { getOAuthErrorMessage } from '#lib/auth/oauth-errors.js'; import { consumeSearchParam } from '#lib/utils/url.js'; + import { m } from '#lib/paraglide/messages.js'; - registerBreadcrumbs(() => [{ label: 'Authentication Error' }]); + registerBreadcrumbs(() => [{ label: m.auth_oauth_error_breadcrumb() }]); let errorCode = $state(); @@ -23,12 +24,12 @@ {#if errorCode} -

Authentication Error

+

{m.auth_oauth_error_title()}

Back to login{m.auth_oauth_error_back()}
diff --git a/src/routes/(auth)/reset-password/[passwordResetId=guid]/[secret]/+page.svelte b/src/routes/(auth)/reset-password/[passwordResetId=guid]/[secret]/+page.svelte index 4c4eb76e..b5ef1b30 100644 --- a/src/routes/(auth)/reset-password/[passwordResetId=guid]/[secret]/+page.svelte +++ b/src/routes/(auth)/reset-password/[passwordResetId=guid]/[secret]/+page.svelte @@ -11,8 +11,9 @@ import { validatePasswordMatch } from '@openshock/svelte-core/inputvalidation'; import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { toast } from 'svelte-sonner'; + import { m } from '#lib/paraglide/messages.js'; - registerBreadcrumbs(() => [{ label: 'Reset Password' }]); + registerBreadcrumbs(() => [{ label: m.auth_reset_breadcrumb() }]); let passwordResetId = $derived(page.params.passwordResetId!); let secret = $derived(page.params.secret!); @@ -53,7 +54,7 @@ body: { password }, }); - toast.success('Password has been reset'); + toast.success(m.auth_reset_toast_success()); goto(resolve('login')); } catch (e) { await handleApiError(e); @@ -69,14 +70,14 @@ -

Reset Password

+

{m.auth_reset_title()}

{#if checking} - Verifying reset link... + {m.auth_reset_checking()} {:else if valid} - Choose a new password for your account + {m.auth_reset_choose_password()} {:else} - This reset link is invalid or has expired + {m.auth_reset_invalid()} {/if}
@@ -84,8 +85,8 @@ {#if valid}
- + {:else if !checking} {m.auth_reset_request_new()} {/if} diff --git a/src/routes/(auth)/signup/+page.svelte b/src/routes/(auth)/signup/+page.svelte index 4cfafd94..a33d326a 100644 --- a/src/routes/(auth)/signup/+page.svelte +++ b/src/routes/(auth)/signup/+page.svelte @@ -19,8 +19,10 @@ import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { backendMetadata } from '#lib/state/backend-metadata-state.svelte.js'; import { Skeleton } from '@openshock/svelte-core/components/ui/skeleton'; + import { m } from '#lib/paraglide/messages.js'; + import { ParaglideMessage } from '@inlang/paraglide-js-svelte'; - registerBreadcrumbs(() => [{ label: 'Sign Up' }]); + registerBreadcrumbs(() => [{ label: m.auth_signup_breadcrumb() }]); let username = $state(''); let usernameValid = $state(false); @@ -55,9 +57,7 @@ if (!open) { accountCreated = false; toast.success( - mailEnabled - ? 'Account created successfully. Please check your email to verify your account.' - : 'Account created successfully. You can log in now.' + mailEnabled ? m.auth_signup_toast_created_mail() : m.auth_signup_toast_created_no_mail() ); goto(resolve('login')); @@ -89,20 +89,20 @@ accountCreated, onOpenChange}> - Welcome! Thank you for signing up! ❤️ + {m.auth_signup_dialog_title()}
{#if mailEnabled} -

Your account has been created. 🎉 Please check your email to verify your account.

-

After verifying your email, you can log in to your account.

+

{m.auth_signup_dialog_created_mail()}

+

{m.auth_signup_dialog_verify_then_login()}

{:else} -

Your account has been created and is ready to use. 🎉

-

You can log in to your account now.

+

{m.auth_signup_dialog_created_no_mail()}

+

{m.auth_signup_dialog_login_now()}

{/if} {m.auth_signup_dialog_ok()}
@@ -112,14 +112,14 @@ -

Create your account

+

{m.auth_signup_title()}

{#if backendMetadata.state === null} - Loading available sign-up methods + {m.auth_signup_loading_methods()} {:else if useEmail} - Signing up using email + {m.auth_signup_using_email()} {:else} - Choose your preferred sign-up method + {m.auth_signup_choose_method()} {/if}
@@ -137,25 +137,26 @@ variant="ghost" size="sm" class="text-muted-foreground hover:text-foreground -mx-2 -mt-2 mb-2 w-fit gap-1" - onclick={() => (useEmail = false)}>Back (useEmail = false)} + >{m.auth_signup_back()} {/if} - + - Already have an account? - Sign in + + {#snippet signin({ children })} + {@render children?.()} + {/snippet} + {:else} {#if anyOAuthProviders} - Or + {m.auth_signup_or()} {/if} {m.auth_signup_with_email()} {/if}
- By clicking Create Account, you agree to our Terms of Service - and Privacy Policy. + + {#snippet tos({ children })} + {@render children?.()} + {/snippet} + {#snippet privacy({ children })} + {@render children?.()} + {/snippet} + diff --git a/src/routes/(auth)/verify-email/+page.svelte b/src/routes/(auth)/verify-email/+page.svelte index 485bc5c2..471aac71 100644 --- a/src/routes/(auth)/verify-email/+page.svelte +++ b/src/routes/(auth)/verify-email/+page.svelte @@ -9,8 +9,9 @@ import { registerBreadcrumbs } from '#lib/state/breadcrumbs-state.svelte.js'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; + import { m } from '#lib/paraglide/messages.js'; - registerBreadcrumbs(() => [{ label: 'Verify Email' }]); + registerBreadcrumbs(() => [{ label: m.auth_verify_breadcrumb() }]); let token = browser ? page.url.searchParams.get('token') : null; @@ -23,7 +24,7 @@ accountEmailVerify({ query: { token } }) .then(() => { status = 'success'; - toast.success('Email verified'); + toast.success(m.auth_verify_toast_success()); }) .catch(async (e: unknown) => { status = 'failed'; @@ -34,22 +35,23 @@ -

Verify Email

+

{m.auth_verify_title()}

{#if status === 'verifying'} - Verifying your email address... + {m.auth_verify_verifying()} {:else if status === 'success'} - Your email has been verified + {m.auth_verify_success()} {:else if status === 'failed'} - This verification link is invalid or has expired + {m.auth_verify_failed()} {:else} - Missing verification token + {m.auth_verify_missing_token()} {/if}
{#if status === 'success'} {/if}
diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index 3aea3724..ff785cea 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -10,6 +10,8 @@ } from '@openshock/svelte-core/components/metadata'; import ArrowLeft from '@lucide/svelte/icons/arrow-left'; import { isValidRedirectURL } from '#lib/utils/url.js'; + import { openGraphLocale } from '#lib/i18n/open-graph.js'; + import { m } from '#lib/paraglide/messages.js'; let previousPath = $state(resolve('/')); @@ -22,9 +24,9 @@ }); const meta = { - title: 'Service Unavailable', - description: 'OpenShock is currently unavailable', - image: { src: asset('logo.svg'), alt: 'OpenShock Logo' }, + title: m.error_meta_title(), + description: m.error_meta_description(), + image: { src: asset('logo.svg'), alt: m.error_meta_logo_alt() }, }; @@ -35,7 +37,7 @@ url={page.url.origin} siteName={PUBLIC_SITE_NAME} determiner="auto" - metaLocale="en_US" + metaLocale={openGraphLocale()} /> @@ -46,20 +48,19 @@

- {page.error?.message ?? 'Something went wrong.'} + {page.error?.message ?? m.error_fallback_message()}

- {page.status === 404 - ? "The page you're looking for doesn't exist." - : 'An unexpected error occurred.'} + {page.status === 404 ? m.error_not_found_description() : m.error_unexpected_description()}

- Go back + + {m.error_go_back()} diff --git a/src/routes/Footer.svelte b/src/routes/Footer.svelte index 3912faaa..e1d9605e 100644 --- a/src/routes/Footer.svelte +++ b/src/routes/Footer.svelte @@ -7,6 +7,9 @@ import * as DropdownMenu from '@openshock/svelte-core/components/ui/dropdown-menu'; import { Button } from '@openshock/svelte-core/components/ui/button'; import { backendMetadata } from '#lib/state/backend-metadata-state.svelte.js'; + import { m } from '#lib/paraglide/messages.js'; + import { ParaglideMessage } from '@inlang/paraglide-js-svelte'; + import LanguageSwitcher from './LanguageSwitcher.svelte'; function shortCommit(commit: string): string { return commit.slice(0, 7); @@ -26,13 +29,19 @@ class="text-muted-foreground text-sm-foreground bottom-0 flex flex-none items-center justify-between px-2" >
- Made with - ♥ - by the - - OpenShock Team + + {#snippet heart()} + ♥ + {/snippet} + {#snippet team({ children })} + + {@render children?.()} + {/snippet} +
+ {#snippet child({ props })} @@ -41,7 +50,7 @@ variant="ghost" size="icon" class="h-4 w-4" - aria-label="Server connection status" + aria-label={m.footer_connection_status_label()} > {#if getConnectionState() === HubConnectionState.Connected} @@ -55,7 +64,7 @@ - + - - + - - +
SignalR State{m.footer_signalr_state()}

{#if getConnectionState() === HubConnectionState.Connected} @@ -69,8 +78,8 @@ >

Frontend Version + {m.footer_frontend_version()} {shortCommit(frontendCommit)} {#if backendMetadata.state}
Backend Version + {m.footer_backend_version()} {backendMetadata.state.version} @@ -30,7 +31,12 @@
- @@ -55,28 +61,28 @@ {userState.self.name}'s avatar - {@render dropdownItem('Profile', 'profile')} - {@render dropdownItem('Settings', 'settings/account')} - {@render dropdownItem('Logout', 'logout')} + {@render dropdownItem(m.header_menu_profile(), 'profile')} + {@render dropdownItem(m.header_menu_settings(), 'settings/account')} + {@render dropdownItem(m.header_menu_logout(), 'logout')} {:else} - - + + diff --git a/src/routes/Sidebar.svelte b/src/routes/Sidebar.svelte index 4532a699..0a14f1c6 100644 --- a/src/routes/Sidebar.svelte +++ b/src/routes/Sidebar.svelte @@ -176,7 +176,7 @@ ...(PUBLIC_HANGFIRE_URL ? [ { - title: m.sidebar_admin_hangfire(), + title: 'Hangfire', Icon: Timer, externalHref: PUBLIC_HANGFIRE_URL, target: '_blank', From a32dee297e0113ece04a3dd564d5ef0ea2f6176a Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:21:33 +0100 Subject: [PATCH 04/38] correct some norwegian --- messages/nb.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/messages/nb.json b/messages/nb.json index d6d188d3..1f69d97f 100644 --- a/messages/nb.json +++ b/messages/nb.json @@ -7,9 +7,9 @@ "header_menu_settings": "Innstillinger", "header_menu_logout": "Logg ut", "header_login": "Logg inn", - "header_signup": "Registrer deg", - "header_github_title": "Prosjektet på GitHub", - "header_discord_title": "Discord-fellesskapet", + "header_signup": "Registrer konto", + "header_github_title": "OpenShock på GitHub", + "header_discord_title": "Discord-samfunnet", "footer_made_with": "Laget med {#heart/} av {#team}OpenShock-teamet{/team}", "footer_connection_status_label": "Tilkoblingsstatus til serveren", @@ -30,18 +30,18 @@ "sidebar_public_shares": "Offentlige delinger", "sidebar_serial_terminal": "Seriell terminal", "sidebar_group_admin": "Admin", - "sidebar_admin_monitoring": "Overvåking", + "sidebar_admin_monitoring": "Monitorering", "sidebar_admin_online_hubs": "Tilkoblede huber", "sidebar_admin_online_gateways": "Tilkoblede gatewayer", "sidebar_admin_management": "Administrasjon", "sidebar_admin_users": "Brukere", - "sidebar_admin_blacklists": "Svartelister", + "sidebar_admin_blacklists": "Blokkerings Lister", "sidebar_admin_webhooks": "Webhooks", "sidebar_admin_mail": "E-post", - "sidebar_admin_automation_tokens": "Automatiseringstokener", + "sidebar_admin_automation_tokens": "Automasjons Tokens", "sidebar_admin_configuration": "Konfigurasjon", "sidebar_group_settings": "Innstillinger", - "sidebar_settings_api_tokens": "API-tokener", + "sidebar_settings_api_tokens": "API-tokens", "sidebar_settings_sessions": "Økter", "sidebar_settings_audit_log": "Aktivitetslogg", "sidebar_settings_connections": "Tilkoblinger", @@ -56,7 +56,7 @@ "error_go_back": "Gå tilbake", "auth_layout_logo_alt": "OpenShock-logo", - "auth_layout_invalid_redirect_removed": "En ugyldig videresendings-URL ble fjernet for din sikkerhet.", + "auth_layout_invalid_redirect_removed": "En ugyldig viderekoblings-URL ble fjernet for din sikkerhet.", "auth_activate_breadcrumb": "Aktiver konto", "auth_activate_title": "Aktiver konto", @@ -82,8 +82,8 @@ "auth_login_loading_methods": "Laster inn tilgjengelige innloggingsmetoder", "auth_login_choose_method": "Logg inn med en av disse metodene", "auth_login_with_account": "Logg inn med OpenShock-kontoen din", - "auth_login_activation_after_signup": "Kontoen din er opprettet. Vi har sendt en aktiveringslenke til e-postadressen din. Sjekk innboksen og søppelpostmappen.", - "auth_login_activation_pending": "Kontoen din er ikke aktivert ennå. Vi har sendt en aktiveringslenke til e-postadressen din. Sjekk innboksen og søppelpostmappen. Hvis ingenting kommer, kan du prøve å logge inn igjen senere for å få en ny lenke.", + "auth_login_activation_after_signup": "Kontoen din er opprettet. Vi har sendt en aktiveringslenke til e-postadressen din. Sjekk innboksen og søppel-mappen.", + "auth_login_activation_pending": "Kontoen din er ikke aktivert ennå. Vi har sendt en aktiveringslenke til e-postadressen din. Sjekk innboksen og søppel-mappen. Hvis ingenting kommer, kan du prøve å logge inn igjen senere for å få en ny lenke.", "auth_login_oauth_only": "Denne kontoen har ikke noe passord. Logg inn med leverandøren du registrerte deg med.", "auth_login_oauth_only_with_buttons": "Denne kontoen har ikke noe passord. Logg inn med leverandøren du registrerte deg med ved å bruke knappene over.", "auth_login_or_continue_with": "Eller fortsett med", @@ -123,7 +123,7 @@ "auth_signup_breadcrumb": "Registrer deg", "auth_signup_toast_created_mail": "Kontoen er opprettet. Sjekk e-posten din for å bekrefte kontoen.", "auth_signup_toast_created_no_mail": "Kontoen er opprettet. Du kan logge inn nå.", - "auth_signup_dialog_title": "Velkommen! Takk for at du registrerte deg! ❤️", + "auth_signup_dialog_title": "Velkommen! Takk for at du valgte å registrere deg med oss! ❤️", "auth_signup_dialog_created_mail": "Kontoen din er opprettet. 🎉 Sjekk e-posten din for å bekrefte kontoen.", "auth_signup_dialog_verify_then_login": "Når du har bekreftet e-postadressen, kan du logge inn på kontoen din.", "auth_signup_dialog_created_no_mail": "Kontoen din er opprettet og klar til bruk. 🎉", @@ -134,9 +134,9 @@ "auth_signup_choose_method": "Velg hvordan du vil registrere deg", "auth_signup_back": "Tilbake", "auth_signup_username_label": "Brukernavn", - "auth_signup_username_placeholder": "Ola OpenShock", + "auth_signup_username_placeholder": "Ole Hansen", "auth_signup_email_label": "E-post", - "auth_signup_email_placeholder": "ola@example.com", + "auth_signup_email_placeholder": "ole@eksempel.no", "auth_signup_password_label": "Passord", "auth_signup_password_placeholder": "Passord", "auth_signup_confirm_label": "Bekreft passord", From 1bf5a4767bea23c8845bb6e5e2ef70710bec9284 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:22:45 +0100 Subject: [PATCH 05/38] openshock on github is better --- messages/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/en.json b/messages/en.json index 28135d7a..ff61e32c 100644 --- a/messages/en.json +++ b/messages/en.json @@ -8,7 +8,7 @@ "header_menu_logout": "Logout", "header_login": "Login", "header_signup": "Sign Up", - "header_github_title": "Project GitHub", + "header_github_title": "OpenShock on GitHub", "header_discord_title": "Community Discord", "footer_made_with": "Made with {#heart/} by the {#team}OpenShock Team{/team}", From 8ca747bb5d704050375f0e63da3deadd5f2d55b2 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:24:34 +0100 Subject: [PATCH 06/38] Add spanish --- messages/es.json | 158 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 messages/es.json diff --git a/messages/es.json b/messages/es.json new file mode 100644 index 00000000..5cdb4585 --- /dev/null +++ b/messages/es.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Mostrar u ocultar la barra lateral", + "header_avatar_alt": "Avatar de {name}", + "header_menu_profile": "Perfil", + "header_menu_settings": "Ajustes", + "header_menu_logout": "Cerrar sesión", + "header_login": "Iniciar sesión", + "header_signup": "Registrarse", + "header_github_title": "OpenShock en GitHub", + "header_discord_title": "Discord de la comunidad", + + "footer_made_with": "Hecho con {#heart/} por el {#team}equipo de OpenShock{/team}", + "footer_connection_status_label": "Estado de la conexión con el servidor", + "footer_signalr_state": "Estado de SignalR", + "footer_frontend_version": "Versión del frontend", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Versión del backend", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Idioma", + + "sidebar_home_link_label": "Página de inicio de OpenShock", + "sidebar_group_general": "General", + "sidebar_home": "Inicio", + "sidebar_shockers": "Shockers", + "sidebar_shared_shockers": "Shockers compartidos", + "sidebar_hubs": "Hubs", + "sidebar_user_shares": "Comparticiones con usuarios", + "sidebar_public_shares": "Comparticiones públicas", + "sidebar_serial_terminal": "Terminal serie", + "sidebar_group_admin": "Administración", + "sidebar_admin_monitoring": "Monitorización", + "sidebar_admin_online_hubs": "Hubs en línea", + "sidebar_admin_online_gateways": "Gateways en línea", + "sidebar_admin_management": "Gestión", + "sidebar_admin_users": "Usuarios", + "sidebar_admin_blacklists": "Listas de bloqueo", + "sidebar_admin_webhooks": "Webhooks", + "sidebar_admin_mail": "Correo", + "sidebar_admin_automation_tokens": "Tokens de automatización", + "sidebar_admin_configuration": "Configuración", + "sidebar_group_settings": "Ajustes", + "sidebar_settings_api_tokens": "Tokens de API", + "sidebar_settings_sessions": "Sesiones", + "sidebar_settings_audit_log": "Registro de actividad", + "sidebar_settings_connections": "Conexiones", + "sidebar_settings_account": "Ajustes de la cuenta", + + "error_meta_title": "Servicio no disponible", + "error_meta_description": "OpenShock no está disponible en este momento", + "error_meta_logo_alt": "Logotipo de OpenShock", + "error_fallback_message": "Algo ha salido mal.", + "error_not_found_description": "La página que buscas no existe.", + "error_unexpected_description": "Se ha producido un error inesperado.", + "error_go_back": "Volver", + + "auth_layout_logo_alt": "Logotipo de OpenShock", + "auth_layout_invalid_redirect_removed": "Se ha eliminado una URL de redirección no válida por tu seguridad.", + + "auth_activate_breadcrumb": "Activar cuenta", + "auth_activate_title": "Activar cuenta", + "auth_activate_activating": "Activando tu cuenta...", + "auth_activate_success": "Tu cuenta se ha activado", + "auth_activate_failed": "Este enlace de activación no es válido o ha caducado", + "auth_activate_missing_token": "Falta el token de activación", + "auth_activate_continue": "Continuar al inicio de sesión", + "auth_activate_toast_success": "Cuenta activada", + + "auth_forgot_breadcrumb": "Contraseña olvidada", + "auth_forgot_title": "Contraseña olvidada", + "auth_forgot_description": "Introduce tu correo electrónico para restablecer tu contraseña", + "auth_forgot_email_label": "Correo electrónico", + "auth_forgot_email_placeholder": "Correo electrónico", + "auth_forgot_submit": "Restablecer contraseña", + "auth_forgot_signin_prompt": "¿Recuerdas tu contraseña? {#signin}Inicia sesión{/signin}", + "auth_forgot_mail_disabled": "No es posible restablecer contraseñas en esta instancia porque no tiene ningún proveedor de correo configurado. Ponte en contacto con tu administrador para recuperar el acceso a tu cuenta.", + "auth_forgot_toast_sent": "Se ha enviado la solicitud de restablecimiento de contraseña al correo indicado", + + "auth_login_breadcrumb": "Iniciar sesión", + "auth_login_title": "Hola de nuevo", + "auth_login_loading_methods": "Cargando los métodos de inicio de sesión disponibles", + "auth_login_choose_method": "Inicia sesión con uno de estos métodos", + "auth_login_with_account": "Inicia sesión con tu cuenta de OpenShock", + "auth_login_activation_after_signup": "Tu cuenta se ha creado. Te hemos enviado un enlace de activación a tu correo electrónico. Revisa tu bandeja de entrada y la carpeta de spam.", + "auth_login_activation_pending": "Tu cuenta aún no está activada. Te hemos enviado un enlace de activación a tu correo electrónico. Revisa tu bandeja de entrada y la carpeta de spam. Si no te llega nada, vuelve a iniciar sesión más tarde para recibir un enlace nuevo.", + "auth_login_oauth_only": "Esta cuenta no tiene contraseña. Inicia sesión con el proveedor con el que te registraste.", + "auth_login_oauth_only_with_buttons": "Esta cuenta no tiene contraseña. Inicia sesión con el proveedor con el que te registraste usando los botones de arriba.", + "auth_login_or_continue_with": "O continúa con", + "auth_login_username_label": "Nombre de usuario o correo electrónico", + "auth_login_password_label": "Contraseña", + "auth_login_submit": "Iniciar sesión", + "auth_login_signup_prompt": "¿No tienes cuenta? {#signup}Regístrate{/signup}", + "auth_login_legal": "Al hacer clic en «Iniciar sesión», aceptas nuestros {#tos}Términos del servicio{/tos} y nuestra {#privacy}Política de privacidad{/privacy}.", + + "auth_oauth_create_breadcrumb": "Registrarse con {provider}", + "auth_oauth_create_title": "Registrarse con {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Completa los datos de tu cuenta", + "auth_oauth_create_username_label": "Nombre de usuario", + "auth_oauth_create_username_placeholder": "Nombre de usuario", + "auth_oauth_create_email_label": "Correo electrónico", + "auth_oauth_create_email_placeholder": "Correo electrónico", + "auth_oauth_create_submit": "Registrarse", + "auth_oauth_create_legal": "Al hacer clic en «Registrarse», aceptas nuestros {#tos}Términos del servicio{/tos} y nuestra {#privacy}Política de privacidad{/privacy}.", + + "auth_oauth_error_breadcrumb": "Error de autenticación", + "auth_oauth_error_title": "Error de autenticación", + "auth_oauth_error_back": "Volver al inicio de sesión", + + "auth_reset_breadcrumb": "Restablecer contraseña", + "auth_reset_title": "Restablecer contraseña", + "auth_reset_checking": "Comprobando el enlace de restablecimiento...", + "auth_reset_choose_password": "Elige una contraseña nueva para tu cuenta", + "auth_reset_invalid": "Este enlace de restablecimiento no es válido o ha caducado", + "auth_reset_password_label": "Contraseña nueva", + "auth_reset_password_placeholder": "Contraseña nueva", + "auth_reset_confirm_label": "Confirmar contraseña nueva", + "auth_reset_confirm_placeholder": "Confirmar contraseña nueva", + "auth_reset_submit": "Restablecer contraseña", + "auth_reset_request_new": "Solicitar un enlace nuevo", + "auth_reset_toast_success": "Se ha restablecido la contraseña", + + "auth_signup_breadcrumb": "Registrarse", + "auth_signup_toast_created_mail": "Cuenta creada correctamente. Revisa tu correo electrónico para verificar tu cuenta.", + "auth_signup_toast_created_no_mail": "Cuenta creada correctamente. Ya puedes iniciar sesión.", + "auth_signup_dialog_title": "¡Te damos la bienvenida! ¡Gracias por registrarte! ❤️", + "auth_signup_dialog_created_mail": "Tu cuenta se ha creado. 🎉 Revisa tu correo electrónico para verificar tu cuenta.", + "auth_signup_dialog_verify_then_login": "Cuando hayas verificado tu correo electrónico, podrás iniciar sesión en tu cuenta.", + "auth_signup_dialog_created_no_mail": "Tu cuenta se ha creado y ya está lista para usarse. 🎉", + "auth_signup_dialog_login_now": "Ya puedes iniciar sesión en tu cuenta.", + "auth_signup_title": "Crea tu cuenta", + "auth_signup_loading_methods": "Cargando los métodos de registro disponibles", + "auth_signup_using_email": "Registro con correo electrónico", + "auth_signup_choose_method": "Elige cómo quieres registrarte", + "auth_signup_back": "Atrás", + "auth_signup_username_label": "Nombre de usuario", + "auth_signup_username_placeholder": "Juan OpenShock", + "auth_signup_email_label": "Correo electrónico", + "auth_signup_email_placeholder": "juan@example.com", + "auth_signup_password_label": "Contraseña", + "auth_signup_password_placeholder": "Contraseña", + "auth_signup_confirm_label": "Confirmar contraseña", + "auth_signup_confirm_placeholder": "Confirmar contraseña", + "auth_signup_submit": "Crear cuenta", + "auth_signup_signin_prompt": "¿Ya tienes cuenta? {#signin}Inicia sesión{/signin}", + "auth_signup_or": "O", + "auth_signup_with_email": "Registrarse con correo electrónico", + "auth_signup_legal": "Al hacer clic en «Crear cuenta», aceptas nuestros {#tos}Términos del servicio{/tos} y nuestra {#privacy}Política de privacidad{/privacy}.", + + "auth_verify_breadcrumb": "Verificar correo electrónico", + "auth_verify_title": "Verificar correo electrónico", + "auth_verify_verifying": "Verificando tu correo electrónico...", + "auth_verify_success": "Tu correo electrónico se ha verificado", + "auth_verify_failed": "Este enlace de verificación no es válido o ha caducado", + "auth_verify_missing_token": "Falta el token de verificación", + "auth_verify_continue": "Continuar al inicio de sesión", + "auth_verify_toast_success": "Correo electrónico verificado" +} From abce20235886d2e0020a9cddf224cddcd2542545 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:30:49 +0100 Subject: [PATCH 07/38] add more translations pending verification --- messages/et.json | 158 +++++++++++++++++++++++++++++ messages/fi.json | 158 +++++++++++++++++++++++++++++ messages/fr.json | 158 +++++++++++++++++++++++++++++ messages/sv.json | 158 +++++++++++++++++++++++++++++ messages/uwu.json | 158 +++++++++++++++++++++++++++++ project.inlang/settings.json | 2 +- src/lib/i18n/open-graph.ts | 7 ++ src/routes/LanguageSwitcher.svelte | 2 + 8 files changed, 800 insertions(+), 1 deletion(-) create mode 100644 messages/et.json create mode 100644 messages/fi.json create mode 100644 messages/fr.json create mode 100644 messages/sv.json create mode 100644 messages/uwu.json diff --git a/messages/et.json b/messages/et.json new file mode 100644 index 00000000..db368db8 --- /dev/null +++ b/messages/et.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Näita või peida külgriba", + "header_avatar_alt": "Kasutaja {name} avatar", + "header_menu_profile": "Profiil", + "header_menu_settings": "Seaded", + "header_menu_logout": "Logi välja", + "header_login": "Logi sisse", + "header_signup": "Registreeru", + "header_github_title": "OpenShock GitHubis", + "header_discord_title": "Kogukonna Discord", + + "footer_made_with": "Tehtud armastusega {#heart/} – {#team}OpenShocki meeskond{/team}", + "footer_connection_status_label": "Serveriühenduse olek", + "footer_signalr_state": "SignalR-i olek", + "footer_frontend_version": "Esiliidese versioon", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Tagarakenduse versioon", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Keel", + + "sidebar_home_link_label": "OpenShocki avaleht", + "sidebar_group_general": "Üldine", + "sidebar_home": "Avaleht", + "sidebar_shockers": "Shockerid", + "sidebar_shared_shockers": "Jagatud shockerid", + "sidebar_hubs": "Jaoturid", + "sidebar_user_shares": "Kasutajatega jagamised", + "sidebar_public_shares": "Avalikud jagamised", + "sidebar_serial_terminal": "Jadaterminal", + "sidebar_group_admin": "Haldus", + "sidebar_admin_monitoring": "Jälgimine", + "sidebar_admin_online_hubs": "Ühendatud jaoturid", + "sidebar_admin_online_gateways": "Ühendatud lüüsid", + "sidebar_admin_management": "Haldamine", + "sidebar_admin_users": "Kasutajad", + "sidebar_admin_blacklists": "Blokeerimisloendid", + "sidebar_admin_webhooks": "Veebihaagid", + "sidebar_admin_mail": "E-post", + "sidebar_admin_automation_tokens": "Automatiseerimisvõtmed", + "sidebar_admin_configuration": "Konfiguratsioon", + "sidebar_group_settings": "Seaded", + "sidebar_settings_api_tokens": "API-võtmed", + "sidebar_settings_sessions": "Seansid", + "sidebar_settings_audit_log": "Tegevuslogi", + "sidebar_settings_connections": "Seotud kontod", + "sidebar_settings_account": "Konto seaded", + + "error_meta_title": "Teenus pole saadaval", + "error_meta_description": "OpenShock pole praegu saadaval", + "error_meta_logo_alt": "OpenShocki logo", + "error_fallback_message": "Midagi läks valesti.", + "error_not_found_description": "Otsitavat lehte pole olemas.", + "error_unexpected_description": "Tekkis ootamatu viga.", + "error_go_back": "Tagasi", + + "auth_layout_logo_alt": "OpenShocki logo", + "auth_layout_invalid_redirect_removed": "Kehtetu ümbersuunamise URL eemaldati sinu turvalisuse huvides.", + + "auth_activate_breadcrumb": "Konto aktiveerimine", + "auth_activate_title": "Konto aktiveerimine", + "auth_activate_activating": "Sinu kontot aktiveeritakse...", + "auth_activate_success": "Sinu konto on aktiveeritud", + "auth_activate_failed": "See aktiveerimislink on kehtetu või aegunud", + "auth_activate_missing_token": "Aktiveerimisvõti puudub", + "auth_activate_continue": "Jätka sisselogimisega", + "auth_activate_toast_success": "Konto on aktiveeritud", + + "auth_forgot_breadcrumb": "Unustatud parool", + "auth_forgot_title": "Unustatud parool", + "auth_forgot_description": "Sisesta oma e-posti aadress, et parool lähtestada", + "auth_forgot_email_label": "E-post", + "auth_forgot_email_placeholder": "E-post", + "auth_forgot_submit": "Lähtesta parool", + "auth_forgot_signin_prompt": "Mäletad oma parooli? {#signin}Logi sisse{/signin}", + "auth_forgot_mail_disabled": "Paroolide lähtestamine pole selles instantsis saadaval, sest e-posti teenusepakkujat pole seadistatud. Konto taastamiseks võta ühendust oma administraatoriga.", + "auth_forgot_toast_sent": "Parooli lähtestamise taotlus saadeti antud e-posti aadressile", + + "auth_login_breadcrumb": "Sisselogimine", + "auth_login_title": "Tere tulemast tagasi", + "auth_login_loading_methods": "Saadaolevate sisselogimisviiside laadimine", + "auth_login_choose_method": "Logi sisse ühega neist viisidest", + "auth_login_with_account": "Logi sisse oma OpenShocki kontoga", + "auth_login_activation_after_signup": "Sinu konto on loodud. Saatsime aktiveerimislingi sinu e-posti aadressile. Kontrolli postkasti ja rämpsposti kausta.", + "auth_login_activation_pending": "Sinu konto pole veel aktiveeritud. Saatsime aktiveerimislingi sinu e-posti aadressile. Kontrolli postkasti ja rämpsposti kausta. Kui midagi ei saabu, proovi hiljem uuesti sisse logida, et saada uus link.", + "auth_login_oauth_only": "Sellel kontol pole parooli. Logi sisse teenusepakkujaga, millega registreerusid.", + "auth_login_oauth_only_with_buttons": "Sellel kontol pole parooli. Logi ülal olevate nuppude abil sisse teenusepakkujaga, millega registreerusid.", + "auth_login_or_continue_with": "Või jätka teenusega", + "auth_login_username_label": "Kasutajanimi või e-post", + "auth_login_password_label": "Parool", + "auth_login_submit": "Logi sisse", + "auth_login_signup_prompt": "Pole veel kontot? {#signup}Registreeru{/signup}", + "auth_login_legal": "Klõpsates „Logi sisse“, nõustud meie {#tos}kasutustingimustega{/tos} ja {#privacy}privaatsuspoliitikaga{/privacy}.", + + "auth_oauth_create_breadcrumb": "Registreeru teenusega {provider}", + "auth_oauth_create_title": "Registreeru teenusega {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Täida oma konto andmed", + "auth_oauth_create_username_label": "Kasutajanimi", + "auth_oauth_create_username_placeholder": "Kasutajanimi", + "auth_oauth_create_email_label": "E-post", + "auth_oauth_create_email_placeholder": "E-post", + "auth_oauth_create_submit": "Registreeru", + "auth_oauth_create_legal": "Klõpsates „Registreeru“, nõustud meie {#tos}kasutustingimustega{/tos} ja {#privacy}privaatsuspoliitikaga{/privacy}.", + + "auth_oauth_error_breadcrumb": "Autentimise viga", + "auth_oauth_error_title": "Autentimise viga", + "auth_oauth_error_back": "Tagasi sisselogimise juurde", + + "auth_reset_breadcrumb": "Parooli lähtestamine", + "auth_reset_title": "Parooli lähtestamine", + "auth_reset_checking": "Lähtestamislingi kontrollimine...", + "auth_reset_choose_password": "Vali oma kontole uus parool", + "auth_reset_invalid": "See lähtestamislink on kehtetu või aegunud", + "auth_reset_password_label": "Uus parool", + "auth_reset_password_placeholder": "Uus parool", + "auth_reset_confirm_label": "Kinnita uus parool", + "auth_reset_confirm_placeholder": "Kinnita uus parool", + "auth_reset_submit": "Lähtesta parool", + "auth_reset_request_new": "Taotle uut linki", + "auth_reset_toast_success": "Parool on lähtestatud", + + "auth_signup_breadcrumb": "Registreerumine", + "auth_signup_toast_created_mail": "Konto loodi edukalt. Konto kinnitamiseks vaata oma e-posti.", + "auth_signup_toast_created_no_mail": "Konto loodi edukalt. Võid nüüd sisse logida.", + "auth_signup_dialog_title": "Tere tulemast! Aitäh, et registreerusid! ❤️", + "auth_signup_dialog_created_mail": "Sinu konto on loodud. 🎉 Konto kinnitamiseks vaata oma e-posti.", + "auth_signup_dialog_verify_then_login": "Pärast e-posti aadressi kinnitamist saad oma kontole sisse logida.", + "auth_signup_dialog_created_no_mail": "Sinu konto on loodud ja kasutusvalmis. 🎉", + "auth_signup_dialog_login_now": "Võid nüüd oma kontole sisse logida.", + "auth_signup_title": "Loo konto", + "auth_signup_loading_methods": "Saadaolevate registreerumisviiside laadimine", + "auth_signup_using_email": "Registreerumine e-postiga", + "auth_signup_choose_method": "Vali, kuidas soovid registreeruda", + "auth_signup_back": "Tagasi", + "auth_signup_username_label": "Kasutajanimi", + "auth_signup_username_placeholder": "Jaan OpenShock", + "auth_signup_email_label": "E-post", + "auth_signup_email_placeholder": "jaan@example.com", + "auth_signup_password_label": "Parool", + "auth_signup_password_placeholder": "Parool", + "auth_signup_confirm_label": "Kinnita parool", + "auth_signup_confirm_placeholder": "Kinnita parool", + "auth_signup_submit": "Loo konto", + "auth_signup_signin_prompt": "Sul on juba konto? {#signin}Logi sisse{/signin}", + "auth_signup_or": "Või", + "auth_signup_with_email": "Registreeru e-postiga", + "auth_signup_legal": "Klõpsates „Loo konto“, nõustud meie {#tos}kasutustingimustega{/tos} ja {#privacy}privaatsuspoliitikaga{/privacy}.", + + "auth_verify_breadcrumb": "E-posti kinnitamine", + "auth_verify_title": "E-posti kinnitamine", + "auth_verify_verifying": "Sinu e-posti aadressi kinnitamine...", + "auth_verify_success": "Sinu e-posti aadress on kinnitatud", + "auth_verify_failed": "See kinnituslink on kehtetu või aegunud", + "auth_verify_missing_token": "Kinnitusvõti puudub", + "auth_verify_continue": "Jätka sisselogimisega", + "auth_verify_toast_success": "E-posti aadress on kinnitatud" +} diff --git a/messages/fi.json b/messages/fi.json new file mode 100644 index 00000000..7cddd007 --- /dev/null +++ b/messages/fi.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Näytä tai piilota sivupalkki", + "header_avatar_alt": "Käyttäjän {name} avatar", + "header_menu_profile": "Profiili", + "header_menu_settings": "Asetukset", + "header_menu_logout": "Kirjaudu ulos", + "header_login": "Kirjaudu sisään", + "header_signup": "Rekisteröidy", + "header_github_title": "OpenShock GitHubissa", + "header_discord_title": "Yhteisön Discord", + + "footer_made_with": "Tehty rakkaudella {#heart/} – {#team}OpenShock-tiimi{/team}", + "footer_connection_status_label": "Palvelinyhteyden tila", + "footer_signalr_state": "SignalR-tila", + "footer_frontend_version": "Frontendin versio", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Backendin versio", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Kieli", + + "sidebar_home_link_label": "OpenShockin etusivu", + "sidebar_group_general": "Yleiset", + "sidebar_home": "Etusivu", + "sidebar_shockers": "Shockerit", + "sidebar_shared_shockers": "Jaetut shockerit", + "sidebar_hubs": "Hubit", + "sidebar_user_shares": "Käyttäjäjaot", + "sidebar_public_shares": "Julkiset jaot", + "sidebar_serial_terminal": "Sarjapääte", + "sidebar_group_admin": "Ylläpito", + "sidebar_admin_monitoring": "Valvonta", + "sidebar_admin_online_hubs": "Yhdistetyt hubit", + "sidebar_admin_online_gateways": "Yhdistetyt yhdyskäytävät", + "sidebar_admin_management": "Hallinta", + "sidebar_admin_users": "Käyttäjät", + "sidebar_admin_blacklists": "Estolistat", + "sidebar_admin_webhooks": "Webhookit", + "sidebar_admin_mail": "Sähköposti", + "sidebar_admin_automation_tokens": "Automaatiotunnukset", + "sidebar_admin_configuration": "Määritykset", + "sidebar_group_settings": "Asetukset", + "sidebar_settings_api_tokens": "API-tunnukset", + "sidebar_settings_sessions": "Istunnot", + "sidebar_settings_audit_log": "Tapahtumaloki", + "sidebar_settings_connections": "Yhdistetyt tilit", + "sidebar_settings_account": "Tilin asetukset", + + "error_meta_title": "Palvelu ei ole käytettävissä", + "error_meta_description": "OpenShock ei ole tällä hetkellä käytettävissä", + "error_meta_logo_alt": "OpenShockin logo", + "error_fallback_message": "Jokin meni pieleen.", + "error_not_found_description": "Etsimääsi sivua ei ole olemassa.", + "error_unexpected_description": "Tapahtui odottamaton virhe.", + "error_go_back": "Takaisin", + + "auth_layout_logo_alt": "OpenShockin logo", + "auth_layout_invalid_redirect_removed": "Virheellinen uudelleenohjausosoite poistettiin turvallisuutesi vuoksi.", + + "auth_activate_breadcrumb": "Aktivoi tili", + "auth_activate_title": "Aktivoi tili", + "auth_activate_activating": "Tiliäsi aktivoidaan...", + "auth_activate_success": "Tilisi on aktivoitu", + "auth_activate_failed": "Tämä aktivointilinkki on virheellinen tai vanhentunut", + "auth_activate_missing_token": "Aktivointitunnus puuttuu", + "auth_activate_continue": "Jatka kirjautumiseen", + "auth_activate_toast_success": "Tili aktivoitu", + + "auth_forgot_breadcrumb": "Unohtunut salasana", + "auth_forgot_title": "Unohtunut salasana", + "auth_forgot_description": "Anna sähköpostiosoitteesi salasanan palauttamista varten", + "auth_forgot_email_label": "Sähköposti", + "auth_forgot_email_placeholder": "Sähköposti", + "auth_forgot_submit": "Palauta salasana", + "auth_forgot_signin_prompt": "Muistatko salasanasi? {#signin}Kirjaudu sisään{/signin}", + "auth_forgot_mail_disabled": "Salasanan palautus ei ole käytettävissä tässä instanssissa, koska sähköpostipalvelua ei ole määritetty. Ota yhteyttä ylläpitäjään saadaksesi apua tilisi palauttamiseen.", + "auth_forgot_toast_sent": "Salasanan palautuspyyntö on lähetetty annettuun sähköpostiosoitteeseen", + + "auth_login_breadcrumb": "Kirjaudu sisään", + "auth_login_title": "Tervetuloa takaisin", + "auth_login_loading_methods": "Ladataan käytettävissä olevia kirjautumistapoja", + "auth_login_choose_method": "Kirjaudu jollakin näistä tavoista", + "auth_login_with_account": "Kirjaudu OpenShock-tililläsi", + "auth_login_activation_after_signup": "Tilisi on luotu. Lähetimme aktivointilinkin sähköpostiosoitteeseesi. Tarkista saapuneet viestit ja roskapostikansio.", + "auth_login_activation_pending": "Tiliäsi ei ole vielä aktivoitu. Lähetimme aktivointilinkin sähköpostiosoitteeseesi. Tarkista saapuneet viestit ja roskapostikansio. Jos mitään ei tule, yritä kirjautua myöhemmin uudelleen saadaksesi uuden linkin.", + "auth_login_oauth_only": "Tällä tilillä ei ole salasanaa. Kirjaudu sisään palvelulla, jolla rekisteröidyit.", + "auth_login_oauth_only_with_buttons": "Tällä tilillä ei ole salasanaa. Kirjaudu sisään yllä olevilla painikkeilla palvelulla, jolla rekisteröidyit.", + "auth_login_or_continue_with": "Tai jatka käyttäen", + "auth_login_username_label": "Käyttäjänimi tai sähköposti", + "auth_login_password_label": "Salasana", + "auth_login_submit": "Kirjaudu sisään", + "auth_login_signup_prompt": "Eikö sinulla ole tiliä? {#signup}Rekisteröidy{/signup}", + "auth_login_legal": "Napsauttamalla ”Kirjaudu sisään” hyväksyt {#tos}käyttöehtomme{/tos} ja {#privacy}tietosuojakäytäntömme{/privacy}.", + + "auth_oauth_create_breadcrumb": "Rekisteröidy palvelulla {provider}", + "auth_oauth_create_title": "Rekisteröidy palvelulla {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Täydennä tilisi tiedot", + "auth_oauth_create_username_label": "Käyttäjänimi", + "auth_oauth_create_username_placeholder": "Käyttäjänimi", + "auth_oauth_create_email_label": "Sähköposti", + "auth_oauth_create_email_placeholder": "Sähköposti", + "auth_oauth_create_submit": "Rekisteröidy", + "auth_oauth_create_legal": "Napsauttamalla ”Rekisteröidy” hyväksyt {#tos}käyttöehtomme{/tos} ja {#privacy}tietosuojakäytäntömme{/privacy}.", + + "auth_oauth_error_breadcrumb": "Tunnistautumisvirhe", + "auth_oauth_error_title": "Tunnistautumisvirhe", + "auth_oauth_error_back": "Takaisin kirjautumiseen", + + "auth_reset_breadcrumb": "Palauta salasana", + "auth_reset_title": "Palauta salasana", + "auth_reset_checking": "Tarkistetaan palautuslinkkiä...", + "auth_reset_choose_password": "Valitse tilillesi uusi salasana", + "auth_reset_invalid": "Tämä palautuslinkki on virheellinen tai vanhentunut", + "auth_reset_password_label": "Uusi salasana", + "auth_reset_password_placeholder": "Uusi salasana", + "auth_reset_confirm_label": "Vahvista uusi salasana", + "auth_reset_confirm_placeholder": "Vahvista uusi salasana", + "auth_reset_submit": "Palauta salasana", + "auth_reset_request_new": "Pyydä uusi palautuslinkki", + "auth_reset_toast_success": "Salasana on palautettu", + + "auth_signup_breadcrumb": "Rekisteröidy", + "auth_signup_toast_created_mail": "Tili luotiin onnistuneesti. Vahvista tilisi sähköpostiisi lähetetyllä linkillä.", + "auth_signup_toast_created_no_mail": "Tili luotiin onnistuneesti. Voit nyt kirjautua sisään.", + "auth_signup_dialog_title": "Tervetuloa! Kiitos rekisteröitymisestä! ❤️", + "auth_signup_dialog_created_mail": "Tilisi on luotu. 🎉 Vahvista tilisi sähköpostiisi lähetetyllä linkillä.", + "auth_signup_dialog_verify_then_login": "Kun olet vahvistanut sähköpostiosoitteesi, voit kirjautua tilillesi.", + "auth_signup_dialog_created_no_mail": "Tilisi on luotu ja valmis käytettäväksi. 🎉", + "auth_signup_dialog_login_now": "Voit nyt kirjautua tilillesi.", + "auth_signup_title": "Luo tili", + "auth_signup_loading_methods": "Ladataan käytettävissä olevia rekisteröitymistapoja", + "auth_signup_using_email": "Rekisteröityminen sähköpostilla", + "auth_signup_choose_method": "Valitse, miten haluat rekisteröityä", + "auth_signup_back": "Takaisin", + "auth_signup_username_label": "Käyttäjänimi", + "auth_signup_username_placeholder": "Matti OpenShock", + "auth_signup_email_label": "Sähköposti", + "auth_signup_email_placeholder": "matti@example.com", + "auth_signup_password_label": "Salasana", + "auth_signup_password_placeholder": "Salasana", + "auth_signup_confirm_label": "Vahvista salasana", + "auth_signup_confirm_placeholder": "Vahvista salasana", + "auth_signup_submit": "Luo tili", + "auth_signup_signin_prompt": "Onko sinulla jo tili? {#signin}Kirjaudu sisään{/signin}", + "auth_signup_or": "Tai", + "auth_signup_with_email": "Rekisteröidy sähköpostilla", + "auth_signup_legal": "Napsauttamalla ”Luo tili” hyväksyt {#tos}käyttöehtomme{/tos} ja {#privacy}tietosuojakäytäntömme{/privacy}.", + + "auth_verify_breadcrumb": "Vahvista sähköposti", + "auth_verify_title": "Vahvista sähköposti", + "auth_verify_verifying": "Vahvistetaan sähköpostiosoitettasi...", + "auth_verify_success": "Sähköpostiosoitteesi on vahvistettu", + "auth_verify_failed": "Tämä vahvistuslinkki on virheellinen tai vanhentunut", + "auth_verify_missing_token": "Vahvistustunnus puuttuu", + "auth_verify_continue": "Jatka kirjautumiseen", + "auth_verify_toast_success": "Sähköpostiosoite vahvistettu" +} diff --git a/messages/fr.json b/messages/fr.json new file mode 100644 index 00000000..ded78384 --- /dev/null +++ b/messages/fr.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Afficher ou masquer la barre latérale", + "header_avatar_alt": "Avatar de {name}", + "header_menu_profile": "Profil", + "header_menu_settings": "Paramètres", + "header_menu_logout": "Se déconnecter", + "header_login": "Se connecter", + "header_signup": "S'inscrire", + "header_github_title": "OpenShock sur GitHub", + "header_discord_title": "Discord de la communauté", + + "footer_made_with": "Fait avec {#heart/} par l'{#team}équipe OpenShock{/team}", + "footer_connection_status_label": "État de la connexion au serveur", + "footer_signalr_state": "État de SignalR", + "footer_frontend_version": "Version du frontend", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Version du backend", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Langue", + + "sidebar_home_link_label": "Accueil d'OpenShock", + "sidebar_group_general": "Général", + "sidebar_home": "Accueil", + "sidebar_shockers": "Shockers", + "sidebar_shared_shockers": "Shockers partagés", + "sidebar_hubs": "Hubs", + "sidebar_user_shares": "Partages avec des utilisateurs", + "sidebar_public_shares": "Partages publics", + "sidebar_serial_terminal": "Terminal série", + "sidebar_group_admin": "Administration", + "sidebar_admin_monitoring": "Surveillance", + "sidebar_admin_online_hubs": "Hubs en ligne", + "sidebar_admin_online_gateways": "Passerelles en ligne", + "sidebar_admin_management": "Gestion", + "sidebar_admin_users": "Utilisateurs", + "sidebar_admin_blacklists": "Listes de blocage", + "sidebar_admin_webhooks": "Webhooks", + "sidebar_admin_mail": "E-mail", + "sidebar_admin_automation_tokens": "Jetons d'automatisation", + "sidebar_admin_configuration": "Configuration", + "sidebar_group_settings": "Paramètres", + "sidebar_settings_api_tokens": "Jetons d'API", + "sidebar_settings_sessions": "Sessions", + "sidebar_settings_audit_log": "Journal d'activité", + "sidebar_settings_connections": "Comptes associés", + "sidebar_settings_account": "Paramètres du compte", + + "error_meta_title": "Service indisponible", + "error_meta_description": "OpenShock est actuellement indisponible", + "error_meta_logo_alt": "Logo d'OpenShock", + "error_fallback_message": "Une erreur s'est produite.", + "error_not_found_description": "La page que vous cherchez n'existe pas.", + "error_unexpected_description": "Une erreur inattendue s'est produite.", + "error_go_back": "Retour", + + "auth_layout_logo_alt": "Logo d'OpenShock", + "auth_layout_invalid_redirect_removed": "Une URL de redirection invalide a été supprimée pour votre sécurité.", + + "auth_activate_breadcrumb": "Activer le compte", + "auth_activate_title": "Activer le compte", + "auth_activate_activating": "Activation de votre compte...", + "auth_activate_success": "Votre compte a été activé", + "auth_activate_failed": "Ce lien d'activation est invalide ou a expiré", + "auth_activate_missing_token": "Jeton d'activation manquant", + "auth_activate_continue": "Continuer vers la connexion", + "auth_activate_toast_success": "Compte activé", + + "auth_forgot_breadcrumb": "Mot de passe oublié", + "auth_forgot_title": "Mot de passe oublié", + "auth_forgot_description": "Saisissez votre adresse e-mail pour réinitialiser votre mot de passe", + "auth_forgot_email_label": "E-mail", + "auth_forgot_email_placeholder": "E-mail", + "auth_forgot_submit": "Réinitialiser le mot de passe", + "auth_forgot_signin_prompt": "Vous vous souvenez de votre mot de passe ? {#signin}Se connecter{/signin}", + "auth_forgot_mail_disabled": "La réinitialisation des mots de passe n'est pas disponible sur cette instance, car aucun fournisseur d'e-mail n'est configuré. Contactez votre administrateur pour récupérer l'accès à votre compte.", + "auth_forgot_toast_sent": "La demande de réinitialisation du mot de passe a été envoyée à l'adresse e-mail indiquée", + + "auth_login_breadcrumb": "Connexion", + "auth_login_title": "Bon retour parmi nous", + "auth_login_loading_methods": "Chargement des méthodes de connexion disponibles", + "auth_login_choose_method": "Connectez-vous avec l'une de ces méthodes", + "auth_login_with_account": "Connectez-vous avec votre compte OpenShock", + "auth_login_activation_after_signup": "Votre compte a été créé. Nous avons envoyé un lien d'activation à votre adresse e-mail. Vérifiez votre boîte de réception et vos courriers indésirables.", + "auth_login_activation_pending": "Votre compte n'est pas encore activé. Nous avons envoyé un lien d'activation à votre adresse e-mail. Vérifiez votre boîte de réception et vos courriers indésirables. Si vous ne recevez rien, reconnectez-vous plus tard pour obtenir un nouveau lien.", + "auth_login_oauth_only": "Ce compte n'a pas de mot de passe. Connectez-vous avec le fournisseur utilisé lors de votre inscription.", + "auth_login_oauth_only_with_buttons": "Ce compte n'a pas de mot de passe. Connectez-vous avec le fournisseur utilisé lors de votre inscription à l'aide des boutons ci-dessus.", + "auth_login_or_continue_with": "Ou continuer avec", + "auth_login_username_label": "Nom d'utilisateur ou e-mail", + "auth_login_password_label": "Mot de passe", + "auth_login_submit": "Se connecter", + "auth_login_signup_prompt": "Vous n'avez pas de compte ? {#signup}S'inscrire{/signup}", + "auth_login_legal": "En cliquant sur « Se connecter », vous acceptez nos {#tos}Conditions d'utilisation{/tos} et notre {#privacy}Politique de confidentialité{/privacy}.", + + "auth_oauth_create_breadcrumb": "S'inscrire avec {provider}", + "auth_oauth_create_title": "S'inscrire avec {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Complétez les informations de votre compte", + "auth_oauth_create_username_label": "Nom d'utilisateur", + "auth_oauth_create_username_placeholder": "Nom d'utilisateur", + "auth_oauth_create_email_label": "E-mail", + "auth_oauth_create_email_placeholder": "E-mail", + "auth_oauth_create_submit": "S'inscrire", + "auth_oauth_create_legal": "En cliquant sur « S'inscrire », vous acceptez nos {#tos}Conditions d'utilisation{/tos} et notre {#privacy}Politique de confidentialité{/privacy}.", + + "auth_oauth_error_breadcrumb": "Erreur d'authentification", + "auth_oauth_error_title": "Erreur d'authentification", + "auth_oauth_error_back": "Retour à la connexion", + + "auth_reset_breadcrumb": "Réinitialiser le mot de passe", + "auth_reset_title": "Réinitialiser le mot de passe", + "auth_reset_checking": "Vérification du lien de réinitialisation...", + "auth_reset_choose_password": "Choisissez un nouveau mot de passe pour votre compte", + "auth_reset_invalid": "Ce lien de réinitialisation est invalide ou a expiré", + "auth_reset_password_label": "Nouveau mot de passe", + "auth_reset_password_placeholder": "Nouveau mot de passe", + "auth_reset_confirm_label": "Confirmer le nouveau mot de passe", + "auth_reset_confirm_placeholder": "Confirmer le nouveau mot de passe", + "auth_reset_submit": "Réinitialiser le mot de passe", + "auth_reset_request_new": "Demander un nouveau lien", + "auth_reset_toast_success": "Le mot de passe a été réinitialisé", + + "auth_signup_breadcrumb": "Inscription", + "auth_signup_toast_created_mail": "Compte créé avec succès. Consultez vos e-mails pour vérifier votre compte.", + "auth_signup_toast_created_no_mail": "Compte créé avec succès. Vous pouvez vous connecter dès maintenant.", + "auth_signup_dialog_title": "Bienvenue ! Merci de vous être inscrit ! ❤️", + "auth_signup_dialog_created_mail": "Votre compte a été créé. 🎉 Consultez vos e-mails pour vérifier votre compte.", + "auth_signup_dialog_verify_then_login": "Une fois votre adresse e-mail vérifiée, vous pourrez vous connecter à votre compte.", + "auth_signup_dialog_created_no_mail": "Votre compte a été créé et est prêt à l'emploi. 🎉", + "auth_signup_dialog_login_now": "Vous pouvez vous connecter à votre compte dès maintenant.", + "auth_signup_title": "Créez votre compte", + "auth_signup_loading_methods": "Chargement des méthodes d'inscription disponibles", + "auth_signup_using_email": "Inscription par e-mail", + "auth_signup_choose_method": "Choisissez votre méthode d'inscription", + "auth_signup_back": "Retour", + "auth_signup_username_label": "Nom d'utilisateur", + "auth_signup_username_placeholder": "Jean OpenShock", + "auth_signup_email_label": "E-mail", + "auth_signup_email_placeholder": "jean@example.com", + "auth_signup_password_label": "Mot de passe", + "auth_signup_password_placeholder": "Mot de passe", + "auth_signup_confirm_label": "Confirmer le mot de passe", + "auth_signup_confirm_placeholder": "Confirmer le mot de passe", + "auth_signup_submit": "Créer un compte", + "auth_signup_signin_prompt": "Vous avez déjà un compte ? {#signin}Se connecter{/signin}", + "auth_signup_or": "Ou", + "auth_signup_with_email": "S'inscrire par e-mail", + "auth_signup_legal": "En cliquant sur « Créer un compte », vous acceptez nos {#tos}Conditions d'utilisation{/tos} et notre {#privacy}Politique de confidentialité{/privacy}.", + + "auth_verify_breadcrumb": "Vérifier l'e-mail", + "auth_verify_title": "Vérifier l'e-mail", + "auth_verify_verifying": "Vérification de votre adresse e-mail...", + "auth_verify_success": "Votre adresse e-mail a été vérifiée", + "auth_verify_failed": "Ce lien de vérification est invalide ou a expiré", + "auth_verify_missing_token": "Jeton de vérification manquant", + "auth_verify_continue": "Continuer vers la connexion", + "auth_verify_toast_success": "Adresse e-mail vérifiée" +} diff --git a/messages/sv.json b/messages/sv.json new file mode 100644 index 00000000..955cbdc6 --- /dev/null +++ b/messages/sv.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Visa/dölj sidopanelen", + "header_avatar_alt": "{name}s avatar", + "header_menu_profile": "Profil", + "header_menu_settings": "Inställningar", + "header_menu_logout": "Logga ut", + "header_login": "Logga in", + "header_signup": "Registrera dig", + "header_github_title": "OpenShock på GitHub", + "header_discord_title": "Communityns Discord", + + "footer_made_with": "Gjort med {#heart/} av {#team}OpenShock-teamet{/team}", + "footer_connection_status_label": "Anslutningsstatus till servern", + "footer_signalr_state": "SignalR-status", + "footer_frontend_version": "Frontend-version", + "footer_frontend_commit_title": "Commit {commit}", + "footer_backend_version": "Backend-version", + "footer_backend_commit_title": "Commit {commit}", + "footer_language_label": "Språk", + + "sidebar_home_link_label": "OpenShocks startsida", + "sidebar_group_general": "Allmänt", + "sidebar_home": "Hem", + "sidebar_shockers": "Shockers", + "sidebar_shared_shockers": "Delade shockers", + "sidebar_hubs": "Hubbar", + "sidebar_user_shares": "Användardelningar", + "sidebar_public_shares": "Offentliga delningar", + "sidebar_serial_terminal": "Seriell terminal", + "sidebar_group_admin": "Admin", + "sidebar_admin_monitoring": "Övervakning", + "sidebar_admin_online_hubs": "Anslutna hubbar", + "sidebar_admin_online_gateways": "Anslutna gateways", + "sidebar_admin_management": "Administration", + "sidebar_admin_users": "Användare", + "sidebar_admin_blacklists": "Blocklistor", + "sidebar_admin_webhooks": "Webhooks", + "sidebar_admin_mail": "E-post", + "sidebar_admin_automation_tokens": "Automatiseringstokens", + "sidebar_admin_configuration": "Konfiguration", + "sidebar_group_settings": "Inställningar", + "sidebar_settings_api_tokens": "API-tokens", + "sidebar_settings_sessions": "Sessioner", + "sidebar_settings_audit_log": "Aktivitetslogg", + "sidebar_settings_connections": "Kopplingar", + "sidebar_settings_account": "Kontoinställningar", + + "error_meta_title": "Tjänsten är inte tillgänglig", + "error_meta_description": "OpenShock är inte tillgängligt just nu", + "error_meta_logo_alt": "OpenShocks logotyp", + "error_fallback_message": "Något gick fel.", + "error_not_found_description": "Sidan du letar efter finns inte.", + "error_unexpected_description": "Ett oväntat fel inträffade.", + "error_go_back": "Gå tillbaka", + + "auth_layout_logo_alt": "OpenShocks logotyp", + "auth_layout_invalid_redirect_removed": "En ogiltig omdirigerings-URL togs bort för din säkerhet.", + + "auth_activate_breadcrumb": "Aktivera konto", + "auth_activate_title": "Aktivera konto", + "auth_activate_activating": "Aktiverar ditt konto...", + "auth_activate_success": "Ditt konto har aktiverats", + "auth_activate_failed": "Den här aktiveringslänken är ogiltig eller har gått ut", + "auth_activate_missing_token": "Aktiveringstoken saknas", + "auth_activate_continue": "Fortsätt till inloggningen", + "auth_activate_toast_success": "Kontot har aktiverats", + + "auth_forgot_breadcrumb": "Glömt lösenord", + "auth_forgot_title": "Glömt lösenord", + "auth_forgot_description": "Ange din e-postadress för att återställa ditt lösenord", + "auth_forgot_email_label": "E-post", + "auth_forgot_email_placeholder": "E-post", + "auth_forgot_submit": "Återställ lösenord", + "auth_forgot_signin_prompt": "Kommer du ihåg ditt lösenord? {#signin}Logga in{/signin}", + "auth_forgot_mail_disabled": "Det går inte att återställa lösenord på den här instansen eftersom ingen e-postleverantör är konfigurerad. Kontakta din administratör för att få hjälp att återfå åtkomst till ditt konto.", + "auth_forgot_toast_sent": "En begäran om att återställa lösenordet har skickats till den angivna e-postadressen", + + "auth_login_breadcrumb": "Logga in", + "auth_login_title": "Välkommen tillbaka", + "auth_login_loading_methods": "Läser in tillgängliga inloggningsmetoder", + "auth_login_choose_method": "Logga in med någon av dessa metoder", + "auth_login_with_account": "Logga in med ditt OpenShock-konto", + "auth_login_activation_after_signup": "Ditt konto har skapats. Vi har skickat en aktiveringslänk till din e-postadress. Kontrollera inkorgen och skräpposten.", + "auth_login_activation_pending": "Ditt konto är inte aktiverat än. Vi har skickat en aktiveringslänk till din e-postadress. Kontrollera inkorgen och skräpposten. Om inget kommer fram kan du försöka logga in igen senare för att få en ny länk.", + "auth_login_oauth_only": "Det här kontot har inget lösenord. Logga in med leverantören du registrerade dig med.", + "auth_login_oauth_only_with_buttons": "Det här kontot har inget lösenord. Logga in med leverantören du registrerade dig med via knapparna ovan.", + "auth_login_or_continue_with": "Eller fortsätt med", + "auth_login_username_label": "Användarnamn eller e-post", + "auth_login_password_label": "Lösenord", + "auth_login_submit": "Logga in", + "auth_login_signup_prompt": "Har du inget konto? {#signup}Registrera dig{/signup}", + "auth_login_legal": "Genom att klicka på ”Logga in” godkänner du våra {#tos}användarvillkor{/tos} och vår {#privacy}integritetspolicy{/privacy}.", + + "auth_oauth_create_breadcrumb": "Registrera dig med {provider}", + "auth_oauth_create_title": "Registrera dig med {#provider}{provider}{/provider}", + "auth_oauth_create_description": "Fyll i dina kontouppgifter", + "auth_oauth_create_username_label": "Användarnamn", + "auth_oauth_create_username_placeholder": "Användarnamn", + "auth_oauth_create_email_label": "E-post", + "auth_oauth_create_email_placeholder": "E-post", + "auth_oauth_create_submit": "Registrera dig", + "auth_oauth_create_legal": "Genom att klicka på ”Registrera dig” godkänner du våra {#tos}användarvillkor{/tos} och vår {#privacy}integritetspolicy{/privacy}.", + + "auth_oauth_error_breadcrumb": "Autentiseringsfel", + "auth_oauth_error_title": "Autentiseringsfel", + "auth_oauth_error_back": "Tillbaka till inloggningen", + + "auth_reset_breadcrumb": "Återställ lösenord", + "auth_reset_title": "Återställ lösenord", + "auth_reset_checking": "Kontrollerar återställningslänken...", + "auth_reset_choose_password": "Välj ett nytt lösenord för ditt konto", + "auth_reset_invalid": "Den här återställningslänken är ogiltig eller har gått ut", + "auth_reset_password_label": "Nytt lösenord", + "auth_reset_password_placeholder": "Nytt lösenord", + "auth_reset_confirm_label": "Bekräfta nytt lösenord", + "auth_reset_confirm_placeholder": "Bekräfta nytt lösenord", + "auth_reset_submit": "Återställ lösenord", + "auth_reset_request_new": "Begär en ny länk", + "auth_reset_toast_success": "Lösenordet har återställts", + + "auth_signup_breadcrumb": "Registrera dig", + "auth_signup_toast_created_mail": "Kontot har skapats. Kontrollera din e-post för att verifiera kontot.", + "auth_signup_toast_created_no_mail": "Kontot har skapats. Du kan logga in nu.", + "auth_signup_dialog_title": "Välkommen! Tack för att du registrerade dig! ❤️", + "auth_signup_dialog_created_mail": "Ditt konto har skapats. 🎉 Kontrollera din e-post för att verifiera kontot.", + "auth_signup_dialog_verify_then_login": "När du har verifierat din e-postadress kan du logga in på ditt konto.", + "auth_signup_dialog_created_no_mail": "Ditt konto har skapats och är redo att användas. 🎉", + "auth_signup_dialog_login_now": "Du kan logga in på ditt konto nu.", + "auth_signup_title": "Skapa ditt konto", + "auth_signup_loading_methods": "Läser in tillgängliga registreringsmetoder", + "auth_signup_using_email": "Registrering med e-post", + "auth_signup_choose_method": "Välj hur du vill registrera dig", + "auth_signup_back": "Tillbaka", + "auth_signup_username_label": "Användarnamn", + "auth_signup_username_placeholder": "Sven OpenShock", + "auth_signup_email_label": "E-post", + "auth_signup_email_placeholder": "sven@example.com", + "auth_signup_password_label": "Lösenord", + "auth_signup_password_placeholder": "Lösenord", + "auth_signup_confirm_label": "Bekräfta lösenord", + "auth_signup_confirm_placeholder": "Bekräfta lösenord", + "auth_signup_submit": "Skapa konto", + "auth_signup_signin_prompt": "Har du redan ett konto? {#signin}Logga in{/signin}", + "auth_signup_or": "Eller", + "auth_signup_with_email": "Registrera dig med e-post", + "auth_signup_legal": "Genom att klicka på ”Skapa konto” godkänner du våra {#tos}användarvillkor{/tos} och vår {#privacy}integritetspolicy{/privacy}.", + + "auth_verify_breadcrumb": "Verifiera e-post", + "auth_verify_title": "Verifiera e-post", + "auth_verify_verifying": "Verifierar din e-postadress...", + "auth_verify_success": "Din e-postadress har verifierats", + "auth_verify_failed": "Den här verifieringslänken är ogiltig eller har gått ut", + "auth_verify_missing_token": "Verifieringstoken saknas", + "auth_verify_continue": "Fortsätt till inloggningen", + "auth_verify_toast_success": "E-postadressen har verifierats" +} diff --git a/messages/uwu.json b/messages/uwu.json new file mode 100644 index 00000000..aea68c66 --- /dev/null +++ b/messages/uwu.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + + "header_sidebar_toggle": "Fwip the sidebar", + "header_avatar_alt": "{name}'s adorable little face", + "header_menu_profile": "My fursona", + "header_menu_settings": "Settings~", + "header_menu_logout": "Log out (come back soon!!)", + "header_login": "Log in", + "header_signup": "Join the pack!", + "header_github_title": "OpenShock on GitHub~", + "header_discord_title": "Come hang out in our Discord den!", + + "footer_made_with": "Made with lots of {#heart/} by the {#team}OpenShock pack{/team} *wags tail*", + "footer_connection_status_label": "Is the server still there? *pokes*", + "footer_signalr_state": "SignalR vibes", + "footer_frontend_version": "Frontend version", + "footer_frontend_commit_title": "Commit {commit} >w<", + "footer_backend_version": "Backend version", + "footer_backend_commit_title": "Commit {commit} >w<", + "footer_language_label": "Language~", + + "sidebar_home_link_label": "OpenShock home den", + "sidebar_group_general": "General stuffs", + "sidebar_home": "Home den", + "sidebar_shockers": "Zappy fwiends", + "sidebar_shared_shockers": "Zappy fwiends I borrowed", + "sidebar_hubs": "Hubbies", + "sidebar_user_shares": "Sharing with fwiends", + "sidebar_public_shares": "Sharing with everyone", + "sidebar_serial_terminal": "Serial terminal (beep boop)", + "sidebar_group_admin": "Pack leader", + "sidebar_admin_monitoring": "Keeping watch", + "sidebar_admin_online_hubs": "Hubbies who are awake", + "sidebar_admin_online_gateways": "Gateways who are awake", + "sidebar_admin_management": "Pack business", + "sidebar_admin_users": "Every lil fwiend", + "sidebar_admin_blacklists": "No-no lists", + "sidebar_admin_webhooks": "Web hookies", + "sidebar_admin_mail": "Letters", + "sidebar_admin_automation_tokens": "Robot tokens", + "sidebar_admin_configuration": "Knobs and dials", + "sidebar_group_settings": "Settings~", + "sidebar_settings_api_tokens": "API tokies", + "sidebar_settings_sessions": "Where I'm logged in", + "sidebar_settings_audit_log": "What I've been up to", + "sidebar_settings_connections": "Linked accounts", + "sidebar_settings_account": "My account stuffs", + + "error_meta_title": "OpenShock is taking a nap", + "error_meta_description": "OpenShock is snoozing right now, pwease try again in a bit~", + "error_meta_logo_alt": "OpenShock logo", + "error_fallback_message": "Oopsie woopsie! Something went wrong!", + "error_not_found_description": "I sniffed everywhere, but this page doesn't exist... *sad ear flop*", + "error_unexpected_description": "Something unexpected happened! *hides behind tail*", + "error_go_back": "Go back~", + + "auth_layout_logo_alt": "OpenShock logo", + "auth_layout_invalid_redirect_removed": "I found a sketchy redirect link and chewed it up to keep you safe! *protective growl*", + + "auth_activate_breadcrumb": "Wake up account", + "auth_activate_title": "Wake up your account", + "auth_activate_activating": "Waking up your account... *big stretch*", + "auth_activate_success": "Your account is wide awake! Yay!", + "auth_activate_failed": "This link is broken or too old... *whimpers*", + "auth_activate_missing_token": "The activation token wandered off somewhere...", + "auth_activate_continue": "Onward to login!", + "auth_activate_toast_success": "Account activated! *happy tail wags*", + + "auth_forgot_breadcrumb": "Forgot pawsword", + "auth_forgot_title": "Forgot your pawsword?", + "auth_forgot_description": "Tell me your email and I'll fetch you a reset link!", + "auth_forgot_email_label": "Email", + "auth_forgot_email_placeholder": "Email", + "auth_forgot_submit": "Fetch reset link!", + "auth_forgot_signin_prompt": "Remembered it after all? {#signin}Log in{/signin}", + "auth_forgot_mail_disabled": "Sowwy, this den has no mailbox set up, so I can't send reset links... Pwease ask your pack leader (the admin) to help you get back in!", + "auth_forgot_toast_sent": "Reset link fetched and sent to your email! *drops it at your feet*", + + "auth_login_breadcrumb": "Log in", + "auth_login_title": "Welcome back, fwiend!", + "auth_login_loading_methods": "Sniffing out ways to log in...", + "auth_login_choose_method": "Pick your favorite way to log in~", + "auth_login_with_account": "Log in with your OpenShock account", + "auth_login_activation_after_signup": "Your account is ready! I sent a wake-up link to your email. Check your inbox, and peek in the spam folder too~", + "auth_login_activation_pending": "Your account is still sleepy and not activated yet! I sent a wake-up link to your email. Check your inbox and spam folder. If nothing shows up, try logging in again later and I'll send a fresh one!", + "auth_login_oauth_only": "This account doesn't have a pawsword! Log in with the service you signed up with~", + "auth_login_oauth_only_with_buttons": "This account doesn't have a pawsword! Log in with the service you signed up with, using the buttons up there~", + "auth_login_or_continue_with": "Or continue with", + "auth_login_username_label": "Username or email", + "auth_login_password_label": "Pawsword", + "auth_login_submit": "Log in!", + "auth_login_signup_prompt": "Not in the pack yet? {#signup}Join us!{/signup}", + "auth_login_legal": "By pressing Log in, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}.", + + "auth_oauth_create_breadcrumb": "Join with {provider}", + "auth_oauth_create_title": "Join the pack with {#provider}{provider}{/provider}!", + "auth_oauth_create_description": "Just a few more details and you're in~", + "auth_oauth_create_username_label": "Username", + "auth_oauth_create_username_placeholder": "Username", + "auth_oauth_create_email_label": "Email", + "auth_oauth_create_email_placeholder": "Email", + "auth_oauth_create_submit": "Join the pack!", + "auth_oauth_create_legal": "By pressing Join the pack, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}.", + + "auth_oauth_error_breadcrumb": "Login oopsie", + "auth_oauth_error_title": "Uh oh, login oopsie!", + "auth_oauth_error_back": "Back to login", + + "auth_reset_breadcrumb": "Reset pawsword", + "auth_reset_title": "Reset pawsword", + "auth_reset_checking": "Sniffing your reset link...", + "auth_reset_choose_password": "Pick a shiny new pawsword~", + "auth_reset_invalid": "This reset link is broken or too old... *whimpers*", + "auth_reset_password_label": "New pawsword", + "auth_reset_password_placeholder": "New pawsword", + "auth_reset_confirm_label": "New pawsword again", + "auth_reset_confirm_placeholder": "New pawsword again", + "auth_reset_submit": "Reset pawsword!", + "auth_reset_request_new": "Fetch a new reset link", + "auth_reset_toast_success": "Pawsword reset! Don't forget it this time, silly~", + + "auth_signup_breadcrumb": "Join the pack", + "auth_signup_toast_created_mail": "Yay, account made! Check your email to verify it~", + "auth_signup_toast_created_no_mail": "Yay, account made! You can log in right away~", + "auth_signup_dialog_title": "Welcome to the pack!! Thank you for joining! ❤️ *nuzzles*", + "auth_signup_dialog_created_mail": "Your account is made! 🎉 Check your email to verify it~", + "auth_signup_dialog_verify_then_login": "Once your email is verified, you can hop right in!", + "auth_signup_dialog_created_no_mail": "Your account is made and ready to go! 🎉", + "auth_signup_dialog_login_now": "You can log in right now~", + "auth_signup_title": "Join the pack!", + "auth_signup_loading_methods": "Sniffing out ways to sign up...", + "auth_signup_using_email": "Signing up with email~", + "auth_signup_choose_method": "How do you wanna join the pack?", + "auth_signup_back": "Back", + "auth_signup_username_label": "Username", + "auth_signup_username_placeholder": "Fluffy McShockface", + "auth_signup_email_label": "Email", + "auth_signup_email_placeholder": "fluffy@example.com", + "auth_signup_password_label": "Pawsword", + "auth_signup_password_placeholder": "Pawsword", + "auth_signup_confirm_label": "Pawsword again", + "auth_signup_confirm_placeholder": "Pawsword again", + "auth_signup_submit": "Join the pack!", + "auth_signup_signin_prompt": "Already in the pack? {#signin}Log in{/signin}", + "auth_signup_or": "Or", + "auth_signup_with_email": "Join with email", + "auth_signup_legal": "By pressing Join the pack, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}.", + + "auth_verify_breadcrumb": "Verify email", + "auth_verify_title": "Verify your email", + "auth_verify_verifying": "Sniffing your email address...", + "auth_verify_success": "Your email is verified! *happy bouncing*", + "auth_verify_failed": "This verification link is broken or too old... *whimpers*", + "auth_verify_missing_token": "The verification token wandered off somewhere...", + "auth_verify_continue": "Onward to login!", + "auth_verify_toast_success": "Email verified! *wags*" +} diff --git a/project.inlang/settings.json b/project.inlang/settings.json index 4fdc710c..cb9dfea8 100644 --- a/project.inlang/settings.json +++ b/project.inlang/settings.json @@ -1,7 +1,7 @@ { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", - "locales": ["en", "de", "nb"], + "locales": ["en", "de", "nb", "es", "sv", "fi", "fr", "et", "uwu"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4.5.0/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.3.0/dist/index.js" diff --git a/src/lib/i18n/open-graph.ts b/src/lib/i18n/open-graph.ts index 6d467757..d7c3322e 100644 --- a/src/lib/i18n/open-graph.ts +++ b/src/lib/i18n/open-graph.ts @@ -4,6 +4,13 @@ const OG_LOCALES: Record = { en: 'en_US', de: 'de_DE', nb: 'nb_NO', + es: 'es_ES', + sv: 'sv_SE', + fi: 'fi_FI', + fr: 'fr_FR', + et: 'et_EE', + // Not a real language, so it shares English's territory. + uwu: 'en_US', }; /** The current locale in the `language_TERRITORY` form `og:locale` expects. */ diff --git a/src/routes/LanguageSwitcher.svelte b/src/routes/LanguageSwitcher.svelte index 95d7caa6..8be34127 100644 --- a/src/routes/LanguageSwitcher.svelte +++ b/src/routes/LanguageSwitcher.svelte @@ -9,6 +9,8 @@ // Each language is named in itself, so users can find theirs whatever the page is in. function nativeName(locale: Locale): string { + // Not a real language, so Intl has no name for it. + if (locale === 'uwu') return 'UwU'; const name = new Intl.DisplayNames([locale], { type: 'language' }).of(locale) ?? locale; return name.charAt(0).toLocaleUpperCase(locale) + name.slice(1); } From 22df125b9683716802a3fe7607ca1a73c37d3ead Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:42:56 +0100 Subject: [PATCH 08/38] fix(i18n): translate framework error messages and let the switcher save a ?lang override SvelteKit's "Not Found" and "Internal Error" messages are always English, so the error page now swaps them for translated text. Messages thrown by the app itself are still shown as they are. Re-picking the language a ?lang= link set changed nothing, so it could not be saved. Each switcher item now handles its own selection, so re-picking it saves the language and drops the parameter. --- messages/de.json | 1 + messages/en.json | 1 + messages/es.json | 1 + messages/et.json | 1 + messages/fi.json | 1 + messages/fr.json | 1 + messages/nb.json | 1 + messages/sv.json | 1 + src/lib/i18n/query-locale.ts | 8 ++++---- src/routes/+error.svelte | 10 +++++++++- src/routes/LanguageSwitcher.svelte | 14 ++++++++------ 11 files changed, 29 insertions(+), 11 deletions(-) diff --git a/messages/de.json b/messages/de.json index 48206cd6..9f446cd8 100644 --- a/messages/de.json +++ b/messages/de.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock ist derzeit nicht verfügbar", "error_meta_logo_alt": "OpenShock-Logo", "error_fallback_message": "Etwas ist schiefgelaufen.", + "error_not_found_message": "Seite nicht gefunden", "error_not_found_description": "Die gesuchte Seite existiert nicht.", "error_unexpected_description": "Ein unerwarteter Fehler ist aufgetreten.", "error_go_back": "Zurück", diff --git a/messages/en.json b/messages/en.json index ff61e32c..5b2c91e4 100644 --- a/messages/en.json +++ b/messages/en.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock is currently unavailable", "error_meta_logo_alt": "OpenShock Logo", "error_fallback_message": "Something went wrong.", + "error_not_found_message": "Page not found", "error_not_found_description": "The page you're looking for doesn't exist.", "error_unexpected_description": "An unexpected error occurred.", "error_go_back": "Go back", diff --git a/messages/es.json b/messages/es.json index 5cdb4585..9c56af3d 100644 --- a/messages/es.json +++ b/messages/es.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock no está disponible en este momento", "error_meta_logo_alt": "Logotipo de OpenShock", "error_fallback_message": "Algo ha salido mal.", + "error_not_found_message": "Página no encontrada", "error_not_found_description": "La página que buscas no existe.", "error_unexpected_description": "Se ha producido un error inesperado.", "error_go_back": "Volver", diff --git a/messages/et.json b/messages/et.json index db368db8..dc03c30d 100644 --- a/messages/et.json +++ b/messages/et.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock pole praegu saadaval", "error_meta_logo_alt": "OpenShocki logo", "error_fallback_message": "Midagi läks valesti.", + "error_not_found_message": "Lehte ei leitud", "error_not_found_description": "Otsitavat lehte pole olemas.", "error_unexpected_description": "Tekkis ootamatu viga.", "error_go_back": "Tagasi", diff --git a/messages/fi.json b/messages/fi.json index 7cddd007..63ead96b 100644 --- a/messages/fi.json +++ b/messages/fi.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock ei ole tällä hetkellä käytettävissä", "error_meta_logo_alt": "OpenShockin logo", "error_fallback_message": "Jokin meni pieleen.", + "error_not_found_message": "Sivua ei löytynyt", "error_not_found_description": "Etsimääsi sivua ei ole olemassa.", "error_unexpected_description": "Tapahtui odottamaton virhe.", "error_go_back": "Takaisin", diff --git a/messages/fr.json b/messages/fr.json index ded78384..f2508e27 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock est actuellement indisponible", "error_meta_logo_alt": "Logo d'OpenShock", "error_fallback_message": "Une erreur s'est produite.", + "error_not_found_message": "Page introuvable", "error_not_found_description": "La page que vous cherchez n'existe pas.", "error_unexpected_description": "Une erreur inattendue s'est produite.", "error_go_back": "Retour", diff --git a/messages/nb.json b/messages/nb.json index 1f69d97f..b60be81c 100644 --- a/messages/nb.json +++ b/messages/nb.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock er for øyeblikket utilgjengelig", "error_meta_logo_alt": "OpenShock-logo", "error_fallback_message": "Noe gikk galt.", + "error_not_found_message": "Fant ikke siden", "error_not_found_description": "Siden du leter etter finnes ikke.", "error_unexpected_description": "Det oppstod en uventet feil.", "error_go_back": "Gå tilbake", diff --git a/messages/sv.json b/messages/sv.json index 955cbdc6..204e79d1 100644 --- a/messages/sv.json +++ b/messages/sv.json @@ -51,6 +51,7 @@ "error_meta_description": "OpenShock är inte tillgängligt just nu", "error_meta_logo_alt": "OpenShocks logotyp", "error_fallback_message": "Något gick fel.", + "error_not_found_message": "Sidan hittades inte", "error_not_found_description": "Sidan du letar efter finns inte.", "error_unexpected_description": "Ett oväntat fel inträffade.", "error_go_back": "Gå tillbaka", diff --git a/src/lib/i18n/query-locale.ts b/src/lib/i18n/query-locale.ts index 9c8f1c55..4d25bcaf 100644 --- a/src/lib/i18n/query-locale.ts +++ b/src/lib/i18n/query-locale.ts @@ -24,23 +24,23 @@ defineCustomServerStrategy('custom-query', { // Read once per document, so client-side navigation away from the `?lang=` URL keeps the // whole page in one language until the next full load. -const override = +export const localeOverride = typeof window === 'undefined' ? undefined : localeFromUrl(new URL(window.location.href)); defineCustomClientStrategy('custom-query', { - getLocale: () => override, + getLocale: () => localeOverride, // Persisting is the cookie strategy's job; this one only ever reads the URL. setLocale: () => {}, }); -if (override) { +if (localeOverride) { const originalSetLocale = setLocale; overwriteSetLocale((locale, options) => { // Paraglide calls `setLocale(resolved, { reload: false })` on the first `getLocale()` to // persist the detected locale. Skip it for the override so the cookie keeps the user's choice. if (options?.reload === false) { - if (locale === override) return; + if (locale === localeOverride) return; return originalSetLocale(locale, options); } diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index ff785cea..8d7d687d 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -23,6 +23,14 @@ } }); + // SvelteKit's own messages are always English, so swap those for translated ones. + // Messages thrown by the app itself are shown as they are. + function errorHeading(message: string | undefined): string { + if (message === 'Not Found') return m.error_not_found_message(); + if (message === undefined || message === 'Internal Error') return m.error_fallback_message(); + return message; + } + const meta = { title: m.error_meta_title(), description: m.error_meta_description(), @@ -48,7 +56,7 @@

- {page.error?.message ?? m.error_fallback_message()} + {errorHeading(page.error?.message)}

diff --git a/src/routes/LanguageSwitcher.svelte b/src/routes/LanguageSwitcher.svelte index 8be34127..4a5315c9 100644 --- a/src/routes/LanguageSwitcher.svelte +++ b/src/routes/LanguageSwitcher.svelte @@ -3,7 +3,8 @@ import { Button } from '@openshock/svelte-core/components/ui/button'; import * as DropdownMenu from '@openshock/svelte-core/components/ui/dropdown-menu'; import { m } from '#lib/paraglide/messages.js'; - import { getLocale, isLocale, locales, setLocale, type Locale } from '#lib/paraglide/runtime.js'; + import { localeOverride } from '#lib/i18n/query-locale.js'; + import { getLocale, locales, setLocale, type Locale } from '#lib/paraglide/runtime.js'; const current = getLocale(); @@ -15,9 +16,10 @@ return name.charAt(0).toLocaleUpperCase(locale) + name.slice(1); } - function onValueChange(value: string) { - // setLocale saves the choice and reloads the page. - if (isLocale(value) && value !== current) setLocale(value); + // setLocale saves the choice and reloads the page. + // Picking the language a `?lang=` link set is not a change, but it still saves it and drops the parameter. + function select(locale: Locale) { + if (locale !== current || localeOverride !== undefined) setLocale(locale); } @@ -38,9 +40,9 @@ {m.footer_language_label()} - + {#each locales as locale (locale)} - + select(locale)}> {nativeName(locale)} {/each} From 97eacd666bd6be9e036530cf2589771b40891962 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:43:14 +0100 Subject: [PATCH 09/38] feat(i18n): rewrite the uwu locale --- messages/uwu.json | 99 ++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/messages/uwu.json b/messages/uwu.json index aea68c66..960573ef 100644 --- a/messages/uwu.json +++ b/messages/uwu.json @@ -1,19 +1,19 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "header_sidebar_toggle": "Fwip the sidebar", - "header_avatar_alt": "{name}'s adorable little face", - "header_menu_profile": "My fursona", + "header_sidebar_toggle": "Fwip the sidebar~", + "header_avatar_alt": "{name}'s adorable little face ♡", + "header_menu_profile": "My fursona ✨", "header_menu_settings": "Settings~", - "header_menu_logout": "Log out (come back soon!!)", - "header_login": "Log in", - "header_signup": "Join the pack!", + "header_menu_logout": "Log out (you'll come back, right? *pouts*)", + "header_login": "Log in~", + "header_signup": "Join the pack! ♡", "header_github_title": "OpenShock on GitHub~", - "header_discord_title": "Come hang out in our Discord den!", + "header_discord_title": "Come hang out in our Discord den, cutie!", - "footer_made_with": "Made with lots of {#heart/} by the {#team}OpenShock pack{/team} *wags tail*", - "footer_connection_status_label": "Is the server still there? *pokes*", - "footer_signalr_state": "SignalR vibes", + "footer_made_with": "Made with lots of {#heart/} by the {#team}OpenShock pack{/team} *wags tail happily*", + "footer_connection_status_label": "Is the server still there? *pokes gently*", + "footer_signalr_state": "SignalR vibes ✨", "footer_frontend_version": "Frontend version", "footer_frontend_commit_title": "Commit {commit} >w<", "footer_backend_version": "Backend version", @@ -23,13 +23,13 @@ "sidebar_home_link_label": "OpenShock home den", "sidebar_group_general": "General stuffs", "sidebar_home": "Home den", - "sidebar_shockers": "Zappy fwiends", - "sidebar_shared_shockers": "Zappy fwiends I borrowed", + "sidebar_shockers": "Zappy fwiends ⚡", + "sidebar_shared_shockers": "Zappy fwiends I borrowed hehe", "sidebar_hubs": "Hubbies", - "sidebar_user_shares": "Sharing with fwiends", + "sidebar_user_shares": "Sharing with fwiends ♡", "sidebar_public_shares": "Sharing with everyone", - "sidebar_serial_terminal": "Serial terminal (beep boop)", - "sidebar_group_admin": "Pack leader", + "sidebar_serial_terminal": "Serial terminal (beep boop~)", + "sidebar_group_admin": "Pack leader 👑", "sidebar_admin_monitoring": "Keeping watch", "sidebar_admin_online_hubs": "Hubbies who are awake", "sidebar_admin_online_gateways": "Gateways who are awake", @@ -37,48 +37,49 @@ "sidebar_admin_users": "Every lil fwiend", "sidebar_admin_blacklists": "No-no lists", "sidebar_admin_webhooks": "Web hookies", - "sidebar_admin_mail": "Letters", + "sidebar_admin_mail": "Love letters 💌", "sidebar_admin_automation_tokens": "Robot tokens", "sidebar_admin_configuration": "Knobs and dials", "sidebar_group_settings": "Settings~", "sidebar_settings_api_tokens": "API tokies", "sidebar_settings_sessions": "Where I'm logged in", - "sidebar_settings_audit_log": "What I've been up to", + "sidebar_settings_audit_log": "What I've been up to (no peeking!)", "sidebar_settings_connections": "Linked accounts", - "sidebar_settings_account": "My account stuffs", + "sidebar_settings_account": "My account stuffs ✨", - "error_meta_title": "OpenShock is taking a nap", + "error_meta_title": "OpenShock is taking a beauty nap", "error_meta_description": "OpenShock is snoozing right now, pwease try again in a bit~", "error_meta_logo_alt": "OpenShock logo", - "error_fallback_message": "Oopsie woopsie! Something went wrong!", + "error_fallback_message": "Oopsie woopsie! Something went wrong! *flustered*", + "error_not_found_message": "This page wandered off! *sniff sniff*", "error_not_found_description": "I sniffed everywhere, but this page doesn't exist... *sad ear flop*", - "error_unexpected_description": "Something unexpected happened! *hides behind tail*", + "error_unexpected_description": "Something unexpected happened! *hides behind fluffy tail*", "error_go_back": "Go back~", "auth_layout_logo_alt": "OpenShock logo", - "auth_layout_invalid_redirect_removed": "I found a sketchy redirect link and chewed it up to keep you safe! *protective growl*", + "auth_layout_invalid_redirect_removed": "Eep! I found a sketchy redirect link and chewed it up to keep you safe! *protective growl*", "auth_activate_breadcrumb": "Wake up account", - "auth_activate_title": "Wake up your account", - "auth_activate_activating": "Waking up your account... *big stretch*", - "auth_activate_success": "Your account is wide awake! Yay!", + "auth_activate_title": "Wake up your account~", + "auth_activate_activating": "Waking up your account... *big sleepy stretch*", + "auth_activate_success": "Your account is wide awake! Yay~ ♡", "auth_activate_failed": "This link is broken or too old... *whimpers*", - "auth_activate_missing_token": "The activation token wandered off somewhere...", + "auth_activate_missing_token": "The activation token wandered off somewhere... *pouts*", "auth_activate_continue": "Onward to login!", "auth_activate_toast_success": "Account activated! *happy tail wags*", "auth_forgot_breadcrumb": "Forgot pawsword", - "auth_forgot_title": "Forgot your pawsword?", + "auth_forgot_title": "Forgot your pawsword? Silly~", "auth_forgot_description": "Tell me your email and I'll fetch you a reset link!", "auth_forgot_email_label": "Email", "auth_forgot_email_placeholder": "Email", "auth_forgot_submit": "Fetch reset link!", - "auth_forgot_signin_prompt": "Remembered it after all? {#signin}Log in{/signin}", - "auth_forgot_mail_disabled": "Sowwy, this den has no mailbox set up, so I can't send reset links... Pwease ask your pack leader (the admin) to help you get back in!", + "auth_forgot_signin_prompt": "Remembered it after all? {#signin}Log in~{/signin}", + "auth_forgot_mail_disabled": "Sowwy, this den has no mailbox set up, so I can't send reset links... *fidgets* Pwease ask your pack leader (the admin) to help you get back in!", "auth_forgot_toast_sent": "Reset link fetched and sent to your email! *drops it at your feet*", "auth_login_breadcrumb": "Log in", - "auth_login_title": "Welcome back, fwiend!", + "auth_login_title": "Welcome back, cutie! ♡", "auth_login_loading_methods": "Sniffing out ways to log in...", "auth_login_choose_method": "Pick your favorite way to log in~", "auth_login_with_account": "Log in with your OpenShock account", @@ -88,29 +89,29 @@ "auth_login_oauth_only_with_buttons": "This account doesn't have a pawsword! Log in with the service you signed up with, using the buttons up there~", "auth_login_or_continue_with": "Or continue with", "auth_login_username_label": "Username or email", - "auth_login_password_label": "Pawsword", - "auth_login_submit": "Log in!", - "auth_login_signup_prompt": "Not in the pack yet? {#signup}Join us!{/signup}", - "auth_login_legal": "By pressing Log in, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}.", + "auth_login_password_label": "Pawsword (no peeking!)", + "auth_login_submit": "Log in~", + "auth_login_signup_prompt": "Not in the pack yet? {#signup}Join us, cutie!{/signup}", + "auth_login_legal": "By pressing Log in, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}. ♡", "auth_oauth_create_breadcrumb": "Join with {provider}", - "auth_oauth_create_title": "Join the pack with {#provider}{provider}{/provider}!", + "auth_oauth_create_title": "Join the pack with {#provider}{provider}{/provider}! ✨", "auth_oauth_create_description": "Just a few more details and you're in~", "auth_oauth_create_username_label": "Username", "auth_oauth_create_username_placeholder": "Username", "auth_oauth_create_email_label": "Email", "auth_oauth_create_email_placeholder": "Email", - "auth_oauth_create_submit": "Join the pack!", - "auth_oauth_create_legal": "By pressing Join the pack, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}.", + "auth_oauth_create_submit": "Join the pack! ♡", + "auth_oauth_create_legal": "By pressing Join the pack, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}. ♡", "auth_oauth_error_breadcrumb": "Login oopsie", - "auth_oauth_error_title": "Uh oh, login oopsie!", + "auth_oauth_error_title": "Uh oh, login oopsie! *blushes*", "auth_oauth_error_back": "Back to login", "auth_reset_breadcrumb": "Reset pawsword", "auth_reset_title": "Reset pawsword", "auth_reset_checking": "Sniffing your reset link...", - "auth_reset_choose_password": "Pick a shiny new pawsword~", + "auth_reset_choose_password": "Pick a shiny new pawsword~ ✨", "auth_reset_invalid": "This reset link is broken or too old... *whimpers*", "auth_reset_password_label": "New pawsword", "auth_reset_password_placeholder": "New pawsword", @@ -118,20 +119,20 @@ "auth_reset_confirm_placeholder": "New pawsword again", "auth_reset_submit": "Reset pawsword!", "auth_reset_request_new": "Fetch a new reset link", - "auth_reset_toast_success": "Pawsword reset! Don't forget it this time, silly~", + "auth_reset_toast_success": "Pawsword reset! Don't forget it this time, silly~ hehe", "auth_signup_breadcrumb": "Join the pack", "auth_signup_toast_created_mail": "Yay, account made! Check your email to verify it~", "auth_signup_toast_created_no_mail": "Yay, account made! You can log in right away~", - "auth_signup_dialog_title": "Welcome to the pack!! Thank you for joining! ❤️ *nuzzles*", + "auth_signup_dialog_title": "Welcome to the pack!! Thank you for joining! ❤️ *nuzzles and twirls*", "auth_signup_dialog_created_mail": "Your account is made! 🎉 Check your email to verify it~", "auth_signup_dialog_verify_then_login": "Once your email is verified, you can hop right in!", - "auth_signup_dialog_created_no_mail": "Your account is made and ready to go! 🎉", + "auth_signup_dialog_created_no_mail": "Your account is made and ready to go! 🎉 *happy wiggle*", "auth_signup_dialog_login_now": "You can log in right now~", - "auth_signup_title": "Join the pack!", + "auth_signup_title": "Join the pack! ♡", "auth_signup_loading_methods": "Sniffing out ways to sign up...", "auth_signup_using_email": "Signing up with email~", - "auth_signup_choose_method": "How do you wanna join the pack?", + "auth_signup_choose_method": "How do you wanna join the pack, cutie?", "auth_signup_back": "Back", "auth_signup_username_label": "Username", "auth_signup_username_placeholder": "Fluffy McShockface", @@ -141,18 +142,18 @@ "auth_signup_password_placeholder": "Pawsword", "auth_signup_confirm_label": "Pawsword again", "auth_signup_confirm_placeholder": "Pawsword again", - "auth_signup_submit": "Join the pack!", - "auth_signup_signin_prompt": "Already in the pack? {#signin}Log in{/signin}", + "auth_signup_submit": "Join the pack! ♡", + "auth_signup_signin_prompt": "Already in the pack? {#signin}Log in~{/signin}", "auth_signup_or": "Or", "auth_signup_with_email": "Join with email", - "auth_signup_legal": "By pressing Join the pack, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}.", + "auth_signup_legal": "By pressing Join the pack, you pinky-paw promise to follow our {#tos}Terms of Service{/tos} and our {#privacy}Privacy Policy{/privacy}. ♡", "auth_verify_breadcrumb": "Verify email", "auth_verify_title": "Verify your email", "auth_verify_verifying": "Sniffing your email address...", - "auth_verify_success": "Your email is verified! *happy bouncing*", + "auth_verify_success": "Your email is verified! *happy bouncing* ✨", "auth_verify_failed": "This verification link is broken or too old... *whimpers*", - "auth_verify_missing_token": "The verification token wandered off somewhere...", + "auth_verify_missing_token": "The verification token wandered off somewhere... *pouts*", "auth_verify_continue": "Onward to login!", "auth_verify_toast_success": "Email verified! *wags*" } From 2cd1f78d398665dddc8e7f3388b1c8226071d523 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 9 Nov 2026 19:56:11 +0100 Subject: [PATCH 10/38] feat(i18n): translate the shockers pages and control modules Covers the own, shared, logs and edit pages, the add-shocker dialog, the shocker menu and the control and pause components. Control type names go through a shared controlTypeLabel helper so the buttons and the logs table use the same words. Shocker counts use plural messages. --- messages/de.json | 113 ++++++++++++++++- messages/en.json | 113 ++++++++++++++++- messages/es.json | 113 ++++++++++++++++- messages/et.json | 113 ++++++++++++++++- messages/fi.json | 113 ++++++++++++++++- messages/fr.json | 113 ++++++++++++++++- messages/nb.json | 113 ++++++++++++++++- messages/sv.json | 113 ++++++++++++++++- messages/uwu.json | 116 +++++++++++++++++- .../ClassicControlModule.svelte | 10 +- .../ControlModules/LiveButton.svelte | 13 +- .../ControlModules/LiveControlModule.svelte | 10 +- .../PublicShareClassicControlModule.svelte | 9 +- .../SharedShockerControlModule.svelte | 11 +- .../ControlModules/ShockerCard.svelte | 3 +- .../ControlModules/SimpleControlHeader.svelte | 15 ++- .../dialogs/dialog-shocker-add.svelte | 25 ++-- .../ControlModules/impl/ActionButtons.svelte | 3 +- .../ControlModules/impl/CircleSlider.svelte | 3 +- .../ControlModules/impl/LimitsDisplay.svelte | 5 +- .../ControlModules/impl/LiveSlider.svelte | 3 +- .../ControlModules/impl/PauseOverlay.svelte | 7 +- .../ControlModules/impl/ShockerMenu.svelte | 25 ++-- .../impl/ShockerPauseButton.svelte | 9 +- .../components/utils/MultiPauseToggle.svelte | 3 +- src/lib/components/utils/PauseToggle.svelte | 3 +- src/lib/i18n/control-type.ts | 15 +++ .../[shockerId=guid]/edit/+page.svelte | 53 ++++---- src/routes/(app)/shockers/logs/+page.svelte | 49 +++++--- src/routes/(app)/shockers/own/+page.svelte | 67 +++++----- src/routes/(app)/shockers/shared/+page.svelte | 19 +-- 31 files changed, 1232 insertions(+), 148 deletions(-) create mode 100644 src/lib/i18n/control-type.ts diff --git a/messages/de.json b/messages/de.json index 9f446cd8..854e17bd 100644 --- a/messages/de.json +++ b/messages/de.json @@ -155,5 +155,116 @@ "auth_verify_failed": "Dieser Bestätigungslink ist ungültig oder abgelaufen", "auth_verify_missing_token": "Bestätigungstoken fehlt", "auth_verify_continue": "Weiter zur Anmeldung", - "auth_verify_toast_success": "E-Mail-Adresse bestätigt" + "auth_verify_toast_success": "E-Mail-Adresse bestätigt", + + "common_cancel": "Abbrechen", + "common_delete": "Löschen", + "common_edit": "Bearbeiten", + "common_share": "Teilen", + "common_copy_id": "ID kopieren", + "common_id_copied": "ID in die Zwischenablage kopiert", + "common_open_menu": "Menü öffnen", + "common_refresh": "Aktualisieren", + "common_settings": "Einstellungen", + "common_clear": "Zurücksetzen", + "common_online": "Online", + "common_offline": "Offline", + "common_paused": "Pausiert", + "common_resume": "Fortsetzen", + "common_resuming": "Wird fortgesetzt...", + "common_coming_soon": "(Demnächst)", + "common_save_changes": "Änderungen speichern", + "common_danger_zone": "Gefahrenbereich", + "common_name": "Name", + "control_intensity": "Intensität", + "control_duration": "Dauer", + "control_max_intensity": "Max. Intensität", + "control_max_duration": "Max. Dauer", + "control_vibration": "Vibration", + "control_value": "Wert", + "control_live_intensity": "Live-Intensität", + "control_type_stop": "Stopp", + "control_type_shock": "Schock", + "control_type_vibrate": "Vibrieren", + "control_type_sound": "Ton", + "live_tooltip_active": "Live über {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Verbinde...", + "live_tooltip_connect": "Mit Live-Steuerung verbinden", + "live_tooltip_hub_start": "Strg+Klick startet den ganzen Hub", + "live_tooltip_hub_stop": "Strg+Klick stoppt den ganzen Hub", + "pause_resume_shocker": "Shocker fortsetzen", + "pause_pause_shocker": "Shocker pausieren", + "pause_resume_all": "Alle Shocker fortsetzen", + "pause_pause_all": "Alle Shocker pausieren", + "shocker_label": "Shocker", + "shocker_hub": "Hub", + "shocker_select_hub": "Hub auswählen", + "shocker_rf_id": "RF-ID", + "shocker_rf_id_invalid": "Die RF-ID muss eine ganze Zahl zwischen {min} und {max} sein.", + "shocker_model": "Modell", + "shocker_select_model": "Modell auswählen", + "shocker_name_placeholder": "Shocker-Name", + "shocker_menu_view_logs": "Protokolle anzeigen", + "shocker_delete_title": "Shocker löschen", + "shocker_delete_desc": "Möchtest du „{name}“ wirklich löschen? Dies kann nicht rückgängig gemacht werden.", + "shocker_toast_deleted": "Shocker „{name}“ gelöscht", + "shocker_toast_paused": "Shocker pausiert", + "shocker_toast_resumed": "Shocker fortgesetzt", + "shocker_add_desc": "Registriere einen neuen Shocker an einem deiner Hubs.", + "shocker_add_name_placeholder": "Mein Shocker", + "shockers_title": "Shocker", + "shockers_subtitle": "Alle Shocker, die mit deinen Hubs gekoppelt sind.", + "shockers_loading": "Shocker werden geladen...", + "shockers_toast_refreshed": "Shocker aktualisiert", + "shockers_toast_need_hub": "Du musst einen Hub erstellen, bevor du Shocker hinzufügen kannst.", + "shockers_toast_added": "Shocker hinzugefügt", + "shockers_unknown_module": "Unbekannter Modultyp", + "shockers_add": "Shocker hinzufügen", + "shockers_view_mode": "Ansicht", + "shockers_mode_classic": "Klassisch", + "shockers_mode_rich": "Erweitert", + "shockers_mode_simple": "Einfach", + "shockers_mode_map": "Karte", + "shockers_group_by_hub": "Nach Hub gruppieren", + "shockers_global_limits": "Globale Limits", + "shockers_logs_label": "Protokolle", + "shockers_empty_title": "Noch keine Shocker", + "shockers_empty_no_hubs": "Erstelle zuerst einen Hub und füge dann Shocker hinzu.", + "shockers_empty_has_hubs": "Füge einem deiner Hubs einen Shocker hinzu, um loszulegen.", + "shockers_create_hub": "Hub erstellen", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} Shocker", "countPlural=other": "{count} Shocker" } + } + ], + "shared_shockers_title": "Geteilte Shocker", + "shared_shockers_subtitle": "Shocker, die andere Nutzer mit dir geteilt haben.", + "shared_shockers_empty_title": "Bisher wurden keine Shocker mit dir geteilt.", + "shared_shockers_empty_desc": "Sobald jemand Shocker mit dir teilt, erscheinen sie hier.", + "shocker_logs_title": "Shocker-Protokolle", + "shocker_logs_subtitle": "Alle Steuerbefehle, die an deine Shocker gesendet wurden.", + "shocker_logs_live_title": "Neue Einträge erscheinen in Echtzeit", + "shocker_logs_paused_title": "Live-Updates pausieren beim Sortieren oder auf anderen Seiten als der ersten", + "shocker_logs_live": "Live", + "shocker_logs_col_time": "Zeit", + "shocker_logs_col_type": "Typ", + "shocker_logs_col_by": "Von", + "shocker_logs_filter_label": "Nach Shocker filtern", + "shocker_logs_filter_placeholder": "Shocker suchen...", + "shocker_logs_filter_all": "Alle Shocker", + "shocker_logs_filter_none": "Keine passenden Shocker", + "shocker_edit_title": "Shocker bearbeiten", + "shocker_edit_subtitle": "Ändere die Details dieses Shockers oder entferne ihn.", + "shocker_edit_back": "Zurück zu den Shockern", + "shocker_edit_load_failed": "Shocker konnte nicht geladen werden.", + "shocker_edit_loading": "Shocker wird geladen...", + "shocker_edit_details_title": "Shocker-Details", + "shocker_edit_details_desc": "Aktualisiere Name, RF-ID und Modell dieses Shockers.", + "shocker_edit_pause_state": "Pausenstatus", + "shocker_edit_is_paused": "Shocker ist pausiert", + "shocker_edit_is_active": "Shocker ist aktiv", + "shocker_edit_toast_updated": "Shocker aktualisiert", + "shocker_edit_delete_desc": "Lösche diesen Shocker dauerhaft. Dies kann nicht rückgängig gemacht werden." } diff --git a/messages/en.json b/messages/en.json index 5b2c91e4..42f12486 100644 --- a/messages/en.json +++ b/messages/en.json @@ -155,5 +155,116 @@ "auth_verify_failed": "This verification link is invalid or has expired", "auth_verify_missing_token": "Missing verification token", "auth_verify_continue": "Continue to login", - "auth_verify_toast_success": "Email verified" + "auth_verify_toast_success": "Email verified", + + "common_cancel": "Cancel", + "common_delete": "Delete", + "common_edit": "Edit", + "common_share": "Share", + "common_copy_id": "Copy ID", + "common_id_copied": "ID copied to clipboard", + "common_open_menu": "Open menu", + "common_refresh": "Refresh", + "common_settings": "Settings", + "common_clear": "Clear", + "common_online": "Online", + "common_offline": "Offline", + "common_paused": "Paused", + "common_resume": "Resume", + "common_resuming": "Resuming...", + "common_coming_soon": "(Coming soon)", + "common_save_changes": "Save Changes", + "common_danger_zone": "Danger Zone", + "common_name": "Name", + "control_intensity": "Intensity", + "control_duration": "Duration", + "control_max_intensity": "Max Intensity", + "control_max_duration": "Max Duration", + "control_vibration": "Vibration", + "control_value": "Value", + "control_live_intensity": "Live intensity", + "control_type_stop": "Stop", + "control_type_shock": "Shock", + "control_type_vibrate": "Vibrate", + "control_type_sound": "Sound", + "live_tooltip_active": "Live via {gateway} ({country}), {latency}ms", + "live_tooltip_connecting": "Connecting...", + "live_tooltip_connect": "Connect to Live Control", + "live_tooltip_hub_start": "Ctrl+click to start the whole hub", + "live_tooltip_hub_stop": "Ctrl+click to stop the whole hub", + "pause_resume_shocker": "Resume shocker", + "pause_pause_shocker": "Pause shocker", + "pause_resume_all": "Resume all shockers", + "pause_pause_all": "Pause all shockers", + "shocker_label": "Shocker", + "shocker_hub": "Hub", + "shocker_select_hub": "Select hub", + "shocker_rf_id": "RF ID", + "shocker_rf_id_invalid": "RF ID must be a whole number between {min} and {max}.", + "shocker_model": "Model", + "shocker_select_model": "Select model", + "shocker_name_placeholder": "Shocker name", + "shocker_menu_view_logs": "View Logs", + "shocker_delete_title": "Delete Shocker", + "shocker_delete_desc": "Are you sure you want to delete \"{name}\"? This action cannot be undone.", + "shocker_toast_deleted": "Shocker \"{name}\" deleted", + "shocker_toast_paused": "Shocker paused", + "shocker_toast_resumed": "Shocker resumed", + "shocker_add_desc": "Register a new shocker to one of your hubs.", + "shocker_add_name_placeholder": "My Shocker", + "shockers_title": "Shockers", + "shockers_subtitle": "Every shocker paired to your hubs.", + "shockers_loading": "Loading shockers...", + "shockers_toast_refreshed": "Shockers refreshed", + "shockers_toast_need_hub": "You need to create a hub before adding shockers.", + "shockers_toast_added": "Shocker added", + "shockers_unknown_module": "Unknown module type", + "shockers_add": "Add Shocker", + "shockers_view_mode": "View mode", + "shockers_mode_classic": "Classic", + "shockers_mode_rich": "Rich", + "shockers_mode_simple": "Simple", + "shockers_mode_map": "Map", + "shockers_group_by_hub": "Group by Hub", + "shockers_global_limits": "Global Limits", + "shockers_logs_label": "Logs", + "shockers_empty_title": "No shockers yet", + "shockers_empty_no_hubs": "Create a hub first, then add shockers to it.", + "shockers_empty_has_hubs": "Add a shocker to one of your hubs to get started.", + "shockers_create_hub": "Create a Hub", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockers" } + } + ], + "shared_shockers_title": "Shared Shockers", + "shared_shockers_subtitle": "Shockers other users have shared with you.", + "shared_shockers_empty_title": "No shockers have been shared with you yet.", + "shared_shockers_empty_desc": "When someone shares their shockers with you, they'll appear here.", + "shocker_logs_title": "Shocker Logs", + "shocker_logs_subtitle": "Every control command sent to your shockers.", + "shocker_logs_live_title": "New logs appear in real time", + "shocker_logs_paused_title": "Live updates pause when sorting or viewing a page other than the first", + "shocker_logs_live": "Live", + "shocker_logs_col_time": "Time", + "shocker_logs_col_type": "Type", + "shocker_logs_col_by": "By", + "shocker_logs_filter_label": "Filter by shocker", + "shocker_logs_filter_placeholder": "Search shockers...", + "shocker_logs_filter_all": "All shockers", + "shocker_logs_filter_none": "No matching shockers", + "shocker_edit_title": "Edit Shocker", + "shocker_edit_subtitle": "Change this shocker's details or remove it.", + "shocker_edit_back": "Back to Shockers", + "shocker_edit_load_failed": "Failed to load shocker.", + "shocker_edit_loading": "Loading shocker...", + "shocker_edit_details_title": "Shocker Details", + "shocker_edit_details_desc": "Update the name, RF ID, and model for this shocker.", + "shocker_edit_pause_state": "Pause State", + "shocker_edit_is_paused": "Shocker is paused", + "shocker_edit_is_active": "Shocker is active", + "shocker_edit_toast_updated": "Shocker updated", + "shocker_edit_delete_desc": "Permanently delete this shocker. This action cannot be undone." } diff --git a/messages/es.json b/messages/es.json index 9c56af3d..c5ecf78b 100644 --- a/messages/es.json +++ b/messages/es.json @@ -155,5 +155,116 @@ "auth_verify_failed": "Este enlace de verificación no es válido o ha caducado", "auth_verify_missing_token": "Falta el token de verificación", "auth_verify_continue": "Continuar al inicio de sesión", - "auth_verify_toast_success": "Correo electrónico verificado" + "auth_verify_toast_success": "Correo electrónico verificado", + + "common_cancel": "Cancelar", + "common_delete": "Eliminar", + "common_edit": "Editar", + "common_share": "Compartir", + "common_copy_id": "Copiar ID", + "common_id_copied": "ID copiado al portapapeles", + "common_open_menu": "Abrir menú", + "common_refresh": "Actualizar", + "common_settings": "Ajustes", + "common_clear": "Borrar", + "common_online": "En línea", + "common_offline": "Sin conexión", + "common_paused": "En pausa", + "common_resume": "Reanudar", + "common_resuming": "Reanudando...", + "common_coming_soon": "(Próximamente)", + "common_save_changes": "Guardar cambios", + "common_danger_zone": "Zona de peligro", + "common_name": "Nombre", + "control_intensity": "Intensidad", + "control_duration": "Duración", + "control_max_intensity": "Intensidad máx.", + "control_max_duration": "Duración máx.", + "control_vibration": "Vibración", + "control_value": "Valor", + "control_live_intensity": "Intensidad en directo", + "control_type_stop": "Detener", + "control_type_shock": "Descarga", + "control_type_vibrate": "Vibrar", + "control_type_sound": "Sonido", + "live_tooltip_active": "En directo vía {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Conectando...", + "live_tooltip_connect": "Conectar al control en directo", + "live_tooltip_hub_start": "Ctrl+clic para iniciar todo el hub", + "live_tooltip_hub_stop": "Ctrl+clic para detener todo el hub", + "pause_resume_shocker": "Reanudar shocker", + "pause_pause_shocker": "Pausar shocker", + "pause_resume_all": "Reanudar todos los shockers", + "pause_pause_all": "Pausar todos los shockers", + "shocker_label": "Shocker", + "shocker_hub": "Hub", + "shocker_select_hub": "Seleccionar hub", + "shocker_rf_id": "ID de RF", + "shocker_rf_id_invalid": "El ID de RF debe ser un número entero entre {min} y {max}.", + "shocker_model": "Modelo", + "shocker_select_model": "Seleccionar modelo", + "shocker_name_placeholder": "Nombre del shocker", + "shocker_menu_view_logs": "Ver registros", + "shocker_delete_title": "Eliminar shocker", + "shocker_delete_desc": "¿Seguro que quieres eliminar «{name}»? Esta acción no se puede deshacer.", + "shocker_toast_deleted": "Shocker «{name}» eliminado", + "shocker_toast_paused": "Shocker en pausa", + "shocker_toast_resumed": "Shocker reanudado", + "shocker_add_desc": "Registra un nuevo shocker en uno de tus hubs.", + "shocker_add_name_placeholder": "Mi shocker", + "shockers_title": "Shockers", + "shockers_subtitle": "Todos los shockers vinculados a tus hubs.", + "shockers_loading": "Cargando shockers...", + "shockers_toast_refreshed": "Shockers actualizados", + "shockers_toast_need_hub": "Necesitas crear un hub antes de añadir shockers.", + "shockers_toast_added": "Shocker añadido", + "shockers_unknown_module": "Tipo de módulo desconocido", + "shockers_add": "Añadir shocker", + "shockers_view_mode": "Modo de vista", + "shockers_mode_classic": "Clásico", + "shockers_mode_rich": "Completo", + "shockers_mode_simple": "Sencillo", + "shockers_mode_map": "Mapa", + "shockers_group_by_hub": "Agrupar por hub", + "shockers_global_limits": "Límites globales", + "shockers_logs_label": "Registros", + "shockers_empty_title": "Aún no hay shockers", + "shockers_empty_no_hubs": "Primero crea un hub y luego añade shockers.", + "shockers_empty_has_hubs": "Añade un shocker a uno de tus hubs para empezar.", + "shockers_create_hub": "Crear un hub", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockers" } + } + ], + "shared_shockers_title": "Shockers compartidos", + "shared_shockers_subtitle": "Shockers que otros usuarios han compartido contigo.", + "shared_shockers_empty_title": "Todavía nadie ha compartido shockers contigo.", + "shared_shockers_empty_desc": "Cuando alguien comparta sus shockers contigo, aparecerán aquí.", + "shocker_logs_title": "Registros de shockers", + "shocker_logs_subtitle": "Todos los comandos de control enviados a tus shockers.", + "shocker_logs_live_title": "Los nuevos registros aparecen en tiempo real", + "shocker_logs_paused_title": "Las actualizaciones en directo se pausan al ordenar o al ver una página distinta de la primera", + "shocker_logs_live": "En directo", + "shocker_logs_col_time": "Hora", + "shocker_logs_col_type": "Tipo", + "shocker_logs_col_by": "Por", + "shocker_logs_filter_label": "Filtrar por shocker", + "shocker_logs_filter_placeholder": "Buscar shockers...", + "shocker_logs_filter_all": "Todos los shockers", + "shocker_logs_filter_none": "No hay shockers que coincidan", + "shocker_edit_title": "Editar shocker", + "shocker_edit_subtitle": "Cambia los detalles de este shocker o elimínalo.", + "shocker_edit_back": "Volver a los shockers", + "shocker_edit_load_failed": "No se pudo cargar el shocker.", + "shocker_edit_loading": "Cargando shocker...", + "shocker_edit_details_title": "Detalles del shocker", + "shocker_edit_details_desc": "Actualiza el nombre, el ID de RF y el modelo de este shocker.", + "shocker_edit_pause_state": "Estado de pausa", + "shocker_edit_is_paused": "El shocker está en pausa", + "shocker_edit_is_active": "El shocker está activo", + "shocker_edit_toast_updated": "Shocker actualizado", + "shocker_edit_delete_desc": "Elimina este shocker de forma permanente. Esta acción no se puede deshacer." } diff --git a/messages/et.json b/messages/et.json index dc03c30d..aebec5e4 100644 --- a/messages/et.json +++ b/messages/et.json @@ -155,5 +155,116 @@ "auth_verify_failed": "See kinnituslink on kehtetu või aegunud", "auth_verify_missing_token": "Kinnitusvõti puudub", "auth_verify_continue": "Jätka sisselogimisega", - "auth_verify_toast_success": "E-posti aadress on kinnitatud" + "auth_verify_toast_success": "E-posti aadress on kinnitatud", + + "common_cancel": "Tühista", + "common_delete": "Kustuta", + "common_edit": "Muuda", + "common_share": "Jaga", + "common_copy_id": "Kopeeri ID", + "common_id_copied": "ID kopeeriti lõikelauale", + "common_open_menu": "Ava menüü", + "common_refresh": "Värskenda", + "common_settings": "Seaded", + "common_clear": "Tühjenda", + "common_online": "Võrgus", + "common_offline": "Võrguühenduseta", + "common_paused": "Peatatud", + "common_resume": "Jätka", + "common_resuming": "Jätkamine...", + "common_coming_soon": "(Tulekul)", + "common_save_changes": "Salvesta muudatused", + "common_danger_zone": "Ohutsoon", + "common_name": "Nimi", + "control_intensity": "Intensiivsus", + "control_duration": "Kestus", + "control_max_intensity": "Maksimaalne intensiivsus", + "control_max_duration": "Maksimaalne kestus", + "control_vibration": "Vibratsioon", + "control_value": "Väärtus", + "control_live_intensity": "Reaalajas intensiivsus", + "control_type_stop": "Peata", + "control_type_shock": "Šokk", + "control_type_vibrate": "Vibreeri", + "control_type_sound": "Heli", + "live_tooltip_active": "Reaalajas läbi {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Ühendamine...", + "live_tooltip_connect": "Ühenda reaalajas juhtimisega", + "live_tooltip_hub_start": "Ctrl+klõps käivitab kogu jaoturi", + "live_tooltip_hub_stop": "Ctrl+klõps peatab kogu jaoturi", + "pause_resume_shocker": "Jätka shockeriga", + "pause_pause_shocker": "Peata shocker", + "pause_resume_all": "Jätka kõigi shockeritega", + "pause_pause_all": "Peata kõik shockerid", + "shocker_label": "Shocker", + "shocker_hub": "Jaotur", + "shocker_select_hub": "Vali jaotur", + "shocker_rf_id": "RF-ID", + "shocker_rf_id_invalid": "RF-ID peab olema täisarv vahemikus {min}–{max}.", + "shocker_model": "Mudel", + "shocker_select_model": "Vali mudel", + "shocker_name_placeholder": "Shockeri nimi", + "shocker_menu_view_logs": "Vaata logisid", + "shocker_delete_title": "Kustuta shocker", + "shocker_delete_desc": "Kas soovid kindlasti „{name}“ kustutada? Seda toimingut ei saa tagasi võtta.", + "shocker_toast_deleted": "Shocker „{name}“ kustutati", + "shocker_toast_paused": "Shocker peatati", + "shocker_toast_resumed": "Shocker jätkab", + "shocker_add_desc": "Registreeri uus shocker mõnele oma jaoturile.", + "shocker_add_name_placeholder": "Minu shocker", + "shockers_title": "Shockerid", + "shockers_subtitle": "Kõik sinu jaoturitega seotud shockerid.", + "shockers_loading": "Shockerite laadimine...", + "shockers_toast_refreshed": "Shockerid värskendati", + "shockers_toast_need_hub": "Enne shockerite lisamist pead looma jaoturi.", + "shockers_toast_added": "Shocker lisati", + "shockers_unknown_module": "Tundmatu mooduli tüüp", + "shockers_add": "Lisa shocker", + "shockers_view_mode": "Vaade", + "shockers_mode_classic": "Klassikaline", + "shockers_mode_rich": "Laiendatud", + "shockers_mode_simple": "Lihtne", + "shockers_mode_map": "Kaart", + "shockers_group_by_hub": "Rühmita jaoturi järgi", + "shockers_global_limits": "Üldised piirangud", + "shockers_logs_label": "Logid", + "shockers_empty_title": "Shockereid pole veel", + "shockers_empty_no_hubs": "Loo esmalt jaotur ja lisa sellele siis shockerid.", + "shockers_empty_has_hubs": "Alustamiseks lisa shocker mõnele oma jaoturile.", + "shockers_create_hub": "Loo jaotur", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockerit" } + } + ], + "shared_shockers_title": "Jagatud shockerid", + "shared_shockers_subtitle": "Teiste kasutajate sinuga jagatud shockerid.", + "shared_shockers_empty_title": "Sinuga pole veel ühtegi shockerit jagatud.", + "shared_shockers_empty_desc": "Kui keegi jagab sinuga oma shockereid, ilmuvad need siia.", + "shocker_logs_title": "Shockerite logid", + "shocker_logs_subtitle": "Kõik sinu shockeritele saadetud juhtkäsud.", + "shocker_logs_live_title": "Uued logid ilmuvad reaalajas", + "shocker_logs_paused_title": "Reaalajas uuendused peatuvad sortimisel või muu kui esimese lehe vaatamisel", + "shocker_logs_live": "Reaalajas", + "shocker_logs_col_time": "Aeg", + "shocker_logs_col_type": "Tüüp", + "shocker_logs_col_by": "Saatja", + "shocker_logs_filter_label": "Filtreeri shockeri järgi", + "shocker_logs_filter_placeholder": "Otsi shockereid...", + "shocker_logs_filter_all": "Kõik shockerid", + "shocker_logs_filter_none": "Sobivaid shockereid pole", + "shocker_edit_title": "Muuda shockerit", + "shocker_edit_subtitle": "Muuda selle shockeri andmeid või eemalda see.", + "shocker_edit_back": "Tagasi shockerite juurde", + "shocker_edit_load_failed": "Shockeri laadimine ebaõnnestus.", + "shocker_edit_loading": "Shockeri laadimine...", + "shocker_edit_details_title": "Shockeri andmed", + "shocker_edit_details_desc": "Uuenda selle shockeri nime, RF-ID-d ja mudelit.", + "shocker_edit_pause_state": "Pausi olek", + "shocker_edit_is_paused": "Shocker on peatatud", + "shocker_edit_is_active": "Shocker on aktiivne", + "shocker_edit_toast_updated": "Shocker uuendati", + "shocker_edit_delete_desc": "Kustuta see shocker jäädavalt. Seda toimingut ei saa tagasi võtta." } diff --git a/messages/fi.json b/messages/fi.json index 63ead96b..b779a6ec 100644 --- a/messages/fi.json +++ b/messages/fi.json @@ -155,5 +155,116 @@ "auth_verify_failed": "Tämä vahvistuslinkki on virheellinen tai vanhentunut", "auth_verify_missing_token": "Vahvistustunnus puuttuu", "auth_verify_continue": "Jatka kirjautumiseen", - "auth_verify_toast_success": "Sähköpostiosoite vahvistettu" + "auth_verify_toast_success": "Sähköpostiosoite vahvistettu", + + "common_cancel": "Peruuta", + "common_delete": "Poista", + "common_edit": "Muokkaa", + "common_share": "Jaa", + "common_copy_id": "Kopioi tunniste", + "common_id_copied": "Tunniste kopioitu leikepöydälle", + "common_open_menu": "Avaa valikko", + "common_refresh": "Päivitä", + "common_settings": "Asetukset", + "common_clear": "Tyhjennä", + "common_online": "Verkossa", + "common_offline": "Ei verkossa", + "common_paused": "Tauotettu", + "common_resume": "Jatka", + "common_resuming": "Jatketaan...", + "common_coming_soon": "(Tulossa pian)", + "common_save_changes": "Tallenna muutokset", + "common_danger_zone": "Vaaravyöhyke", + "common_name": "Nimi", + "control_intensity": "Voimakkuus", + "control_duration": "Kesto", + "control_max_intensity": "Suurin voimakkuus", + "control_max_duration": "Pisin kesto", + "control_vibration": "Värinä", + "control_value": "Arvo", + "control_live_intensity": "Reaaliaikainen voimakkuus", + "control_type_stop": "Pysäytä", + "control_type_shock": "Isku", + "control_type_vibrate": "Väristä", + "control_type_sound": "Ääni", + "live_tooltip_active": "Reaaliaikainen yhteys: {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Yhdistetään...", + "live_tooltip_connect": "Yhdistä reaaliaikaiseen ohjaukseen", + "live_tooltip_hub_start": "Ctrl+napsautus käynnistää koko hubin", + "live_tooltip_hub_stop": "Ctrl+napsautus pysäyttää koko hubin", + "pause_resume_shocker": "Jatka shockeria", + "pause_pause_shocker": "Tauota shocker", + "pause_resume_all": "Jatka kaikkia shockereita", + "pause_pause_all": "Tauota kaikki shockerit", + "shocker_label": "Shocker", + "shocker_hub": "Hubi", + "shocker_select_hub": "Valitse hubi", + "shocker_rf_id": "RF-tunniste", + "shocker_rf_id_invalid": "RF-tunnisteen on oltava kokonaisluku väliltä {min}–{max}.", + "shocker_model": "Malli", + "shocker_select_model": "Valitse malli", + "shocker_name_placeholder": "Shockerin nimi", + "shocker_menu_view_logs": "Näytä lokit", + "shocker_delete_title": "Poista shocker", + "shocker_delete_desc": "Haluatko varmasti poistaa shockerin ”{name}”? Toimintoa ei voi kumota.", + "shocker_toast_deleted": "Shocker ”{name}” poistettu", + "shocker_toast_paused": "Shocker tauotettu", + "shocker_toast_resumed": "Shockeria jatkettu", + "shocker_add_desc": "Rekisteröi uusi shocker johonkin hubeistasi.", + "shocker_add_name_placeholder": "Oma shocker", + "shockers_title": "Shockerit", + "shockers_subtitle": "Kaikki hubeihisi yhdistetyt shockerit.", + "shockers_loading": "Ladataan shockereita...", + "shockers_toast_refreshed": "Shockerit päivitetty", + "shockers_toast_need_hub": "Luo hubi ennen kuin lisäät shockereita.", + "shockers_toast_added": "Shocker lisätty", + "shockers_unknown_module": "Tuntematon moduulityyppi", + "shockers_add": "Lisää shocker", + "shockers_view_mode": "Näkymä", + "shockers_mode_classic": "Perinteinen", + "shockers_mode_rich": "Laaja", + "shockers_mode_simple": "Yksinkertainen", + "shockers_mode_map": "Kartta", + "shockers_group_by_hub": "Ryhmittele hubin mukaan", + "shockers_global_limits": "Yleiset rajat", + "shockers_logs_label": "Lokit", + "shockers_empty_title": "Ei vielä shockereita", + "shockers_empty_no_hubs": "Luo ensin hubi ja lisää siihen sitten shockereita.", + "shockers_empty_has_hubs": "Aloita lisäämällä shocker johonkin hubeistasi.", + "shockers_create_hub": "Luo hubi", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockeria" } + } + ], + "shared_shockers_title": "Jaetut shockerit", + "shared_shockers_subtitle": "Muiden käyttäjien kanssasi jakamat shockerit.", + "shared_shockers_empty_title": "Kanssasi ei ole vielä jaettu shockereita.", + "shared_shockers_empty_desc": "Kun joku jakaa shockerinsa kanssasi, ne näkyvät täällä.", + "shocker_logs_title": "Shockerien lokit", + "shocker_logs_subtitle": "Kaikki shockereillesi lähetetyt ohjauskomennot.", + "shocker_logs_live_title": "Uudet lokit näkyvät reaaliajassa", + "shocker_logs_paused_title": "Reaaliaikaiset päivitykset tauotetaan, kun lajittelet tai katsot muuta kuin ensimmäistä sivua", + "shocker_logs_live": "Reaaliaikainen", + "shocker_logs_col_time": "Aika", + "shocker_logs_col_type": "Tyyppi", + "shocker_logs_col_by": "Lähettäjä", + "shocker_logs_filter_label": "Suodata shockerin mukaan", + "shocker_logs_filter_placeholder": "Hae shockereita...", + "shocker_logs_filter_all": "Kaikki shockerit", + "shocker_logs_filter_none": "Ei vastaavia shockereita", + "shocker_edit_title": "Muokkaa shockeria", + "shocker_edit_subtitle": "Muuta tämän shockerin tietoja tai poista se.", + "shocker_edit_back": "Takaisin shockereihin", + "shocker_edit_load_failed": "Shockerin lataaminen epäonnistui.", + "shocker_edit_loading": "Ladataan shockeria...", + "shocker_edit_details_title": "Shockerin tiedot", + "shocker_edit_details_desc": "Päivitä tämän shockerin nimi, RF-tunniste ja malli.", + "shocker_edit_pause_state": "Taukotila", + "shocker_edit_is_paused": "Shocker on tauotettu", + "shocker_edit_is_active": "Shocker on aktiivinen", + "shocker_edit_toast_updated": "Shocker päivitetty", + "shocker_edit_delete_desc": "Poista tämä shocker pysyvästi. Toimintoa ei voi kumota." } diff --git a/messages/fr.json b/messages/fr.json index f2508e27..826355fa 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -155,5 +155,116 @@ "auth_verify_failed": "Ce lien de vérification est invalide ou a expiré", "auth_verify_missing_token": "Jeton de vérification manquant", "auth_verify_continue": "Continuer vers la connexion", - "auth_verify_toast_success": "Adresse e-mail vérifiée" + "auth_verify_toast_success": "Adresse e-mail vérifiée", + + "common_cancel": "Annuler", + "common_delete": "Supprimer", + "common_edit": "Modifier", + "common_share": "Partager", + "common_copy_id": "Copier l'ID", + "common_id_copied": "ID copié dans le presse-papiers", + "common_open_menu": "Ouvrir le menu", + "common_refresh": "Actualiser", + "common_settings": "Paramètres", + "common_clear": "Effacer", + "common_online": "En ligne", + "common_offline": "Hors ligne", + "common_paused": "En pause", + "common_resume": "Reprendre", + "common_resuming": "Reprise...", + "common_coming_soon": "(Bientôt disponible)", + "common_save_changes": "Enregistrer les modifications", + "common_danger_zone": "Zone de danger", + "common_name": "Nom", + "control_intensity": "Intensité", + "control_duration": "Durée", + "control_max_intensity": "Intensité max.", + "control_max_duration": "Durée max.", + "control_vibration": "Vibration", + "control_value": "Valeur", + "control_live_intensity": "Intensité en direct", + "control_type_stop": "Arrêt", + "control_type_shock": "Choc", + "control_type_vibrate": "Vibrer", + "control_type_sound": "Son", + "live_tooltip_active": "En direct via {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Connexion...", + "live_tooltip_connect": "Se connecter au contrôle en direct", + "live_tooltip_hub_start": "Ctrl+clic pour démarrer tout le hub", + "live_tooltip_hub_stop": "Ctrl+clic pour arrêter tout le hub", + "pause_resume_shocker": "Reprendre le shocker", + "pause_pause_shocker": "Mettre le shocker en pause", + "pause_resume_all": "Reprendre tous les shockers", + "pause_pause_all": "Mettre tous les shockers en pause", + "shocker_label": "Shocker", + "shocker_hub": "Hub", + "shocker_select_hub": "Sélectionner un hub", + "shocker_rf_id": "ID RF", + "shocker_rf_id_invalid": "L'ID RF doit être un nombre entier compris entre {min} et {max}.", + "shocker_model": "Modèle", + "shocker_select_model": "Sélectionner un modèle", + "shocker_name_placeholder": "Nom du shocker", + "shocker_menu_view_logs": "Voir les journaux", + "shocker_delete_title": "Supprimer le shocker", + "shocker_delete_desc": "Voulez-vous vraiment supprimer « {name} » ? Cette action est irréversible.", + "shocker_toast_deleted": "Shocker « {name} » supprimé", + "shocker_toast_paused": "Shocker mis en pause", + "shocker_toast_resumed": "Shocker repris", + "shocker_add_desc": "Enregistrez un nouveau shocker sur l'un de vos hubs.", + "shocker_add_name_placeholder": "Mon shocker", + "shockers_title": "Shockers", + "shockers_subtitle": "Tous les shockers associés à vos hubs.", + "shockers_loading": "Chargement des shockers...", + "shockers_toast_refreshed": "Shockers actualisés", + "shockers_toast_need_hub": "Vous devez créer un hub avant d'ajouter des shockers.", + "shockers_toast_added": "Shocker ajouté", + "shockers_unknown_module": "Type de module inconnu", + "shockers_add": "Ajouter un shocker", + "shockers_view_mode": "Mode d'affichage", + "shockers_mode_classic": "Classique", + "shockers_mode_rich": "Complet", + "shockers_mode_simple": "Simple", + "shockers_mode_map": "Carte", + "shockers_group_by_hub": "Grouper par hub", + "shockers_global_limits": "Limites globales", + "shockers_logs_label": "Journaux", + "shockers_empty_title": "Aucun shocker pour l'instant", + "shockers_empty_no_hubs": "Créez d'abord un hub, puis ajoutez-y des shockers.", + "shockers_empty_has_hubs": "Ajoutez un shocker à l'un de vos hubs pour commencer.", + "shockers_create_hub": "Créer un hub", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockers" } + } + ], + "shared_shockers_title": "Shockers partagés", + "shared_shockers_subtitle": "Les shockers que d'autres utilisateurs ont partagés avec vous.", + "shared_shockers_empty_title": "Aucun shocker n'a encore été partagé avec vous.", + "shared_shockers_empty_desc": "Lorsque quelqu'un partagera ses shockers avec vous, ils apparaîtront ici.", + "shocker_logs_title": "Journaux des shockers", + "shocker_logs_subtitle": "Toutes les commandes envoyées à vos shockers.", + "shocker_logs_live_title": "Les nouveaux journaux apparaissent en temps réel", + "shocker_logs_paused_title": "Les mises à jour en direct sont suspendues lors d'un tri ou sur une autre page que la première", + "shocker_logs_live": "En direct", + "shocker_logs_col_time": "Heure", + "shocker_logs_col_type": "Type", + "shocker_logs_col_by": "Par", + "shocker_logs_filter_label": "Filtrer par shocker", + "shocker_logs_filter_placeholder": "Rechercher des shockers...", + "shocker_logs_filter_all": "Tous les shockers", + "shocker_logs_filter_none": "Aucun shocker correspondant", + "shocker_edit_title": "Modifier le shocker", + "shocker_edit_subtitle": "Modifiez les informations de ce shocker ou supprimez-le.", + "shocker_edit_back": "Retour aux shockers", + "shocker_edit_load_failed": "Impossible de charger le shocker.", + "shocker_edit_loading": "Chargement du shocker...", + "shocker_edit_details_title": "Informations du shocker", + "shocker_edit_details_desc": "Mettez à jour le nom, l'ID RF et le modèle de ce shocker.", + "shocker_edit_pause_state": "État de pause", + "shocker_edit_is_paused": "Le shocker est en pause", + "shocker_edit_is_active": "Le shocker est actif", + "shocker_edit_toast_updated": "Shocker mis à jour", + "shocker_edit_delete_desc": "Supprimez définitivement ce shocker. Cette action est irréversible." } diff --git a/messages/nb.json b/messages/nb.json index b60be81c..9fa9f50d 100644 --- a/messages/nb.json +++ b/messages/nb.json @@ -155,5 +155,116 @@ "auth_verify_failed": "Denne bekreftelseslenken er ugyldig eller utløpt", "auth_verify_missing_token": "Bekreftelsestoken mangler", "auth_verify_continue": "Fortsett til innlogging", - "auth_verify_toast_success": "E-postadressen er bekreftet" + "auth_verify_toast_success": "E-postadressen er bekreftet", + + "common_cancel": "Avbryt", + "common_delete": "Slett", + "common_edit": "Rediger", + "common_share": "Del", + "common_copy_id": "Kopier ID", + "common_id_copied": "ID kopiert til utklippstavlen", + "common_open_menu": "Åpne meny", + "common_refresh": "Oppdater", + "common_settings": "Innstillinger", + "common_clear": "Tøm", + "common_online": "Tilkoblet", + "common_offline": "Frakoblet", + "common_paused": "Satt på pause", + "common_resume": "Fortsett", + "common_resuming": "Fortsetter...", + "common_coming_soon": "(Kommer snart)", + "common_save_changes": "Lagre endringer", + "common_danger_zone": "Faresone", + "common_name": "Navn", + "control_intensity": "Intensitet", + "control_duration": "Varighet", + "control_max_intensity": "Maks. intensitet", + "control_max_duration": "Maks. varighet", + "control_vibration": "Vibrasjon", + "control_value": "Verdi", + "control_live_intensity": "Live-intensitet", + "control_type_stop": "Stopp", + "control_type_shock": "Støt", + "control_type_vibrate": "Vibrer", + "control_type_sound": "Lyd", + "live_tooltip_active": "Live via {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Kobler til...", + "live_tooltip_connect": "Koble til live-kontroll", + "live_tooltip_hub_start": "Ctrl+klikk for å starte hele huben", + "live_tooltip_hub_stop": "Ctrl+klikk for å stoppe hele huben", + "pause_resume_shocker": "Fortsett shockeren", + "pause_pause_shocker": "Sett shockeren på pause", + "pause_resume_all": "Fortsett alle shockere", + "pause_pause_all": "Sett alle shockere på pause", + "shocker_label": "Shocker", + "shocker_hub": "Hub", + "shocker_select_hub": "Velg hub", + "shocker_rf_id": "RF-ID", + "shocker_rf_id_invalid": "RF-ID må være et heltall mellom {min} og {max}.", + "shocker_model": "Modell", + "shocker_select_model": "Velg modell", + "shocker_name_placeholder": "Navn på shocker", + "shocker_menu_view_logs": "Vis logger", + "shocker_delete_title": "Slett shocker", + "shocker_delete_desc": "Er du sikker på at du vil slette «{name}»? Dette kan ikke angres.", + "shocker_toast_deleted": "Shockeren «{name}» er slettet", + "shocker_toast_paused": "Shockeren er satt på pause", + "shocker_toast_resumed": "Shockeren fortsetter", + "shocker_add_desc": "Registrer en ny shocker på en av hubene dine.", + "shocker_add_name_placeholder": "Min shocker", + "shockers_title": "Shockere", + "shockers_subtitle": "Alle shockere som er paret med hubene dine.", + "shockers_loading": "Laster inn shockere...", + "shockers_toast_refreshed": "Shockerne er oppdatert", + "shockers_toast_need_hub": "Du må opprette en hub før du kan legge til shockere.", + "shockers_toast_added": "Shocker lagt til", + "shockers_unknown_module": "Ukjent modultype", + "shockers_add": "Legg til shocker", + "shockers_view_mode": "Visningsmodus", + "shockers_mode_classic": "Klassisk", + "shockers_mode_rich": "Utvidet", + "shockers_mode_simple": "Enkel", + "shockers_mode_map": "Kart", + "shockers_group_by_hub": "Grupper etter hub", + "shockers_global_limits": "Globale grenser", + "shockers_logs_label": "Logger", + "shockers_empty_title": "Ingen shockere ennå", + "shockers_empty_no_hubs": "Opprett en hub først, og legg deretter til shockere.", + "shockers_empty_has_hubs": "Legg til en shocker i en av hubene dine for å komme i gang.", + "shockers_create_hub": "Opprett en hub", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockere" } + } + ], + "shared_shockers_title": "Delte shockere", + "shared_shockers_subtitle": "Shockere som andre brukere har delt med deg.", + "shared_shockers_empty_title": "Ingen shockere er delt med deg ennå.", + "shared_shockers_empty_desc": "Når noen deler shockerne sine med deg, dukker de opp her.", + "shocker_logs_title": "Shocker-logger", + "shocker_logs_subtitle": "Alle kontrollkommandoer som er sendt til shockerne dine.", + "shocker_logs_live_title": "Nye logger vises i sanntid", + "shocker_logs_paused_title": "Direkteoppdateringer settes på pause når du sorterer eller ser på en annen side enn den første", + "shocker_logs_live": "Direkte", + "shocker_logs_col_time": "Tidspunkt", + "shocker_logs_col_type": "Type", + "shocker_logs_col_by": "Av", + "shocker_logs_filter_label": "Filtrer etter shocker", + "shocker_logs_filter_placeholder": "Søk etter shockere...", + "shocker_logs_filter_all": "Alle shockere", + "shocker_logs_filter_none": "Ingen samsvarende shockere", + "shocker_edit_title": "Rediger shocker", + "shocker_edit_subtitle": "Endre detaljene for denne shockeren, eller fjern den.", + "shocker_edit_back": "Tilbake til shockerne", + "shocker_edit_load_failed": "Kunne ikke laste inn shockeren.", + "shocker_edit_loading": "Laster inn shockeren...", + "shocker_edit_details_title": "Shockerdetaljer", + "shocker_edit_details_desc": "Oppdater navn, RF-ID og modell for denne shockeren.", + "shocker_edit_pause_state": "Pausestatus", + "shocker_edit_is_paused": "Shockeren er satt på pause", + "shocker_edit_is_active": "Shockeren er aktiv", + "shocker_edit_toast_updated": "Shockeren er oppdatert", + "shocker_edit_delete_desc": "Slett denne shockeren permanent. Dette kan ikke angres." } diff --git a/messages/sv.json b/messages/sv.json index 204e79d1..d1a46891 100644 --- a/messages/sv.json +++ b/messages/sv.json @@ -155,5 +155,116 @@ "auth_verify_failed": "Den här verifieringslänken är ogiltig eller har gått ut", "auth_verify_missing_token": "Verifieringstoken saknas", "auth_verify_continue": "Fortsätt till inloggningen", - "auth_verify_toast_success": "E-postadressen har verifierats" + "auth_verify_toast_success": "E-postadressen har verifierats", + + "common_cancel": "Avbryt", + "common_delete": "Ta bort", + "common_edit": "Redigera", + "common_share": "Dela", + "common_copy_id": "Kopiera ID", + "common_id_copied": "ID kopierat till urklipp", + "common_open_menu": "Öppna meny", + "common_refresh": "Uppdatera", + "common_settings": "Inställningar", + "common_clear": "Rensa", + "common_online": "Ansluten", + "common_offline": "Frånkopplad", + "common_paused": "Pausad", + "common_resume": "Återuppta", + "common_resuming": "Återupptar...", + "common_coming_soon": "(Kommer snart)", + "common_save_changes": "Spara ändringar", + "common_danger_zone": "Farozon", + "common_name": "Namn", + "control_intensity": "Intensitet", + "control_duration": "Varaktighet", + "control_max_intensity": "Max. intensitet", + "control_max_duration": "Max. varaktighet", + "control_vibration": "Vibration", + "control_value": "Värde", + "control_live_intensity": "Live-intensitet", + "control_type_stop": "Stopp", + "control_type_shock": "Stöt", + "control_type_vibrate": "Vibrera", + "control_type_sound": "Ljud", + "live_tooltip_active": "Live via {gateway} ({country}), {latency} ms", + "live_tooltip_connecting": "Ansluter...", + "live_tooltip_connect": "Anslut till livestyrning", + "live_tooltip_hub_start": "Ctrl+klicka för att starta hela hubben", + "live_tooltip_hub_stop": "Ctrl+klicka för att stoppa hela hubben", + "pause_resume_shocker": "Återuppta shockern", + "pause_pause_shocker": "Pausa shockern", + "pause_resume_all": "Återuppta alla shockers", + "pause_pause_all": "Pausa alla shockers", + "shocker_label": "Shocker", + "shocker_hub": "Hubb", + "shocker_select_hub": "Välj hubb", + "shocker_rf_id": "RF-ID", + "shocker_rf_id_invalid": "RF-ID måste vara ett heltal mellan {min} och {max}.", + "shocker_model": "Modell", + "shocker_select_model": "Välj modell", + "shocker_name_placeholder": "Shockerns namn", + "shocker_menu_view_logs": "Visa loggar", + "shocker_delete_title": "Ta bort shocker", + "shocker_delete_desc": "Vill du verkligen ta bort ”{name}”? Det går inte att ångra.", + "shocker_toast_deleted": "Shockern ”{name}” har tagits bort", + "shocker_toast_paused": "Shockern har pausats", + "shocker_toast_resumed": "Shockern har återupptagits", + "shocker_add_desc": "Registrera en ny shocker på en av dina hubbar.", + "shocker_add_name_placeholder": "Min shocker", + "shockers_title": "Shockers", + "shockers_subtitle": "Alla shockers som är parkopplade med dina hubbar.", + "shockers_loading": "Läser in shockers...", + "shockers_toast_refreshed": "Shockers har uppdaterats", + "shockers_toast_need_hub": "Du måste skapa en hubb innan du kan lägga till shockers.", + "shockers_toast_added": "Shockern har lagts till", + "shockers_unknown_module": "Okänd modultyp", + "shockers_add": "Lägg till shocker", + "shockers_view_mode": "Visningsläge", + "shockers_mode_classic": "Klassisk", + "shockers_mode_rich": "Utökad", + "shockers_mode_simple": "Enkel", + "shockers_mode_map": "Karta", + "shockers_group_by_hub": "Gruppera efter hubb", + "shockers_global_limits": "Globala gränser", + "shockers_logs_label": "Loggar", + "shockers_empty_title": "Inga shockers än", + "shockers_empty_no_hubs": "Skapa en hubb först och lägg sedan till shockers.", + "shockers_empty_has_hubs": "Lägg till en shocker i en av dina hubbar för att komma igång.", + "shockers_create_hub": "Skapa en hubb", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { "countPlural=one": "{count} shocker", "countPlural=other": "{count} shockers" } + } + ], + "shared_shockers_title": "Delade shockers", + "shared_shockers_subtitle": "Shockers som andra användare har delat med dig.", + "shared_shockers_empty_title": "Inga shockers har delats med dig än.", + "shared_shockers_empty_desc": "När någon delar sina shockers med dig visas de här.", + "shocker_logs_title": "Shocker-loggar", + "shocker_logs_subtitle": "Alla styrkommandon som har skickats till dina shockers.", + "shocker_logs_live_title": "Nya loggar visas i realtid", + "shocker_logs_paused_title": "Liveuppdateringar pausas när du sorterar eller visar en annan sida än den första", + "shocker_logs_live": "Live", + "shocker_logs_col_time": "Tid", + "shocker_logs_col_type": "Typ", + "shocker_logs_col_by": "Av", + "shocker_logs_filter_label": "Filtrera efter shocker", + "shocker_logs_filter_placeholder": "Sök shockers...", + "shocker_logs_filter_all": "Alla shockers", + "shocker_logs_filter_none": "Inga matchande shockers", + "shocker_edit_title": "Redigera shocker", + "shocker_edit_subtitle": "Ändra uppgifterna för den här shockern eller ta bort den.", + "shocker_edit_back": "Tillbaka till shockers", + "shocker_edit_load_failed": "Det gick inte att läsa in shockern.", + "shocker_edit_loading": "Läser in shockern...", + "shocker_edit_details_title": "Shockeruppgifter", + "shocker_edit_details_desc": "Uppdatera namn, RF-ID och modell för den här shockern.", + "shocker_edit_pause_state": "Pausläge", + "shocker_edit_is_paused": "Shockern är pausad", + "shocker_edit_is_active": "Shockern är aktiv", + "shocker_edit_toast_updated": "Shockern har uppdaterats", + "shocker_edit_delete_desc": "Ta bort den här shockern permanent. Det går inte att ångra." } diff --git a/messages/uwu.json b/messages/uwu.json index 960573ef..62cc7f5b 100644 --- a/messages/uwu.json +++ b/messages/uwu.json @@ -155,5 +155,119 @@ "auth_verify_failed": "This verification link is broken or too old... *whimpers*", "auth_verify_missing_token": "The verification token wandered off somewhere... *pouts*", "auth_verify_continue": "Onward to login!", - "auth_verify_toast_success": "Email verified! *wags*" + "auth_verify_toast_success": "Email verified! *wags*", + + "common_cancel": "Nevermind~", + "common_delete": "Delete *gulp*", + "common_edit": "Edit~", + "common_share": "Share with fwiends ♡", + "common_copy_id": "Copy ID~", + "common_id_copied": "ID tucked into your clipboard~ ♡", + "common_open_menu": "Open menu~", + "common_refresh": "Freshen up~", + "common_settings": "Settings~", + "common_clear": "Clear it all~", + "common_online": "Awake ✨", + "common_offline": "Sleepy 💤", + "common_paused": "Napping~", + "common_resume": "Wake up!", + "common_resuming": "Waking up... *yawn*", + "common_coming_soon": "(Coming soon, hehe~)", + "common_save_changes": "Save it~ ♡", + "common_danger_zone": "The spooky zone ⚠️", + "common_name": "Name~", + "control_intensity": "Oomph", + "control_duration": "How long~", + "control_max_intensity": "Max oomph", + "control_max_duration": "Longest it can go~", + "control_vibration": "Bzzz", + "control_value": "Number~", + "control_live_intensity": "Live oomph", + "control_type_stop": "Stop!", + "control_type_shock": "Zap ⚡", + "control_type_vibrate": "Bzzz", + "control_type_sound": "Beep boop", + "live_tooltip_active": "Live through {gateway} ({country}), {latency}ms, zoom zoom~", + "live_tooltip_connecting": "Connecting... *paws crossed*", + "live_tooltip_connect": "Go live, cutie!", + "live_tooltip_hub_start": "Ctrl+click to wake up the whole hubby~", + "live_tooltip_hub_stop": "Ctrl+click to tuck the whole hubby in~", + "pause_resume_shocker": "Wake up this zappy fwiend", + "pause_pause_shocker": "Let this zappy fwiend nap", + "pause_resume_all": "Wake up every zappy fwiend", + "pause_pause_all": "Nap time for every zappy fwiend", + "shocker_label": "Zappy fwiend", + "shocker_hub": "Hubby", + "shocker_select_hub": "Pick a hubby~", + "shocker_rf_id": "RF ID", + "shocker_rf_id_invalid": "RF ID has to be a whole number from {min} to {max}, silly~", + "shocker_model": "Model~", + "shocker_select_model": "Pick a model~", + "shocker_name_placeholder": "What's its name?", + "shocker_menu_view_logs": "Peek at the logs", + "shocker_delete_title": "Say bye-bye to this shocker", + "shocker_delete_desc": "Are you suuure you wanna say bye-bye to \"{name}\"? It can never come back... *sniffles*", + "shocker_toast_deleted": "\"{name}\" waved bye-bye~ *sad tail droop*", + "shocker_toast_paused": "Shhh, it's napping now~", + "shocker_toast_resumed": "It's awake and ready! *twirls*", + "shocker_add_desc": "Bring a new zappy fwiend home to one of your hubbies~", + "shocker_add_name_placeholder": "Sir Zaps-a-Lot", + "shockers_title": "Zappy fwiends ⚡", + "shockers_subtitle": "All the zappy fwiends cuddled up with your hubbies~", + "shockers_loading": "Rounding up the zappy fwiends...", + "shockers_toast_refreshed": "All fresh and fluffy! ✨", + "shockers_toast_need_hub": "Your zappy fwiends need a hubby to live in first! Make one~", + "shockers_toast_added": "A new zappy fwiend joined the pack! ♡", + "shockers_unknown_module": "Huh? I don't know this module... *tilts head*", + "shockers_add": "Add a zappy fwiend", + "shockers_view_mode": "How it looks~", + "shockers_mode_classic": "Classic~", + "shockers_mode_rich": "Fancy ✨", + "shockers_mode_simple": "Simple~", + "shockers_mode_map": "Map 🗺️", + "shockers_group_by_hub": "Snuggle by hubby", + "shockers_global_limits": "Global limits", + "shockers_logs_label": "Logs~", + "shockers_empty_title": "No zappy fwiends yet... *pouts*", + "shockers_empty_no_hubs": "Make a hubby first, then invite some zappy fwiends over~", + "shockers_empty_has_hubs": "Give one of your hubbies a zappy fwiend to get started!", + "shockers_create_hub": "Make a hubby!", + "shockers_count": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count} zappy fwiend", + "countPlural=other": "{count} zappy fwiends" + } + } + ], + "shared_shockers_title": "Zappy fwiends I borrowed hehe", + "shared_shockers_subtitle": "Zappy fwiends other cuties are letting you borrow~", + "shared_shockers_empty_title": "Nobody's shared their zappy fwiends with you yet... *sniff*", + "shared_shockers_empty_desc": "When a cutie shares theirs, they'll pop up right here! ♡", + "shocker_logs_title": "Zappy diary 📖", + "shocker_logs_subtitle": "Every little boop your zappy fwiends got~", + "shocker_logs_live_title": "New boops show up the moment they happen!", + "shocker_logs_paused_title": "Live updates nap while you sort or peek past the first page~", + "shocker_logs_live": "Live ✨", + "shocker_logs_col_time": "When", + "shocker_logs_col_type": "What", + "shocker_logs_col_by": "Who", + "shocker_logs_filter_label": "Pick a zappy fwiend", + "shocker_logs_filter_placeholder": "Sniff out a shocker...", + "shocker_logs_filter_all": "Every zappy fwiend", + "shocker_logs_filter_none": "Couldn't sniff any out... *sad*", + "shocker_edit_title": "Groom this zappy fwiend", + "shocker_edit_subtitle": "Brush its fur, give it a new name, or say goodbye~", + "shocker_edit_back": "Back to the zappy fwiends", + "shocker_edit_load_failed": "Couldn't fetch this shocker... *whimpers*", + "shocker_edit_loading": "Fetching your zappy fwiend...", + "shocker_edit_details_title": "All about this cutie", + "shocker_edit_details_desc": "Change its name, RF ID, and model~", + "shocker_edit_pause_state": "Nap status", + "shocker_edit_is_paused": "Taking a little nap 💤", + "shocker_edit_is_active": "Wide awake and ready! ⚡", + "shocker_edit_toast_updated": "All groomed and pretty! ✨", + "shocker_edit_delete_desc": "Say goodbye to this shocker forever. There's no undo... *sniffles*" } diff --git a/src/lib/components/ControlModules/ClassicControlModule.svelte b/src/lib/components/ControlModules/ClassicControlModule.svelte index a44ba81e..350f03e2 100644 --- a/src/lib/components/ControlModules/ClassicControlModule.svelte +++ b/src/lib/components/ControlModules/ClassicControlModule.svelte @@ -1,4 +1,5 @@ - Add shocker - Register a new shocker to one of your hubs. + {m.shockers_add()} + {m.shocker_add_desc()}

- + - RF ID + {m.shocker_rf_id()} - Model + {m.shocker_model()} - {data.model ?? 'Select model'} + {data.model ?? m.shocker_select_model()} @@ -91,10 +96,10 @@ - Hub + {m.shocker_hub()} - {hubs.find(([id]) => id === data.device)?.[1].name ?? 'Select hub'} + {hubs.find(([id]) => id === data.device)?.[1].name ?? m.shocker_select_hub()} @@ -107,6 +112,6 @@
- - + + diff --git a/src/lib/components/ControlModules/impl/ActionButtons.svelte b/src/lib/components/ControlModules/impl/ActionButtons.svelte index 1fea15b5..31b55f1f 100644 --- a/src/lib/components/ControlModules/impl/ActionButtons.svelte +++ b/src/lib/components/ControlModules/impl/ActionButtons.svelte @@ -13,6 +13,7 @@
- + {maxIntensity}% - + {formatDurationSeconds(maxDuration)} diff --git a/src/lib/components/ControlModules/impl/LiveSlider.svelte b/src/lib/components/ControlModules/impl/LiveSlider.svelte index bbb7cdd1..ff3b0908 100644 --- a/src/lib/components/ControlModules/impl/LiveSlider.svelte +++ b/src/lib/components/ControlModules/impl/LiveSlider.svelte @@ -1,4 +1,5 @@ - + {#if loadError} - + {:else if !shocker} - + {:else}
- Shocker Details - Update the name, RF ID, and model for this shocker. + {m.shocker_edit_details_title()} + {m.shocker_edit_details_desc()} - + - RF ID + {m.shocker_rf_id()} {#if !rfIdValid} - RF ID must be a whole number between {RfIdMin} and {RfIdMax}. + {m.shocker_rf_id_invalid({ min: RfIdMin, max: RfIdMax })} {/if} - Model + {m.shocker_model()} - {model ?? 'Select model'} + {model ?? m.shocker_select_model()} @@ -155,7 +160,7 @@ - Pause State + {m.shocker_edit_pause_state()}
{}} /> - {shocker.isPaused ? 'Shocker is paused' : 'Shocker is active'} + {shocker.isPaused ? m.shocker_edit_is_paused() : m.shocker_edit_is_active()}
@@ -172,22 +177,22 @@
- Danger Zone + {m.common_danger_zone()} - Permanently delete this shocker. This action cannot be undone. + {m.shocker_edit_delete_desc()} diff --git a/src/routes/(app)/shockers/logs/+page.svelte b/src/routes/(app)/shockers/logs/+page.svelte index 7a8e8761..ea16f3b2 100644 --- a/src/routes/(app)/shockers/logs/+page.svelte +++ b/src/routes/(app)/shockers/logs/+page.svelte @@ -1,4 +1,5 @@ - + {#if liveUpdatesActive} @@ -235,7 +242,7 @@ : 'bg-muted-foreground'}" > - {liveUpdatesActive ? 'Live' : 'Paused'} + {liveUpdatesActive ? m.shocker_logs_live() : m.common_paused()}
@@ -244,14 +251,16 @@
{#if selectedShockerIds.length > 0} - + {/if}
diff --git a/src/routes/(app)/shockers/own/+page.svelte b/src/routes/(app)/shockers/own/+page.svelte index 11d0fb94..74795b48 100644 --- a/src/routes/(app)/shockers/own/+page.svelte +++ b/src/routes/(app)/shockers/own/+page.svelte @@ -1,4 +1,5 @@ @@ -38,11 +41,11 @@

- Troubleshooter + {m.terminal_help_title()}

- Work through these steps in order. Each one rules out a common cause. + {m.terminal_help_desc()} @@ -50,25 +53,30 @@ +{#snippet code({ children }: { children?: Snippet })} + {@render children?.()} +{/snippet} + +{#snippet key({ children }: { children?: Snippet })} + {@render children?.()} +{/snippet} + {#snippet cableBody()}

- Many USB-C and micro-USB cables are charge-only and don't carry data. Swap to a known-good data - cable (one you've used to transfer files or connect other devices) and reconnect the hub. + {m.terminal_help_cable_body()}

{/snippet} {#snippet portBody()}

- Plug the device directly into a USB port on your computer. Avoid USB hubs, docking stations, and - front-panel ports if possible; these are common sources of power and signal issues. If you're on - a desktop, try a port on the back of the machine. + {m.terminal_help_port_body()}

{/snippet} {#snippet driverBody()}
  1. -

    Download the CP210x driver:

    +

    {m.terminal_help_driver_download()}

  2. -
  3. Extract the contents of the zip file to a folder on your computer.
  4. +
  5. {m.terminal_help_driver_extract()}
  6. - Open the folder and run CP210xVCPInstaller_x64.exe. Follow the prompts to - install. +
  7. -
  8. Reconnect the device.
  9. +
  10. {m.terminal_help_driver_reconnect()}
{/snippet} {#snippet manualBody()} -

If the driver installer ran but the device still doesn't show up:

+

{m.terminal_help_manual_intro()}

  1. - Open Device Manager (press Win + X and select - Device Manager). +
  2. - Under Ports (COM & LPT), look for an entry like: +
    • Silicon Labs CP210x USB to UART Bridge
    • Silicon Labs CP210x USB to UART Bridge (COM...)
    - If it's missing, the device isn't being detected by Windows at all; revisit the cable and port steps. + {m.terminal_help_manual_missing()}
  3. - Right-click the device and pick Update Driver. +
  4. - Choose Browse my computer for drivers and select the folder you extracted the driver - zip into. Make sure Include subfolders is checked. +
  5. - On success, the entry should now read Silicon Labs CP210x USB to UART Bridge. +
{/snippet} @@ -118,18 +123,17 @@ {#snippet discordBody()}

- Still stuck after all of the above? Hop into our Discord server and ask in the - # 💭┃support-chat channel. Please include: +

    -
  • your operating system and browser
  • -
  • the device you're using (board model, where you got it)
  • -
  • what you've already tried from the previous steps
  • -
  • any error messages or relevant terminal output
  • +
  • {m.terminal_help_discord_os()}
  • +
  • {m.terminal_help_discord_device()}
  • +
  • {m.terminal_help_discord_tried()}
  • +
  • {m.terminal_help_discord_errors()}
{/snippet} diff --git a/src/routes/terminal/ModePicker.svelte b/src/routes/terminal/ModePicker.svelte index 13a938b1..29ce8e3b 100644 --- a/src/routes/terminal/ModePicker.svelte +++ b/src/routes/terminal/ModePicker.svelte @@ -1,4 +1,5 @@ @@ -373,7 +377,7 @@