Agent skill

Working With Translations

by TriliumNext in TriliumNext/Trilium

A skill your agent uses when adding, finding, changing, reading or auditing a UI string in Trilium — "is there already a key for X?", "add a translation key", "what does <key say?", "what does this…

AGPL-3.0Auto-check passedWriting & Content

Install Working With Translations

skills CLI
$ npx skills add TriliumNext/Trilium --skill working-with-translations -a claude-code

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

GitHub CLI
$ gh skill install TriliumNext/Trilium working-with-translations --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/working-with-translations .claude/skills/working-with-translations && 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
working-with-translations
GitHub stars
38k
Token cost
~3.3k tokens
SKILL.md length
1,531 words
Files
2
Skills in repo
23
Repo updated
First seen
Licence
AGPL-3.0

At a glance

A skill your agent uses when adding, finding, changing, reading or auditing a UI string in Trilium — "is there already a key for X?", "add a translation key", "what does <key say?", "what does this…

  • Auditing a UI string in Trilium — is there already a key for X?
  • SKILL.md covers Reading a string in other…, Which catalogue does a key go…, Adding a string and Auditing, plus 4 more sections
  • Runs JavaScript scripts from its folder; calls node and pnpm
  • Add a translation key

What it does

Working With Translations is an agent skill from TriliumNext/Trilium. Use when adding, finding, changing, reading or auditing a UI string in Trilium — "is there already a key for X?", "add a translation key", "what does <key say?", "what does this button say in German?", "has this string been translated yet?", "which catalogue does this go in?", "any unused or misspelled keys?". Covers the three English catalogues (client / entry / server) and the routing rule that decides which one a key belongs to, and includes i18n.mjs to look a key up across all 39 locales, so neither the 226…

Its SKILL.md is about 3.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file.

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

When your agent uses it

  • Auditing a UI string in Trilium — is there already a key for X?
  • Add a translation key
  • What does <key say?
  • What does this button say in German?

Example prompts

  • “is there already a key for X?”
  • “add a translation key”
  • “what does <key say?”
  • “/working-with-translations”

Requirements

  • Node.js

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

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

  • Network

    No URLs in SKILL.md. Its commands use pnpm, 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

Working With Translations loads about 3.3k tokens when it runs. Until then it costs about 191 tokens; SKILL.md has 1,531 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~191
When it runs · the whole SKILL.md, loaded when a task matches
~3.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,531 words, ~3,314 tokens.

Download SKILL.mdSave it as .claude/skills/working-with-translations/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
working-with-translations
description
Use when adding, finding, changing, reading or auditing a UI string in Trilium — "is there already a key for X?", "add a translation key", "what does <key> say?", "what does this button say in German?", "has this string been translated yet?", "which catalogue does this go in?", "any unused or misspelled keys?". Covers the three English catalogues (client / entry / server) and the routing rule that decides which one a key belongs to, and includes i18n.mjs to look a key up across all 39 locales, so neither the 226 KB catalogue nor a locale file ever has to be read or hand-edited. Writes English only. For deliberately filling in a lagging locale (Romanian is behind, bring a locale to 100%), use the translating-locales skill instead.

Working with Trilium's English UI strings

Trilium's UI is localized with i18next. English is the source of truth and the only catalogue you edit by hand — the other ~38 locales belong to Weblate. This skill is the everyday case: you are building or changing a feature and need to add a string, find one that already exists, or check that nothing dangles.

Every mechanical step is i18n.mjs.

bash
S=.claude/skills/working-with-translations/i18n.mjs
node $S find "Cancel"                        # English text → catalogue + dotted key
node $S find totp_enroll --key               # search key paths instead of values
node $S show about.channel                   # dump one section
node $S show delete_note.delete_note --locales   # what the string says in every language
node $S add client dialog.my_thing "My text" # insert, preserving key order + formatting
node $S callers about.channel.nightly        # where a key is referenced
node $S missing                              # keys used in code but absent from the catalogue
node $S unused                               # catalogue keys nothing references

Never read a whole catalogue to answer a question. apps/client/src/translations/en/translation.json is 226 KB / 4032 lines / 3384 keys across 304 top-level sections — reading it costs more than any question it answers. find and show exist so nothing has to.

