Skip to content
Select themeSelect language

Add an i18n key

SupaCloud’s web UI uses Paraglide for internationalisation. Message catalogs live in web/messages/en.json and web/messages/de.json. Generated code in web/src/lib/paraglide/ is produced from those files — do not edit the generated files directly.

  1. Add the key to web/messages/en.json.

    Keys must match [a-z0-9_]+ (lowercase, digits, underscores only). Choose a name that is descriptive and scoped to the feature, for example workspace_rename_label.

    web/messages/en.json
    "workspace_rename_label": "Rename workspace"
  2. Add the same key to web/messages/de.json with a German translation.

    Both catalogs must stay in sync — the architecture test "uses Paraglide-safe message ids" fails if the key sets differ.

    web/messages/de.json
    "workspace_rename_label": "Workspace umbenennen"
  3. Regenerate the Paraglide output.

    Terminal window
    cd web
    npm run i18n:compile

    This runs the paraglide-js compile pipeline and the clean-paraglide.mjs post-processor. The files under web/src/lib/paraglide/ are updated in place.

  4. Use the key in a Svelte component.

    Import m from the generated module and call it as a function:

    <script lang="ts">
    import { m } from "$lib/paraglide/messages.js";
    </script>
    <button>{m.workspace_rename_label()}</button>

    Never embed a raw string literal for user-visible copy — the architecture test "keeps obvious Svelte template copy behind Paraglide calls" fails on literal text inside Svelte templates.

  5. Verify locally before committing.

    Terminal window
    cd web
    npx svelte-check --threshold error # 0 type errors
    npm run lint # oxlint — 0 findings
    npx vitest run # architecture tests pass

i18n-lint guard — “team” for the tenant

Section titled “i18n-lint guard — “team” for the tenant”

The frontend architecture test "forbids new user-facing Workspace/Teams copy outside the agent/delivery namespaces" scans every value in both catalogs for the word team/teams (case-insensitive).

A new key whose value contains “team” or “teams” will fail CI unless its key name matches one of the allowed namespaces:

Allowed key pattern Purpose
teammate Multi-agent “Teammates” feature
agent_teams Agent team grouping
delivery_* Delivery board screen copy
issue_tracker_team_key Linear “Team key” field label
notify_to_placeholder Sample notify address copy

If the copy you are adding refers to a tenant (a Workspace or Organisation), use those words instead. “Team” and “Teams” are reserved for the agent domain. See the ubiquitous language glossary and ADR 0040.

If the string needs a runtime value, add a placeholder in ICU MessageFormat syntax:

"task_cost_summary": "Total cost: {amount}"

The generated function then accepts a typed argument:

{m.task_cost_summary({ amount: formattedCost })}

Keep placeholder names short, lowercase, and descriptive.