Add i18n support: per-locale catalogs, URL-driven locale, translated core and examples
CI / full-gate (push) Successful in 2m37s
CI / full-gate (push) Successful in 2m37s
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
// The core catalog: every string the host itself renders, and the baseline every other locale
|
||||
// is checked against at boot (its keys and its plural/string kinds are the contract). Add a key
|
||||
// here first, then to each sv-SE.ts et al — a locale that drifts stops the boot.
|
||||
//
|
||||
// Values are raw text; views escape them. A value carrying markup is rendered with <%- %> and must
|
||||
// never interpolate untrusted data (see README → Translating).
|
||||
|
||||
const messages = {
|
||||
"auth.continue": "Continue",
|
||||
// Kratos labels its own form fields; these translate the ones the built-in identity schema uses,
|
||||
// keyed on the input name. A deployment's extra traits keep Kratos' label until a plugin covers them.
|
||||
"auth.field.email": "Email",
|
||||
"auth.field.identifier": "Email",
|
||||
"auth.field.password": "Password",
|
||||
"auth.field.traits.email": "Email",
|
||||
"auth.forgotPassword": "Forgot password?",
|
||||
"auth.login.altLabel": "Create one",
|
||||
"auth.login.altText": "Don't have an account?",
|
||||
"auth.login.sub": "Welcome back. Enter your details to continue.",
|
||||
"auth.login.title": "Sign in",
|
||||
"auth.recovery.altLabel": "Sign in",
|
||||
"auth.recovery.altText": "Remembered it?",
|
||||
"auth.recovery.back": "Back to sign in",
|
||||
"auth.recovery.sub": "Enter your email and we'll send you a recovery code.",
|
||||
"auth.recovery.title": "Reset password",
|
||||
"auth.registration.altLabel": "Sign in",
|
||||
"auth.registration.altText": "Already have an account?",
|
||||
"auth.registration.sub": "Get started — it only takes a minute.",
|
||||
"auth.registration.title": "Create account",
|
||||
"auth.settings.sub": "Update your account details.",
|
||||
"auth.settings.title": "Account settings",
|
||||
"auth.sso.divider": "or",
|
||||
"auth.sso.label": "Single sign-on options",
|
||||
"auth.verification.back": "Back to sign in",
|
||||
"auth.verification.sub": "Enter the code we sent you.",
|
||||
"auth.verification.title": "Verify your email",
|
||||
|
||||
"brand.sub": "Console",
|
||||
|
||||
"consent.allow": "Allow",
|
||||
"consent.deny": "Deny",
|
||||
"consent.notYou": "Not you?",
|
||||
"consent.scope.email": "Your email address",
|
||||
"consent.scope.offline_access": "Stay signed in (offline access)",
|
||||
"consent.scope.openid": "Verify your identity",
|
||||
"consent.scope.profile": "Your basic profile (name)",
|
||||
"consent.signedInAs": "Signed in as",
|
||||
"consent.sub": "{{client}} wants access to your account.",
|
||||
"consent.title": "Authorize {{client}}",
|
||||
|
||||
"dashboard.starter.browse": "Browse the example plugin",
|
||||
"dashboard.starter.intro":
|
||||
"This is the built-in <code>/dashboard</code> — the gated home shown to a signed-in user. It's a placeholder so a fresh clone has something here; it holds no real data.",
|
||||
"dashboard.starter.reference":
|
||||
"See the plugin contract in <code>docs/plugin-contract.md</code> (the landing-pages section) and the bundled <code>plugins/scheduling/</code> reference.",
|
||||
"dashboard.starter.replace":
|
||||
"Replace it from a plugin: export a <code>dashboard</code> handler from your plugin's manifest and it owns this page, rendered against your own views with the native app shell (the same menu you see now) via <code>ctx.chrome</code>.",
|
||||
"dashboard.starter.title": "Starter dashboard",
|
||||
"dashboard.title": "Dashboard",
|
||||
|
||||
"error.403.body": "You don't have permission to view that (403).",
|
||||
"error.403.docTitle": "Forbidden",
|
||||
"error.403.title": "Access denied",
|
||||
"error.404.body": "We couldn't find that page (404).",
|
||||
"error.404.docTitle": "Not found",
|
||||
"error.404.title": "Page not found",
|
||||
"error.500.body": "An unexpected error occurred on our end (500).",
|
||||
"error.500.docTitle": "Server error",
|
||||
"error.500.title": "Something went wrong",
|
||||
"error.503.body": "We can't reach the identity service right now (503). Please try again in a moment.",
|
||||
"error.503.docTitle": "Sign-in unavailable",
|
||||
"error.503.title": "Sign-in is temporarily unavailable",
|
||||
"error.backHome": "Back home",
|
||||
"error.backToSignIn": "Back to sign in",
|
||||
"error.flow.body": "We couldn't complete that sign-in step. It may have expired or been opened twice — please try again.",
|
||||
"error.flow.docTitle": "Sign-in error",
|
||||
"error.flow.title": "Something went wrong",
|
||||
"error.reference": "Reference: {{id}}",
|
||||
"error.tryAgain": "Try again",
|
||||
|
||||
"field.optional": "Optional",
|
||||
|
||||
"filter.applied": "Applied",
|
||||
"filter.appliedFilters": "Applied filters",
|
||||
"filter.apply": "Apply filters",
|
||||
"filter.clearAll": "Clear all",
|
||||
"filter.dateRange": "Date range",
|
||||
"filter.from": "From",
|
||||
"filter.label": "Filter",
|
||||
"filter.remove": "Remove {{label}} filter",
|
||||
"filter.reset": "Reset",
|
||||
"filter.search": "Search",
|
||||
"filter.to": "To",
|
||||
"filter.toSeparator": "to",
|
||||
|
||||
// Kratos writes the auth flow's own text and returns it with a stable numeric id. A key here
|
||||
// replaces that text; anything unmapped renders Kratos' English as-is (README → Translating).
|
||||
// Ids not in this list are deliberate: 1070002 is Kratos' generic identity-trait label — it is
|
||||
// "Email" on the login form and "First name" on a registration form with that trait, so it can
|
||||
// only be translated per field (auth.field.* above), never per id.
|
||||
"kratos.1010022": "Sign in with password",
|
||||
"kratos.1040001": "Sign up",
|
||||
"kratos.1060003":
|
||||
"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.",
|
||||
"kratos.1070008": "Resend code",
|
||||
"kratos.1070009": "Continue",
|
||||
"kratos.1070010": "Recovery code",
|
||||
"kratos.1070011": "Verification code",
|
||||
"kratos.1080003":
|
||||
"An email containing a verification code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.",
|
||||
"kratos.4000002": "This field is required.",
|
||||
"kratos.4000006": "The credentials are invalid. Check for typos in your email address or password.",
|
||||
"kratos.4000007": "An account with that email address already exists.",
|
||||
"kratos.4060006": "That recovery code is invalid or has already been used. Please try again.",
|
||||
"kratos.4070006": "That verification code is invalid or has already been used. Please try again.",
|
||||
|
||||
"landing.dashboard": "Go to your dashboard",
|
||||
"landing.lead":
|
||||
"{{brand}} is a self-hostable foundation for admin and operational UIs — sign-in, a config-driven menu, and a server-rendered, zero-JS design system. You add the domain-specific screens by dropping in plugin folders.",
|
||||
"landing.register": "Create account",
|
||||
"landing.signIn": "Log in",
|
||||
"landing.title": "Operational web apps, without the boilerplate.",
|
||||
|
||||
"locale.label": "Language",
|
||||
|
||||
"nav.dashboard": "Dashboard",
|
||||
|
||||
"oauth.consentExpired": "This authorization request has expired. Please start again from the application you were signing in to.",
|
||||
"oauth.loginExpired": "This sign-in request has expired. Please start again from the application you were signing in to.",
|
||||
"oauth.logoutExpired": "This logout request has expired. Please start again from the application you were signing out of.",
|
||||
|
||||
"pagination.go": "Go",
|
||||
"pagination.label": "Pagination",
|
||||
"pagination.next": "Next page",
|
||||
"pagination.of": "of",
|
||||
"pagination.previous": "Previous page",
|
||||
"pagination.rows": "Rows",
|
||||
|
||||
"shell.breadcrumb": "Breadcrumb",
|
||||
"shell.closeMenu": "Close menu",
|
||||
"shell.guest": "Guest",
|
||||
"shell.mainNav": "Main navigation",
|
||||
"shell.openMenu": "Open menu",
|
||||
"shell.preferences": "Preferences",
|
||||
"shell.profile": "Profile",
|
||||
"shell.settings": "Settings",
|
||||
"shell.sidebar": "Primary",
|
||||
"shell.signedInAs": "Signed in as {{name}}",
|
||||
"shell.signIn": "Sign in",
|
||||
"shell.signOut": "Sign out",
|
||||
"shell.skipToContent": "Skip to content",
|
||||
"shell.toggleSection": "Toggle {{label}}",
|
||||
|
||||
"table.actions": "Actions",
|
||||
"table.empty": "Nothing here yet.",
|
||||
"table.row": "row",
|
||||
"table.rowActions": "Row actions for {{name}}",
|
||||
"table.select": "Select {{name}}",
|
||||
"table.selectAll": "Select all rows",
|
||||
|
||||
"theme.auto": "Auto",
|
||||
"theme.dark": "Dark",
|
||||
"theme.label": "Color theme",
|
||||
"theme.light": "Light",
|
||||
};
|
||||
|
||||
// The shape every other core locale is written against: `const messages: CoreMessages = { … }` in
|
||||
// sv-SE.ts et al, so a missing or misspelled key is a type error before the boot check ever runs.
|
||||
export type CoreMessages = typeof messages;
|
||||
|
||||
export default messages;
|
||||
@@ -0,0 +1,155 @@
|
||||
import type { CoreMessages } from "./en-US.ts";
|
||||
|
||||
const messages: CoreMessages = {
|
||||
"auth.continue": "Fortsätt",
|
||||
"auth.field.email": "E-postadress",
|
||||
"auth.field.identifier": "E-postadress",
|
||||
"auth.field.password": "Lösenord",
|
||||
"auth.field.traits.email": "E-postadress",
|
||||
"auth.forgotPassword": "Glömt lösenordet?",
|
||||
"auth.login.altLabel": "Skapa ett",
|
||||
"auth.login.altText": "Har du inget konto?",
|
||||
"auth.login.sub": "Välkommen tillbaka. Fyll i dina uppgifter för att fortsätta.",
|
||||
"auth.login.title": "Logga in",
|
||||
"auth.recovery.altLabel": "Logga in",
|
||||
"auth.recovery.altText": "Kom du på det?",
|
||||
"auth.recovery.back": "Tillbaka till inloggningen",
|
||||
"auth.recovery.sub": "Ange din e-postadress så skickar vi en återställningskod.",
|
||||
"auth.recovery.title": "Återställ lösenord",
|
||||
"auth.registration.altLabel": "Logga in",
|
||||
"auth.registration.altText": "Har du redan ett konto?",
|
||||
"auth.registration.sub": "Kom igång — det tar bara en minut.",
|
||||
"auth.registration.title": "Skapa konto",
|
||||
"auth.settings.sub": "Uppdatera dina kontouppgifter.",
|
||||
"auth.settings.title": "Kontoinställningar",
|
||||
"auth.sso.divider": "eller",
|
||||
"auth.sso.label": "Alternativ för enkel inloggning",
|
||||
"auth.verification.back": "Tillbaka till inloggningen",
|
||||
"auth.verification.sub": "Ange koden vi skickade till dig.",
|
||||
"auth.verification.title": "Verifiera din e-postadress",
|
||||
|
||||
"brand.sub": "Konsol",
|
||||
|
||||
"consent.allow": "Tillåt",
|
||||
"consent.deny": "Neka",
|
||||
"consent.notYou": "Inte du?",
|
||||
"consent.scope.email": "Din e-postadress",
|
||||
"consent.scope.offline_access": "Håll dig inloggad (offlineåtkomst)",
|
||||
"consent.scope.openid": "Verifiera din identitet",
|
||||
"consent.scope.profile": "Din grundläggande profil (namn)",
|
||||
"consent.signedInAs": "Inloggad som",
|
||||
"consent.sub": "{{client}} vill få åtkomst till ditt konto.",
|
||||
"consent.title": "Godkänn {{client}}",
|
||||
|
||||
"dashboard.starter.browse": "Utforska exempelpluginet",
|
||||
"dashboard.starter.intro":
|
||||
"Detta är den inbyggda <code>/dashboard</code> — den inloggade startsidan. Den är en platshållare så att en färsk klon har något här; den innehåller inga riktiga data.",
|
||||
"dashboard.starter.reference":
|
||||
"Se plugin-kontraktet i <code>docs/plugin-contract.md</code> (avsnittet om startsidorna) och referensen <code>plugins/scheduling/</code>.",
|
||||
"dashboard.starter.replace":
|
||||
"Ersätt den från ett plugin: exportera en <code>dashboard</code>-hanterare från pluginets manifest så äger det den här sidan, renderad mot dina egna vyer med appens eget skal (samma meny du ser nu) via <code>ctx.chrome</code>.",
|
||||
"dashboard.starter.title": "Startpanel",
|
||||
"dashboard.title": "Panel",
|
||||
|
||||
"error.403.body": "Du har inte behörighet att se det här (403).",
|
||||
"error.403.docTitle": "Åtkomst nekad",
|
||||
"error.403.title": "Åtkomst nekad",
|
||||
"error.404.body": "Vi hittade inte sidan (404).",
|
||||
"error.404.docTitle": "Sidan finns inte",
|
||||
"error.404.title": "Sidan hittades inte",
|
||||
"error.500.body": "Ett oväntat fel uppstod hos oss (500).",
|
||||
"error.500.docTitle": "Serverfel",
|
||||
"error.500.title": "Något gick fel",
|
||||
"error.503.body": "Vi når inte identitetstjänsten just nu (503). Försök igen om en liten stund.",
|
||||
"error.503.docTitle": "Inloggning otillgänglig",
|
||||
"error.503.title": "Inloggningen är tillfälligt otillgänglig",
|
||||
"error.backHome": "Tillbaka till startsidan",
|
||||
"error.backToSignIn": "Tillbaka till inloggningen",
|
||||
"error.flow.body": "Vi kunde inte slutföra det inloggningssteget. Det kan ha gått ut eller öppnats två gånger — försök igen.",
|
||||
"error.flow.docTitle": "Inloggningsfel",
|
||||
"error.flow.title": "Något gick fel",
|
||||
"error.reference": "Referens: {{id}}",
|
||||
"error.tryAgain": "Försök igen",
|
||||
|
||||
"field.optional": "Frivilligt",
|
||||
|
||||
"filter.applied": "Aktiva",
|
||||
"filter.appliedFilters": "Aktiva filter",
|
||||
"filter.apply": "Använd filter",
|
||||
"filter.clearAll": "Rensa alla",
|
||||
"filter.dateRange": "Datumintervall",
|
||||
"filter.from": "Från",
|
||||
"filter.label": "Filter",
|
||||
"filter.remove": "Ta bort filtret {{label}}",
|
||||
"filter.reset": "Återställ",
|
||||
"filter.search": "Sök",
|
||||
"filter.to": "Till",
|
||||
"filter.toSeparator": "till",
|
||||
|
||||
"kratos.1010022": "Logga in med lösenord",
|
||||
"kratos.1040001": "Skapa konto",
|
||||
"kratos.1060003":
|
||||
"Ett mejl med en återställningskod har skickats till adressen du angav. Har du inte fått något mejl, kontrollera stavningen och att du använder adressen du registrerade dig med.",
|
||||
"kratos.1070008": "Skicka koden igen",
|
||||
"kratos.1070009": "Fortsätt",
|
||||
"kratos.1070010": "Återställningskod",
|
||||
"kratos.1070011": "Verifieringskod",
|
||||
"kratos.1080003":
|
||||
"Ett mejl med en verifieringskod har skickats till adressen du angav. Har du inte fått något mejl, kontrollera stavningen och att du använder adressen du registrerade dig med.",
|
||||
"kratos.4000002": "Fältet är obligatoriskt.",
|
||||
"kratos.4000006": "Uppgifterna stämmer inte. Kontrollera att e-postadressen och lösenordet är rätt stavade.",
|
||||
"kratos.4000007": "Det finns redan ett konto med den e-postadressen.",
|
||||
"kratos.4060006": "Återställningskoden är ogiltig eller redan använd. Försök igen.",
|
||||
"kratos.4070006": "Verifieringskoden är ogiltig eller redan använd. Försök igen.",
|
||||
|
||||
"landing.dashboard": "Gå till din panel",
|
||||
"landing.lead":
|
||||
"{{brand}} är en självhostad grund för administrativa och operativa gränssnitt — inloggning, en konfigurationsstyrd meny och ett serverrenderat designsystem utan JavaScript. Du lägger till de verksamhetsnära skärmarna genom att släppa in plugin-mappar.",
|
||||
"landing.register": "Skapa konto",
|
||||
"landing.signIn": "Logga in",
|
||||
"landing.title": "Operativa webbappar, utan all pannplåt.",
|
||||
|
||||
"locale.label": "Språk",
|
||||
|
||||
"nav.dashboard": "Panel",
|
||||
|
||||
"oauth.consentExpired": "Den här behörighetsbegäran har gått ut. Börja om från appen du skulle logga in i.",
|
||||
"oauth.loginExpired": "Den här inloggningsbegäran har gått ut. Börja om från appen du skulle logga in i.",
|
||||
"oauth.logoutExpired": "Den här utloggningsbegäran har gått ut. Börja om från appen du skulle logga ut från.",
|
||||
|
||||
"pagination.go": "Visa",
|
||||
"pagination.label": "Sidnavigering",
|
||||
"pagination.next": "Nästa sida",
|
||||
"pagination.of": "av",
|
||||
"pagination.previous": "Föregående sida",
|
||||
"pagination.rows": "Rader",
|
||||
|
||||
"shell.breadcrumb": "Sidsökväg",
|
||||
"shell.closeMenu": "Stäng menyn",
|
||||
"shell.guest": "Gäst",
|
||||
"shell.mainNav": "Huvudmeny",
|
||||
"shell.openMenu": "Öppna menyn",
|
||||
"shell.preferences": "Inställningar",
|
||||
"shell.profile": "Profil",
|
||||
"shell.settings": "Inställningar",
|
||||
"shell.sidebar": "Primär",
|
||||
"shell.signedInAs": "Inloggad som {{name}}",
|
||||
"shell.signIn": "Logga in",
|
||||
"shell.signOut": "Logga ut",
|
||||
"shell.skipToContent": "Hoppa till innehållet",
|
||||
"shell.toggleSection": "Visa eller dölj {{label}}",
|
||||
|
||||
"table.actions": "Åtgärder",
|
||||
"table.empty": "Inget här ännu.",
|
||||
"table.row": "raden",
|
||||
"table.rowActions": "Radåtgärder för {{name}}",
|
||||
"table.select": "Markera {{name}}",
|
||||
"table.selectAll": "Markera alla rader",
|
||||
|
||||
"theme.auto": "Auto",
|
||||
"theme.dark": "Mörkt",
|
||||
"theme.label": "Färgtema",
|
||||
"theme.light": "Ljust",
|
||||
};
|
||||
|
||||
export default messages;
|
||||
Reference in New Issue
Block a user