Never hand-edit the JSON either. add writes through the file's own indent (2), EOL (LF) and trailing newline, and appends into the correct parent object. A round-trip is byte-identical, so a one-string change is a 3-line diff. A hand-edit gets the nesting or the trailing comma wrong; a naive JSON.parse → stringify at the wrong indent rewrites all 4032 lines.

Reading a string in other languages

show <key> --locales prints the value in every locale that has it, then the locales that don't and the ones whose value is byte-identical to English:

--- client: delete_note.delete_note
  en      "Delete note"
  de      "Notiz löschen"
  ro      "Șterge notița"
  …
  22/39 locales translated
  missing: az, bg, ca, el, en-GB, fa, fi, hr, hu, md, mr, nb-NO, nl, sl, sv, ur, vi

Use it to answer "what does this button say in German?", "has this been translated yet?", or "why does the Romanian UI show that?". Identical-to-English is usually legitimate — a proper noun (hidden-subtree.llm-title is "AI / LLM" nearly everywhere) or a word the language shares (Italian login.password is "Password"). It works for all three catalogues.

This tool never writes a non-English file. add takes a catalogue (client / entry / server), not a locale, so add ro … is rejected outright — there is no path by which it can touch a Weblate-owned file. Editing a locale is the translating-locales skill's merge, and even then only for a deliberate maintainer pass.

Changing an English string does not update the ~38 existing translations, which then read as translations of text that no longer exists — show --locales makes that visible. Weblate flags them for review on its next pull; you don't hand-edit them.

Which catalogue does a key go in?

CatalogueFileRead by
clientapps/client/src/translations/en/translation.jsonthe browser UI — everything after login
entryapps/client/src/translations/en/entry.jsononly the setup wizard, login and password-reset pages
serverapps/server/src/assets/translations/en/server.jsonapps/server, the Electron main process, packages/trilium-core, and the standalone worker

