>- Internationalization conventions for the RedisInsight UI (i18next). Use when adding or changing user-facing strings under redisinsight/ui/**, editing the locale files (en.json/bg.json), translating API errors or notifications, or when the user mentions i18n, translations, locales, i18next, or <Trans>.
npx skills add https://github.com/redis/RedisInsight --skill i18n
RedisInsight UI is localized with i18next + react-i18next. English (en)
is the source of truth; Bulgarian (bg) is the second locale.
redisinsight/ui/src/i18n/ — the i18next instance and barrel. Import from uiSrc/i18n.redisinsight/ui/src/i18n/locales/en.json and bg.json — the translations (flat keys).redisinsight/ui/src/i18n/i18next.d.ts — augments i18next types from en.json, so keys are type-checked (a typo in t('…') fails type-check).i18next.config.mjs — extraction config.scripts/check-i18n-locales.js — duplicate-key CI check.| Use | When |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| useTranslation() hook → t | Inside a React component rendered in the tree. |
| i18n.t singleton (import i18n from 'uiSrc/i18n') | Non-React code: redux thunks, factories, utils, message builders (e.g. error-messages.tsx, success-messages.tsx, INFINITE_MESSAGES). These run outside React, so the hook is unavailable. |
| <Trans> (from uiSrc/i18n) | A message with mid-sentence markup/links — bold spans, inline <a>. Keys use component tags: "… <consoleLink>Cloud console</consoleLink> …" + components={{ consoleLink: <a … /> }}. |
Hook — inside a React component:
import { useTranslation } from 'uiSrc/i18n';
const AddKeyButton = () => {
const { t } = useTranslation();
return <PrimaryButton>{t('browser.addKey.button.submit')}</PrimaryButton>;
};
Singleton — non-React code (thunks, factories, utils):
import i18n from 'uiSrc/i18n';
export const deletedKeyMessage = () => ({
title: i18n.t('browser.deletedKey.title'),
});
Interpolation — pass values; reference {{vars}} in the string:
// en.json: "browser.deletedKey.message": "{{name}} has been deleted."
t('browser.deletedKey.message', { name: keyName });
<Trans> — mid-sentence markup or links (map tags to components):
import { Trans } from 'uiSrc/i18n';
// en.json: "browser.docs": "See the <docsLink>documentation</docsLink> for details."
<Trans
i18nKey="browser.docs"
components={{
docsLink: <a href={DOCS_URL} target="_blank" rel="noreferrer" />,
}}
/>;
// interpolation still works alongside components via `values={{ … }}`
Backend error resource — interpolated automatically:
// Response: { errorCode: 11200, resource: { databaseId: 'abc' } }
// en.json: "api.error.code.11200.message": "Database {{databaseId}} already exists."
// getTranslatedApiError() fills {{databaseId}} from response.data.resource — no extra code.
Use i18next's native count-based plurals — never a hand-rolled isPlural branch with
.single/.plural keys.
key_one, key_other (a language mayneed more forms — _few, _many — but en/bg only use _one/_other).
count; i18next selects the form:t('key', { count }) or <Trans i18nKey="key" count={n} …/>.
en.json — i18next'stypes resolve it from the _one/_other entries.
fragment — word order, agreement, and the number of plural forms vary by language.
.single → _one) leaves the old key behind in bg.json becausei18n:extract doesn't prune — delete the orphan so en/bg parity holds.
// en.json:
// "workbench.runConfirm.body_one": "…This command is part of…"
// "workbench.runConfirm.body_other": "…These commands are part of…"
<Trans i18nKey="workbench.runConfirm.body" count={commands.length} components={{ bold }} />
keySeparator and nsSeparator are false, so a dot is a literal character, not nesting. "api.error.code.11000.title" is a single key.en.json is the type source — every literal t('…') / i18nKey="…" must exist in en.json or type-check fails. Dynamic/computed keys can't be statically typed — cast with as never (e.g. i18n.t(api.error.code.${code}.message as never)), the same pattern used across the codebase.i18n:extract produces). Edit values in place where possible; only re-sort when adding keys.bg values are OK as a "translate later" placeholder — returnEmptyString: false makes them fall back to English, not render blank.escapeValue: false — interpolated values are not HTML-escaped (React escapes plain strings at render).The top-level segment says where a string belongs. There are two kinds:
1. Cross-cutting (by source), not tied to a page:
api.* — content keyed by a backend identifier (the API contract). Today api.error.code.<errorCode>.{title,message}; scales to api.<type>.* for any future API-originated content.notification.* — FE-authored toast copy: notification.{error,success,infinite}.*.common.* — labels reused across many pages: common.button.save, common.button.cancel, common.button.delete, common.loading, common.yes, common.no.2. By page/module — most UI copy. Top-level = the feature (mirror the folder in
redisinsight/ui/src/pages/<page> → <page>.*); nest by section/component; the leaf
describes the string. A page owns its keys; only promote to common.* when genuinely shared.
| Namespace | Module (pages/…) | Example keys |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------------------- |
| browser.* | browser | browser.keyList.empty, browser.addKey.title, browser.filter.placeholder, browser.addKey.button.submit |
| workbench.* | workbench | workbench.editor.runTooltip, workbench.results.empty |
| rdi.* | rdi | rdi.pipeline.deploy.title, rdi.config.button.deploy |
| settings.* | settings | settings.section.general, settings.language.title |
Common leaf segments: *.title, *.description, *.label, *.placeholder, *.tooltip,
and *.button.<action> for action labels (keep them distinct from titles/descriptions,
e.g. browser.addKey.button.submit, api.error.code.11024.button.signIn).
The backend ships a stable errorCode on every user-facing error (see
redisinsight/api/src/constants/custom-error-codes.ts). The UI translates by that code:
getTranslatedApiError(error) and getTranslatedApiTitle(error) inutils/apiResponse.ts look up
api.error.code.<n>.message / .title and fall back to the backend text when the key is absent.
parseCustomError in utils/errors.tsx does the same in its default case for coded errors it doesn't special-case.resource interpolation: any response.data.resource object fills {{vars}} in the message.en.json and the same key to bg.json (translated, or empty to defer). Keep both sorted and in parity.t('my.key') / i18n.t('my.key') / <Trans i18nKey="my.key" …/>.values (interpolation) or resource (backend errors).npm run i18n:extract to sync/sort, and npm run i18n:check to catch duplicate keys.npm run type-check (new literal keys must resolve) and npm run lint:ui.npm run i18n:extract — scans t()/<Trans> usages and syncs en.json/bg.json (alphabetical; does not prune unused keys). Note: dynamic (as never) keys aren't discovered by extraction — keep them in the locale files manually.npm run i18n:check — fails if a locale file has a duplicate key (JSON silently keeps the last, so a dup would shadow a value). Runs in CI on PRs touching locales/**.?lang=bg to the URL to preview Bulgarian.i18n singleton; components use useTranslation.<page>.* mirroring pages/<page>), or by source (api.*, notification.*, common.*); shared labels go in common.*.i18n:extract sort.count + key_one/key_other (see Plurals).Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks.
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementing any feature or bugfix, before writing implementation code
Use when you have a spec or requirements for a multi-step task, before touching code
Use when creating new skills, editing existing skills, or verifying skills work before deployment
Use when writing or improving README files. Not all READMEs are the same — provides templates and guidance matched to your audience and project type.
| Remove signs of AI-generated writing from text. Use when editing or reviewing text to make it sound more natural and human-written. Based on Wikipedia's inflated symbolism, promotional language, superficial -ing analyses, vague attributions, em dash overuse, rule of three, AI vocabulary words, negative parallelisms, and excessive conjunctive phrases.
Official Opentrons Protocol API for OT-2 and Flex robots. Use when writing protocols specifically for Opentrons hardware with full access to Protocol API v2 features. Best for production Opentrons protocols, official API compatibility. For multi-vendor automation or broader equipment control use pylabrobot.
Take redis/i18n from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.