blocks-localization-configuration
Skill42 starsChanged 12 days agoMITView on GitHub
Configure app translations (i18n) for a SELISE Blocks project through the `blocks` CLI — never raw fetch/curl. Covers authoring local i18n JSON dictionaries, validate/push/pull with the Localization service, managing languages and modules directly, glossary terms, AI translation suggestions, and the composed translate-and-export flow. Use for 'add translations for my login screen', 'push/pull localization changes', 'create a module', 'add a new language'.
In this repository27 other files it gives its agents.
- AGENTS.mdAGENTS.md
- bootstrap/agents.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- blocks-bootstrapSkill
- blocks-captchaSkill
- blocks-data-gateway-configurationSkill
- blocks-data-gateway-crudSkill
- blocks-data-storageSkill
- blocks-frontend-local-httpsSkill
- blocks-functionsSkill
- blocks-iam-access-controlSkill
- blocks-iam-accountSkill
- blocks-iam-mfaSkill
- blocks-iam-organizationsSkill
- blocks-iam-sso-oidc-configurationSkill
- blocks-iam-sso-oidc-implementationSkill
- blocks-iam-usersSkill
- blocks-localization-implementationSkill
- blocks-mailSkill
- blocks-notificationSkill
- blocks-notifierSkill
- blocks-proxySkill
- blocks-release-deploymentSkill
- blocks-secretsSkill
- blocksSkill
- blocks-storage-configurationSkill
- blocks-workflowSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 6
When invoking a project-scoped blocks command, either use the resolved account's saved selection or pass --project <tenantId> for that one …
- Distinctive: line 90
Use this to pull down what's actually published before editing further — same reasoning as pulling data schemas before editing them: don't …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
When invoking a project-scoped blocks command, either use the resolved account's saved selection or pass --project <tenantId> for that one command without changing saved state. --project applies to CLI commands only, never SDK calls.
Blocks Localization — Configuration
Translations (i18n) for a Blocks project's static UI text — labels, titles, button copy — are authored locally as JSON and synced to the Localization service entirely through the blocks CLI. There is no supported reason to hand-roll raw fetch/curl calls anymore, and there's no SDK-based authoring path either — @seliseblocks/client's localization surface (languages(), modules(), languagesForCurrentTenant(), translations(), cloudTranslations(), keysByNames()) is entirely read-only, meant for apps to consume translations at runtime, not to author them. Authoring is CLI-only.
Prerequisite: blocks init has been run and a project is selected (blocks use <tenantId>). blocks init and blocks new web both create the blocks/localization/ folder (new web also seeds common.en.json); the dictionary files themselves appear when blocks localization pull writes one out or you author one by hand. If either the project selection is missing, or auth state is unknown, run the blocks-bootstrap skill first — it covers auth status probing, login, and project selection in detail; this skill assumes that's already done.
The three commands
| Command | What it does |
blocks localization validate --module <name> --language <culture> [--file <path>] [--json] | Validates a local i18n JSON dictionary. Local-only, no API call. |
blocks localization push --module <name> --language <culture> [--file <path>] [--route <route>] [--context <text>] [--publish once|per-key|none] [--dry-run] [--yes] [--json] | Creates/updates keys from the local dictionary, merging the pushed culture into each key's existing translations. If the module doesn't exist yet, creates it as a convenience; localization module save can also create an empty module directly. --dry-run reports created/updated counts and the exact merged request. --publish controls UILM regeneration (see below). Mutating. |
blocks localization pull --module <name> --language <culture> [--out <path>] [--json] | Downloads the published cloud dictionary into a local JSON file. Read-only, overwrites the local file. |
--module is the feature-area bundle name (common, login, dashboard, …). --language is a culture code (en, de-DE, bn-BD, …) — see the culture-matching gotcha below before picking one.
File convention
Local dictionaries default to:
blocks/localization/<module>.<language>.jsonfor example blocks/localization/login.de-DE.json. Pass --file/--out to override the path. Content is a flat or nested JSON object of string values — nested objects are flattened with . before validation/push, so either of these is fine and produces the same keys:
{ "form.title": "Anmelden", "form.submit": "Absenden" }The module already provides the namespace, so do not repeat it in key names: a login module uses title or form.title, not login.title.
{ "form": { "title": "Anmelden", "submit": "Absenden" } }Key names must match ^[A-Za-z0-9][A-Za-z0-9._:-]*$ (letters, numbers, dot, dash, underscore, colon — no spaces) after flattening, and every value must be a non-empty string. localization validate enforces exactly this, locally, before anything touches the network.
Workflow: add or update translations
- Generate or edit the local dictionary at
blocks/localization/<module>.<language>.json— write the JSON yourself (nested or flat), covering every key the screen/feature needs. - Validate locally, no API call:
Fix every flagged key/value before moving on.blocks localization validate --module login --language de-DE --json - Dry-run the push to see exactly what would happen (module create-or-reuse, key count, target project):
blocks localization push --module login --language de-DE --dry-run --json - Get user approval, then push for real:
This is mutating. Never skip straight toblocks localization push --module login --language de-DE --yes --json--yes. By default (--publish once) a push saves every key unpublished and then regenerates the module's UILM file a single time, so a successful push is live for reads right away — the regeneration is part of the push, not a separate step you run.
Bulk pushes — --publish. push regenerates the UILM file once per push by default (once). The old behaviour is --publish per-key, which sets shouldPublish on every key so the worker regenerates once per key — on a few-hundred-key push that queues hundreds of regenerations (~15/min) and blocks every other module's publish behind it for tens of minutes. Prefer the default once for anything beyond a handful of keys. Use --publish none to save without regenerating (then run blocks localization key generate-uilm-file --module-id <id> yourself, e.g. after several pushes into the same module).
Optional flags on push: --route <route> tags every key in this push with one route (e.g. the screen path the strings belong to); --context <text> attaches one context/hint string to every key in the push — both apply to the whole file, not per-key.
Workflow: multiple languages for the same screen
Each push call carries exactly one --language (one culture stamped onto every key in that file). Translating one module into several languages means one dictionary file and one push per language, all against the same --module:
blocks localization validate --module login --language de-DE --json
blocks localization validate --module login --language bn-BD --json
blocks localization push --module login --language de-DE --dry-run --json
blocks localization push --module login --language bn-BD --dry-run --json
# after approval:
blocks localization push --module login --language de-DE --yes --json
blocks localization push --module login --language bn-BD --yes --jsonThe module (login) is only created on the first push that needs it; the second push reuses the module the first one created.
Refreshing local files from the cloud
blocks localization pull --module login --language de-DE --out blocks/localization/login.de-DE.json --jsonUse this to pull down what's actually published before editing further — same reasoning as pulling data schemas before editing them: don't blindly overwrite translations someone else edited in the portal or in a prior session.
Managing languages directly
Pushing translations into an existing language and configuring the tenant's set of languages are different operations — the latter has its own standalone commands, independent of push/pull:
| Command | What it does |
blocks localization language save --language-name <n> --language-code <c> [--is-default] [--item-id <id>] [--dry-run] [--yes] [--json] | Creates or updates a language (upsert by name). Omit --item-id to create a new one. Re-saving an existing language keeps its isDefault unless --is-default/--is-default=false is passed -- the endpoint would otherwise demote the default. Mutating, full dry-run/confirm gate. |
blocks localization language delete <languageName> [--dry-run] [--yes] [--json] | Deletes a language. Mutating. |
blocks localization language set-default <languageName> [--dry-run] [--yes] [--json] | Marks a language as the tenant default. Mutating. |
blocks localization language list [--json] | Lists all languages. Read-only. |
blocks localization language list-for-tenant [--json] | Lists languages configured for the current tenant. Read-only. |
This is distinct from localization push --language de-DE, which stamps translations onto keys and doesn't touch the tenant's language configuration at all.
Managing modules directly
A module is still created implicitly by the first localization push into it, but it can also be created or updated on its own, with no keys, via a standalone command:
| Command | What it does |
blocks localization module save --module-name <n> [--item-id <id>] [--dry-run] [--yes] [--json] | Creates or updates a module. Omit --item-id to create a new one. Mutating. |
blocks localization module list [--json] | Lists all modules. Read-only. |
blocks localization module list-for-tenant [--json] | Lists modules configured for the current tenant. Read-only. |
localization push's implicit module creation is a convenience on top of the same underlying call, not the only path to it.
Other localization commands
A few more commands round out the surface beyond push/pull/validate/language/module — useful, but secondary to the core authoring workflow above:
| Command | What it does |
blocks localization key translate-and-export --module-id <id> [--wait] [--output-type <0-5>] [--dry-run] [--yes] [--json] | Composed flow: translate-all (machine-translates every untranslated key in the module) → if --wait, polls until the operation settles → generate-uilm-file → uilm-export. Without --wait the three steps just fire back-to-back. Mutating. |
--output-type (here and on key uilm-export) is the export file format, zero-based: 0 Json (the default), 1 Xml, 2 Text, 3 Xlsx, 4 Csv, 5 Xlf. Leaving it off gives Json, so an agent that wants a spreadsheet has to pass 3 explicitly.
| blocks localization glossary save --name <n> [--item-id <id>] [--language <c>] [--type <t>] [--context <text>] [--additional-note <text>] [--is-global] [--module-ids a,b] [--dry-run] [--yes] [--json] | Creates or updates a glossary term. With --item-id the current term is read and merged -- the endpoint rebuilds it from the body, so language/type/context/note/module tags would otherwise be dropped. Mutating. |
| blocks localization glossary list [--search <text>] [--module-id <id>] [--is-global] [--page-number <n>] [--page-size <n>] [--json] | Lists glossary terms. Read-only. |
| blocks localization glossary get <itemId> [--json] | Fetches one glossary term. Read-only. |
| blocks localization glossary suggested <itemId> [--max-results <n>] [--json] | Suggests glossary terms relevant to an item. Read-only. |
| blocks localization glossary delete <itemId> [--dry-run] [--yes] [--json] | Deletes a glossary term. Mutating. |
| blocks localization assistant translation-suggestion --source-text <text> [--current-language <c>] [--destination-language <name>] [--destination-language-code <c>] [--module-id <id>] [--glossary-ids a,b] [--element-type <t>] [--element-application-context <text>] [--element-detail-context <text>] [--max-character-length <n>] [--temperature <n>] [--json] | Gets an AI translation suggestion for a single string. Read-only (no confirm gate). |
| blocks localization config get-webhook [--json] | Gets the tenant's localization webhook config. Read-only. |
| blocks localization config save-webhook --url <url> --content-type <type> --secret <s> --header-key <k> [--is-disabled] [--item-id <id>] [--dry-run] [--yes] [--json] | Saves the tenant's localization webhook config. Mutating; the secret is redacted in --dry-run output. |
Gotchas
--languageonpushis not validated against configured cultures.localization pushstamps whatever string you pass as--languagedirectly into each key'sculturefield — it does not check that culture against the tenant's actual configured languages, and doesn't calllanguage list/list-for-tenantto look. Get the culture code wrong (deinstead ofde-DE, or a culture the tenant never configured vialanguage save) and the key saves without error but may never surface at runtime, because runtime lookups match by the tenant's real configuredlanguageCode. Confirm the exact culture code with the user (or runlocalization language list-for-tenant) before pushing, especially for less common languages like Bengali (bn-BDvsbn).- Module auto-create is silent and permanent. The first push against a new
--modulename creates it with no separate confirmation prompt beyond the push's own--dry-run/--yesgate —--dry-runoutput will tell you a module lookup happened, but won't distinguish "will create" from "already exists" as clearly as it could, so read the dry-run JSON's module info carefully, or ask the user to confirm the module name is intentional (typos become new, mostly-empty modules). Preferlocalization module savefirst if the user wants the module created deliberately, without an accompanying key push. localization validateis local-only — it confirms the JSON is well-formed and keys/values pass the naming rules; it does not confirm the push will succeed against the server (module resolution, auth, project selection). Still run--dry-runon the actual push.- One culture per file/push. Don't try to cram multiple languages into one dictionary file — the format is flat key → string value, not key → {culture: value}. Multiple languages means multiple files and multiple push invocations (see above). Running those back to back is safe: each push reads the module's keys first and upserts only its own culture. (Through 0.4.2 it sent just the pushed culture into a
Key/SaveKeyscall that replaces the stored resource list, so four sequential locale pushes left every key holding only the culture pushed last, and the site rendered[ KEY MISSING ]everywhere.) - The runtime reads a generated file, not the keys. Apps (and
localization pull,key get-uilm-file) read a generated UILM file; saving a key only changes what the next generation will contain.shouldPublish: trueis baked into every keylocalization pushsends, andkey savenow defaults to it too (--should-publish=falseopts out), so both queue that regeneration — but it runs asynchronously on a worker. If a just-pushed value still reads[ KEY MISSING ], force it withblocks localization key generate-uilm-file --module-id <moduleId> --yes(--guiddefaults to the module id) before assuming the write failed.key uilm-exportis a different thing again: a downloadable export, not what the runtime serves. language deleteandset-defaultare permanent, mutating calls — always dry-run first, and confirm with the user before deleting a language or changing the tenant default, since either can affect what's visible at runtime for existing translations.
Example trigger prompts
- "Add German translations for my login screen." → push
login.de-DE.jsonafter validate + dry-run + approval. - "Add German and Bengali translations for my login screen." → two dictionary files, two validate/push pairs (
de-DE,bn-BD), same module. - "Set up a
commonmodule for shared strings like Save/Cancel/Delete." → writecommon.<language>.jsonwith those keys, validate, push (this is what creates thecommonmodule) — or uselocalization module save --module-name commondirectly if no keys exist yet. - "Can we add Bengali as a new supported language for the tenant?" →
localization language save --language-name Bengali --language-code bn-BD --dry-run, confirm, then--yes— a language-config change, not apush. - "Machine-translate the whole
loginmodule and give me the export." →localization key translate-and-export --module-id <id> --wait --dry-run→ confirm →--yes.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