Getting this wrong is the most common mistake, and add warns when a key's section lives in a different catalogue than the one you named.

  • entry exists for weight. The three pre-login pages call initLocale(locale, "entry") and read ~12 KB instead of the app's 226 KB — which matters because the wizard reloads the catalogue every time the user picks a language. Put a login.* / set_password.* / setup.* key in client and the pre-login pages simply will not find it.
  • The app loads both client and entry with fallbackNS: "entry", so a call site writes t("login.password") without caring which file holds it. Only the new key has to go in the right place.
  • server.json is misnamed — it is the catalogue for every runtime outside the browser UI. A t() call added to trilium-core resolves in server, desktop and standalone with no extra work (apps/standalone/vite.config.mts copies apps/server/src/assets/** and the worker loads it with ns: "server"). Don't assume a core string needs a fallback to work in the browser build.

Client code imports t from ../services/i18n; server, desktop and core import it from i18next directly.

Adding a string

bash
node $S find "Save changes"                   # 1. does it already exist?
node $S add client dialog.my_thing "Save changes"

add prints two advisories worth reading:

  • Duplicate text — every existing key carrying that exact English string. Reuse one if it means the same thing; each new key costs ~38 locales a translation. But do add a separate key when the same English word differs by context — a language that inflects will need both.
  • Catalogue routing — when the key's section doesn't exist in the catalogue you named but does in another.

add refuses to overwrite an existing key (--force to override) and refuses to nest under one that is already a string.

Then use it: t("dialog.my_thing"). Only en/ changes — Weblate carries the string to the other locales, and you should not hand-edit the other ~38 locale files.

Write US English, and mirror the difference into en-GB

The en catalogues are US English: color, center, meter, recognize, labeled, canceled, organize, defense, gray. en-GB is a locale of its own, and unlike the ~38 translated locales it is sparse on purpose — it carries only the strings whose British spelling differs ("color_type": "Colour", "centerContent": "Keep content centred"), and falls back to en for everything else.

So a string containing one of those words is two edits, not one:

bash
node $S add client dialog.my_thing "Pick a color"
# then, only because the spelling differs:
printf '{"dialog.my_thing":"Pick a colour"}' > /tmp/en-GB.json
node .claude/skills/translating-locales/locale.mjs merge en-GB client /tmp/en-GB.json

Leave en-GB alone where the spelling is the same in both — an entry identical to English is noise that measure then reports as untranslated. i18n.mjs writes en/ only, by design; locale.mjs merge is the sanctioned way into a locale file, and it preserves key order and formatting.

To check a catalogue, grep its values for the British halves of those pairs — colour, centre, metre, -ise/-isation, -lled/-lling, defence, licence, grey, programme, whilst. The English catalogues currently read 0 hits, so anything found is newly introduced.

Pluralization

i18next pluralizes only when the translation has _one / _other keys and the call site passes { count }. Add both forms and call t("key", { count }):

bash
node $S add client space_usage.my_notes_one "{{count}} note"
node $S add client space_usage.my_notes_other "{{count}} notes"

There is no base key — find --key space_usage.my_notes shows only the suffixed pair, and that is correct. Converting an existing single-form English key to a plural pair removes the base key, so every other locale falls back to English until Weblate migrates them; call that out in the PR.

Show full SKILL.md (614 more words)Show less
Interpolation

{{var}} everywhere. The client turns i18next's HTML-escaping off in initLocale(), so {{var}} and {{- var}} behave the same there; the server catalogue still escapes, which is why server.json keeps {{- var}} on values carrying quotes or markup. A value interpolated into a string that is rendered as markup is escaped by the caller: Menu titles and highlightedNotePathTitle take HTML, so their values go through escapeHtml(); the confirm/info dialogs sanitize a string message with DOMPurify. When a string embeds components whose order varies by language (links, note references), use <Trans> from react-i18next rather than t(), so translators can reorder them.

Multi-paragraph text

Text that runs to several paragraphs is one message with a blank line (\n\n) between paragraphs, never one key per paragraph. The translator then sees the whole text and decides where its paragraphs fall — a language can merge two or split one — and a translation that drops the blank line still renders, as a single paragraph. Some 18 English strings already work this way (toast.critical-error.message, call_to_action.new_layout_message, the breadcrumb_badges.*_description tooltips, llm.antigravity_agent_description).

Pass real newlines to add — in bash, $'First paragraph.\n\nSecond paragraph.'. Then render it so the breaks survive, by one of:

  • white-space — put the text in one element with the shared .pre-wrap-text class (stylesheets/style.css), as the call-to-action dialog does. Tooltips (pre-line), toasts and confirm() dialogs already keep line breaks on their own.
  • Split into <p> — when each paragraph should get paragraph spacing: text.split(/\n\s*\n/).map((p) => <p key={p}>{p}</p>), as the add-provider wizard does for connectionDescription.

Auditing

missing is the one that catches real bugs — a t("…") whose key is in no catalogue renders the raw key to the user:

bash
node $S missing        # currently 1 known hit, see below
node $S unused         # advisory only

unused is advisory, not a delete list. It already suppresses three legitimate patterns (plural bases, dynamic prefixes, keys travelling as bare strings), but ~27 call sites build a key from a variable with no visible prefix, and keys can also be referenced from a test fixture or a docs note. Run callers <key> and confirm before deleting anything.

What the scanner does and doesn't see

missing / unused / callers scan apps/client/src (against client + entry) and apps/server/src, apps/desktop/src, packages/trilium-core/src (against server). Within those:

PatternHandled
t("some.key"), <Trans i18nKey="some.key">resolved exactly
t("pdf.annotations", { count }) → _one / _otherplural bases bridged in both directions
t(`about.channel.${channel}`)static prefix keeps every key under it live
"security-dialog.lan-access" passed to a helper that calls t()counted as a soft reference
t(labelKey) — a bare variableinvisible; reported as a caveat count

packages/ckeditor5 is deliberately not scanned. Its plugins pass the English text itself to CKEditor's t(), so the argument is a message, not a key — scanning it would produce nothing but noise. That mechanism has its own rules (a text-editor.ck entry keyed by the slug of the English text) and apps/client/src/services/i18n.spec.ts already enforces it in both directions, so a missing entry and a stale one both fail. See the CKEditor section of CLAUDE.md; don't reimplement that check here.

Validation

bash
pnpm --filter client test src/services/i18n.spec.ts

That is the repo's only translation test: valid JSON, no duplicate keys, plus the CKEditor message-catalogue check. There is no locale-parity test and no typed i18next, so a missing key fails at runtime as a raw key on screen, not at build time — which is exactly why missing is worth running before you finish.

Known outstanding issue

packages/trilium-core/src/services/hidden_subtree_launcherbar.ts:136 calls t("hidden-subtree.llm-chat-title"), which does not exist in server.json (the nearby keys are llm-title, llm-chat-history-title, sidebar-chat-title). Low impact — that launcher entry is enforceDeleted: true, so the note is removed rather than displayed. Fix it or delete the dead entry if you are in that file anyway.

  • translating-locales — filling in a lagging non-English locale: measuring coverage, exporting work lists, placeholder validation, plural categories per locale, and how Weblate sync works. Use that skill, not this one, when the task is "bring <locale> to 100%".

© 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 1 other file in .claude/skills/working-with-translations of TriliumNext/Trilium.

  • SKILL.md
  • i18n.mjs

Open the folder on GitHubat commit 4d9cb6d

Compare with similar skills

Working With Translations 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.

Working With Translations compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Working With Translations this skillTriliumNext/Trilium38k—~3.3kAutomated safety check: PassAGPL-3.0
Translation Diff ExportDevolutions/UniGetUI26k—~1.1kAutomated safety check: PassMIT
Translation Diff TranslateDevolutions/UniGetUI26k—~934Automated safety check: PassMIT
Plane UI Translationmakeplane/plane61k—~16kAutomated safety check: PassAGPL-3.0
Translatetagspaces/tagspaces5.3k—~2.2kAutomated safety check: PassAGPL-3.0
Translation Source SyncDevolutions/UniGetUI26k—~1.2kAutomated 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
  • 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
  • Plane UI Translation

    makeplane/plane

    Sets the rules for translating and updating Plane's UI strings across locales: do-not-translate terms, plural forms, placeholders and AI translation review.

    61k GitHub stars~16k tokensUpdated yesterday
    Writing & ContentAuto-check passed
  • Translate

    tagspaces/tagspaces

    Translate new or untranslated i18n strings from the English locale files (core.json + peri.json) into all other locales, maintaining consistency with each language's existing translations.

    5.3k GitHub stars~2.2k tokensUpdated today
    Writing & ContentAuto-check passed
  • Translation Source Sync

    Devolutions/UniGetUI

    Synchronizes the UniGetUI English language file with source-code usage, identifies missing translation keys, removes unused entries, reports localization drift, and can reorder locale files to match…

    26k GitHub stars~1.2k tokensUpdated yesterday
    Writing & ContentAuto-check passed
  • Translate or audit the values in a Video.js locale pack. An agent skill from videojs/video.js.

    40k GitHub stars~723 tokensUpdated yesterday
    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 Working With Translations

What does Working With Translations do?

A skill your agent uses when adding, finding, changing, reading or auditing a UI string in Trilium — "is there already a key for X?", "add a translation key", "what does <key say?", "what does this…. Working With Translations is an agent skill from TriliumNext/Trilium.".

When should I use Working With Translations?

Working With Translations fits situations like: auditing a UI string in Trilium — is there already a key for X?; add a translation key; what does <key say?; what does this button say in German?.

How do I install Working With Translations in Claude Code?

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

How do I install Working With Translations in Codex?

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

Can I use Working With Translations 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 working-with-translations -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/working-with-translations, .gemini/skills/working-with-translations, .github/skills/working-with-translations and .opencode/skills/working-with-translations in your project.

What does Working With Translations need to run?

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

Does Working With Translations access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Working With Translations 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 Working With Translations use?

Working With Translations 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 Working With Translations use?

About 3.3k tokens (SKILL.md is roughly 13k 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 Working With Translations?

Skills that share tags, products or a category with Working With Translations: Translation Diff Export (Devolutions/UniGetUI, 26k stars), Translation Diff Translate (Devolutions/UniGetUI, 26k stars), Plane UI Translation (makeplane/plane, 61k stars) and Translate (tagspaces/tagspaces, 5.3k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Working With Translations?

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.