Agent skill

Translating Locales

by TriliumNext in TriliumNext/Trilium

A skill your agent uses when filling in or improving a lagging UI translation locale in Trilium (e.g.

AGPL-3.0Auto-check passedWriting & Content

Install Translating Locales

skills CLI
$ npx skills add TriliumNext/Trilium --skill translating-locales -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install TriliumNext/Trilium translating-locales --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/TriliumNext/Trilium.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/translating-locales .claude/skills/translating-locales && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
translating-locales
GitHub stars
38k
Token cost
~3k tokens
SKILL.md length
1,412 words
Files
3
Skills in repo
23
Repo updated
First seen
Licence
AGPL-3.0

At a glance

A skill your agent uses when filling in or improving a lagging UI translation locale in Trilium (e.g.

  • Works in 5 steps: Measure the gap → Export the work list → Draft the translations → …
  • Improving a lagging UI translation locale in Trilium (e.g
  • SKILL.md covers How Weblate sync works here…, File locations, Workflow and Pluralization is a SOURCE-SIDE…, plus 1 more section
  • Runs JavaScript scripts from its folder; calls node, pnpm and git

What it does

Translating Locales is an agent skill from TriliumNext/Trilium. Use when filling in or improving a lagging UI translation locale in Trilium (e.g. "Romanian is behind", "bring <locale to 100% coverage", "translate the missing strings"). Covers measuring the gap vs English, drafting translations that preserve i18next placeholders, merging without diff churn, locale grammar rules (Romanian plurals/gender), the source-side pluralization workflow, how Weblate sync actually works (merging is picked up), and validation. Includes locale.mjs — don't hand-roll a JSON walker.

Its SKILL.md is about 3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `romanian.md`).

It sits in Writing & Content, covering Translation. The repository describes itself as: Build your personal knowledge base with Trilium Notes. The licence is AGPL-3.0.

When your agent uses it

  • Improving a lagging UI translation locale in Trilium (e.g
  • Tasks that involve Translation

Example prompts

  • “Romanian is behind”
  • “bring <locale to 100% coverage”
  • “translate the missing strings”
  • “/translating-locales”

Requirements

  • Node.js

Workflow steps

5 steps, taken from the step headings in SKILL.md.

  1. Measure the gap
  2. Export the work list
  3. Draft the translations
  4. Validate, then merge
  5. Final checks

What it can do on your machine

Read from SKILL.md and the folder at commit 4d9cb6d. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships script files (JavaScript), which the agent can run.

    Shell commands in SKILL.md call:

    • node
    • pnpm
    • git

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use pnpm and git, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Translating Locales loads about 3k tokens when it runs. Until then it costs about 132 tokens; SKILL.md has 1,412 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~132
When it runs · the whole SKILL.md, loaded when a task matches
~3k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from TriliumNext/Trilium at commit 4d9cb6d, republished under its AGPL-3.0 licence (© TriliumNext). 1,412 words, ~2,999 tokens.

Download SKILL.mdSave it as .claude/skills/translating-locales/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.
name
translating-locales
description
Use when filling in or improving a lagging UI translation locale in Trilium (e.g. "Romanian is behind", "bring <locale> to 100% coverage", "translate the missing strings"). Covers measuring the gap vs English, drafting translations that preserve i18next placeholders, merging without diff churn, locale grammar rules (Romanian plurals/gender), the source-side pluralization workflow, how Weblate sync actually works (merging is picked up), and validation. Includes locale.mjs — don't hand-roll a JSON walker.

Translating / improving a locale in Trilium

Trilium's UI is localized with i18next. English is the source of truth; other locales are normally crowd-translated via Hosted Weblate. This skill is for the maintainer case: deliberately filling a locale that lags behind (the recurring one is Romanian, ro).

Scope — this skill fills an already-registered locale. To add a brand-new locale (e.g. Polish) you must first do the one-time registration and build wiring; that's a separate task. Follow docs/Developer Guide/Developer Guide/Concepts/Internationalisation Translations/Adding a new locale.md to register it, then come back here to translate the strings.

en-GB is the exception and must never be filled. The en catalogues are US English, and en-GB carries only the strings whose British spelling differs — "color_type": "Colour", "centerContent": "Keep content centred" — falling back to en for the rest. So measure en-GB reporting a few percent is the intended state, not a gap: an entry that merely repeats the English is noise, and measure would then report it as untranslated anyway. Use merge en-GB <catalog> <file> for the handful of spelling variants a change introduces, and nothing else.

Every mechanical step is locale.mjs — measure, export, validate, merge, audit. Read romanian.md for Romanian grammar and terminology.

bash
S=.claude/skills/translating-locales/locale.mjs
node $S measure                                   # coverage of every locale, both catalogs
node $S measure ro -v                             # one locale, listing missing + untranslated keys
node $S export ro client --chunk 150              # work list (English source) → a temp dir
node $S validate ro client <files…>               # placeholders / whitespace / plural categories
node $S merge ro client <files…>                  # apply, preserving key order + formatting
node $S audit-plurals ro client                   # groups missing a category the locale requires

export writes { key: englishValue }; translate into a flat { key: translatedValue } file and feed it to validate then merge. Chunking matters: a locale that is 1000+ strings behind does not fit one drafting pass, and validate/merge both take a list of files.

How Weblate sync works here (merging a PR IS picked up — no separate upload needed)

Weblate is linked to this repo with the GitHub pull-request push method: .github/workflows/i18n.yml triggers on push to weblate:* branches, and Weblate opens PRs into the repo (the recurring Translations update from Hosted Weblate (#…) PRs). The locale translation.json / server.json files are tracked Weblate components. Consequently:

  • Merging a PR that edits a locale file IS picked up by Weblate. On its next pull of the default branch, Weblate imports the new/changed translations into its database — they persist and show up for other contributors. They are not discarded or overwritten. With the PR-based push method Weblate never force-pushes the default branch, so the repo is authoritative and Weblate merges from it.
  • A direct-to-repo PR is therefore a fully valid, durable way to land translations. You do NOT need to separately upload the file into Weblate.
  • This is the opposite of arbitrary repo files (README, docs): those aren't part of any Weblate component, so Weblate simply ignores them.
  • Real (narrow) caveat: a true conflict only arises if the same key is edited simultaneously in Weblate (pending, un-pushed) and in the repo — then Weblate's merge/rebase conflict handling picks the winner. Filling previously-empty/untranslated strings doesn't conflict.
  • CLAUDE.md's "only add new keys to en/translation.json" rule is about where new source strings originate (so they enter the translation pipeline), not a prohibition on landing translations for an existing locale via the repo.
  • Changing an English source string (e.g. pluralizing a key) makes Weblate flag the matching translations in other locales as needing review and drop orphaned removed-key entries — normal, and it doesn't affect the locale you just filled.

File locations

CatalogEnglish sourceTarget locale
client — the browser UIapps/client/src/translations/en/translation.jsonapps/client/src/translations/<locale>/translation.json
entry — setup wizard, login, password resetapps/client/src/translations/en/entry.jsonapps/client/src/translations/<locale>/entry.json
server — everything elseapps/server/src/assets/translations/en/server.jsonapps/server/src/assets/translations/<locale>/server.json

All three have their own EN↔locale pair — do all three; measure reports them side by side. entry is easy to forget: it is a separate i18next namespace loaded on its own by the three pre-login pages, so a locale can read 100% on client while its login screen is entirely English. Several of its keys (login.*, the SSO errors) are near-duplicates of server.json ones — translate them the same way.

server.json is misnamed: it is the catalog for every runtime outside the browser UI — apps/server, the Electron main process, packages/trilium-core, and the standalone worker, which loads it with ns: "server" from the copy of apps/server/src/assets that its vite build emits under server-assets. Translating it therefore covers the standalone and mobile builds too, and a missing key there is not "server-only" breakage.

Workflow

1. Measure the gap

node $S measure <locale> -v. Two numbers matter: missing (absent from the locale) and untranslated (present but byte-identical to English). The latter is mostly legitimate proper nouns — -v lists them so you can tell.

2. Export the work list

node $S export <locale> <catalog> --chunk 150. Chunks of ~150 strings keep each drafting pass reviewable; --out DIR if you want them somewhere specific (default: a fresh temp dir).

3. Draft the translations
  • Probe the existing file first and match its terminology and tone. RO uses notiță for "note", Anulează for "Cancel", formal-plural address (Selectați). Don't invent a second term for something the file already names.
  • Keep proper nouns and kept technical terms in English: ETAPI, MCP, OAuth/OpenID, Markdown, Widget, Mermaid, Bing/Google/Gantt/Kanban/…, and words spelled the same in the target language. Also keep literal UI labels of other products quoted in English (Notion's "Create folders for subpages") — the user has to find that exact control.
  • Preserve every placeholder exactly (the #1 source of bugs) — validate checks all of these:
    • {{var}}, {{- var}} (unescaped — keep the hyphen), {keyword} (single-brace, e.g. date patterns and search-engine URLs)
    • JSX/<Trans> tags: <buildRevision />, <Note/>, <code>…</code>; HTML entities like &rarr;
    • \n line breaks, leading/trailing spaces, trailing punctuation
  • Honor i18next plural suffixes — the locale's required categories, not English's two. See below.
  • Copy each form's placeholders from its own English key, not from the group. English often writes _one without {{count}} ("An archived note was copied here", "1 hidden note") while _other has it. validate compares a locale-only _few against _other, so put {{count}} in _few/_other and leave _one as English left it.
Show full SKILL.md (476 more words)Show less
4. Validate, then merge

node $S validate must print 0 errors before you merge. Then node $S merge, and check git diff --stat: a 600-string change is ~600 added lines, not thousands.

merge preserves what a naive JSON.stringify round-trip destroys:

  • Key order. Never alphabetize — neither the EN file nor the locale files are sorted, and sorting turns a 600-string change into a 2500-line diff. New keys are appended to their parent object.
  • Formatting. Indent, EOL and the trailing newline are detected from the file being written. The catalogs are currently LF; do not hard-code CRLF (an earlier version of this skill did, and it rewrites every line).
5. Final checks
bash
node $S audit-plurals <locale> <catalog>
node $S measure <locale>                          # expect 100%; leftovers should be proper nouns
pnpm --filter client test src/services/i18n.spec.ts

i18n.spec.ts is the repo's only translation test — valid JSON, no duplicate keys, plus the CKEditor message-catalog check. There is no locale-parity test and no typed-i18next, so adding a _few that English lacks is fine and won't break pnpm typecheck.

Pluralization is a SOURCE-SIDE decision

i18next only pluralizes when (a) the translation has plural-suffixed keys and (b) the call site passes { count }. So:

  • You may freely add locale-specific plural categories (Romanian _few, Russian _many) to an existing plural group — EN supplies _one/_other, the locale supplies its own set. locale.mjs derives the required set from Intl.PluralRules, so validate accepts exactly the categories the locale uses and rejects the ones it doesn't.
  • To pluralize a string that EN keeps as a single form (e.g. "{{count}} notes"), you must:
    1. Verify the call site passes count (grep for the key; look for t("…", { count })). If it doesn't, or the key is unused, don't pluralize.
    2. Convert the English key from base → _one/_other in en/translation.json (this is the sanctioned place to edit EN).
    3. Add the locale's own categories.
    4. No code change needed — t("key", {count}) resolves to the suffixed keys automatically.
  • ⚠️ Cross-locale cost: converting an EN base key to plural removes the base key, so all other locales fall back to English for that string until Weblate migrates them. Call this out in the PR. Don't unilaterally edit the other ~36 locale files — let Weblate migrate.
  • Skip strings i18next can't handle: more than one count-like variable (e.g. "{{count}} sources from {{sites}} sites" — only count triggers plurals).
Editing the English file by hand

For a handful of in-place EN edits (pluralizing one key, fixing a typo), do text-level replacement — never JSON.parse → stringify the EN file, which can reformat or re-escape unrelated lines:

js
const fs = require("fs");
function replaceOnce(path, pairs) { // pairs: [[oldLine, [newLine1, newLine2, …]], …]
    let s = fs.readFileSync(path, "utf8");
    const E = s.includes("\r\n") ? "\r\n" : "\n";
    for (const [oldL, newLines] of pairs) {
        if (s.indexOf(oldL) === -1) throw new Error("NOT FOUND: " + oldL);
        if (s.split(oldL).length > 2) throw new Error("NOT UNIQUE: " + oldL);
        s = s.replace(oldL, newLines.join(E));
    }
    fs.writeFileSync(path, s, "utf8");
}

Common pitfalls

  • Alphabetizing the file (massive diff) — merge won't, don't do it by hand either.
  • Writing the wrong EOL (whole-file diff) — merge detects it; a hand-written stringify won't.
  • Dropping a trailing . or a placeholder when copying a reviewer's suggestion — re-run validate after applying review feedback, not just after the first draft.
  • Putting server strings in the client file or vice-versa — separate namespaces, separate EN sources.
  • Translating a string whose English is a foreign UI label the user must click.

© TriliumNext, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 2 other files in .claude/skills/translating-locales of TriliumNext/Trilium.

  • SKILL.md
  • locale.mjs
  • romanian.md

Open the folder on GitHubat commit 4d9cb6d

Compare with similar skills

Translating Locales next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Translating Locales compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Translating Locales this skillTriliumNext/Trilium38k—~3kAutomated safety check: PassAGPL-3.0
Translation Diff ExportDevolutions/UniGetUI26k—~1.1kAutomated safety check: PassMIT
Sync Translationssymfony/symfony31k—~1.9kAutomated safety check: PassMIT
Translation Diff ImportDevolutions/UniGetUI26k—~750Automated safety check: PassMIT
Translation Diff TranslateDevolutions/UniGetUI26k—~934Automated safety check: PassMIT
Generate Translationspayloadcms/payload45k—~1.1kAutomated safety check: PassMIT

Similar skills

  • Translation Diff Export

    Devolutions/UniGetUI

    Compares UniGetUI JSON locale files against English, identifies untranslated or source-changed keys, and generates patch, reference, and handoff files for a target language.

    26k GitHub stars~1.1k tokensUpdated yesterday
    Writing & ContentAuto-check passed
  • Sync Translations

    symfony/symfony

    Synchronize translation catalogs across maintained Symfony branches: find messages that newer branches added to the English catalogs but that are still missing from the oldest maintained branch…

    31k GitHub stars~1.9k tokensUpdated today
    Writing & ContentAuto-check passed
  • Translation Diff Import

    Devolutions/UniGetUI

    Merges translated key-value pairs from a UniGetUI JSON localization patch back into the full language file and validates the merged result.

    26k GitHub stars~750 tokensUpdated yesterday
    Writing & ContentAuto-check passed
  • Translation Diff Translate

    Devolutions/UniGetUI

    Translates a sparse UniGetUI JSON language patch, writes completed entries into the working copy, preserves placeholders and terminology, and prepares the patch for merge-back.

    26k GitHub stars~934 tokensUpdated yesterday
    Writing & ContentAuto-check passed
  • Generate Translations

    payloadcms/payload

    A skill your agent uses when new translation keys are added to packages to generate new translations strings

    45k GitHub stars~1.1k tokensUpdated yesterday
    Writing & ContentAuto-check passed
  • Drives long-form fiction, scripts, storyboards, interactive films and long-document translation through InkOS, with every change made by a typed action.

    10k GitHub starsUsed in 1 repo~1.1k tokens
    Writing & ContentAuto-check passed

More from TriliumNext/Trilium

All 24 skills in this repo
  • Cutting A Release

    TriliumNext/Trilium

    A skill your agent uses when cutting, preparing, or debugging a Trilium release — bumping the monorepo version, tagging, or diagnosing a failed "Release" workflow run.

    38k GitHub stars~3.2k tokensUpdated today
    Auto-check passed
  • Developing Electron Desktop

    TriliumNext/Trilium

    A skill your agent uses when working on the Trilium Electron desktop app (apps/desktop) — adding or changing an electronApi method / IPC channel, touching preload.ts, main.ts, services/window.ts or…

    38k GitHub stars~5.7k tokensUpdated today
    Auto-check passed
  • Evolving The Data Model

    TriliumNext/Trilium

    A skill your agent uses when adding a DB migration or a new column/field to a Becca entity in Trilium ("add a migration", "new column on notes/attributes", "ALTER TABLE", "add a field to…

    38k GitHub stars~2.1k tokensUpdated today
    Auto-check passed
  • Adding Internal API Route

    TriliumNext/Trilium

    A skill your agent uses when adding, moving, or wiring an internal REST endpoint in Trilium (a new /api/ route) — choosing between a core-shared handler (packages/trilium-core/src/routes/index.ts…

    38k GitHub stars~3.3k tokensUpdated today
    Auto-check passed
  • Adding LLM MCP Tools

    TriliumNext/Trilium

    A skill your agent uses when adding, changing, or reviewing an LLM/MCP tool in Trilium (the defineTools definitions under packages/trilium-core/src/services/llm/tools/ —…

    38k GitHub stars~2.5k tokensUpdated today
    Auto-check passed
  • Ckeditor5 Plugin Development

    TriliumNext/Trilium

    Write, extend, and review CKEditor 5 plugins in the Trilium (TriliumNext Notes) monorepo — the rich-text-note editor under packages/ckeditor5, whose plugins live in src/plugins/.

    38k GitHub stars~4.9k tokensUpdated today
    Auto-check passed

Questions about Translating Locales

What does Translating Locales do?

A skill your agent uses when filling in or improving a lagging UI translation locale in Trilium (e.g. Translating Locales is an agent skill from TriliumNext/Trilium.g.

When should I use Translating Locales?

Translating Locales fits situations like: improving a lagging UI translation locale in Trilium (e.g; tasks that involve Translation.

How do I install Translating Locales in Claude Code?

Run `npx skills add TriliumNext/Trilium --skill translating-locales -a claude-code`. Or copy the skill folder (.claude/skills/translating-locales in TriliumNext/Trilium) into .claude/skills/translating-locales in your project. Claude Code loads it when a task matches its description.

How do I install Translating Locales in Codex?

Run `npx skills add TriliumNext/Trilium --skill translating-locales -a codex`. Or copy the skill folder (.claude/skills/translating-locales in TriliumNext/Trilium) into .agents/skills/translating-locales in your project. Codex loads it when a task matches its description.

Can I use Translating Locales in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add TriliumNext/Trilium --skill translating-locales -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/translating-locales, .gemini/skills/translating-locales, .github/skills/translating-locales and .opencode/skills/translating-locales in your project.

What does Translating Locales need to run?

Going by SKILL.md and its folder, Translating Locales needs JavaScript for the scripts in its folder and the command-line tools its instructions call (node, pnpm and git). Our summary lists: Node.js.

Does Translating Locales access the network?

SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Translating Locales safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Translating Locales use?

Translating Locales is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Translating Locales use?

About 3k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Translating Locales?

Skills that share tags, products or a category with Translating Locales: Translation Diff Export (Devolutions/UniGetUI, 26k stars), Sync Translations (symfony/symfony, 31k stars), Translation Diff Import (Devolutions/UniGetUI, 26k stars) and Translation Diff Translate (Devolutions/UniGetUI, 26k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Translating Locales?

TriliumNext (a GitHub organization) maintains it in TriliumNext/Trilium, which has 38,274 GitHub stars. The repository holds 23 skills in this directory. The repository was last updated on October 11, 2026.

Source: TriliumNext/Trilium on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.