بنقرة واحدة
add-translation
How to add, update, and manage i18n translations in Deenruv plugins and admin UI
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
How to add, update, and manage i18n translations in Deenruv plugins and admin UI
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Build admin UI components and pages using @deenruv/react-ui-devkit (shadcn/Tailwind-based)
Step-by-step guide to create a new Deenruv plugin with server and UI components
Write E2E tests for Deenruv plugins using Vitest and @deenruv/testing utilities
How to extend the Deenruv GraphQL API with new types, queries, mutations, and resolvers
استنادا إلى تصنيف SOC المهني
| name | add-translation |
| description | How to add, update, and manage i18n translations in Deenruv plugins and admin UI |
Deenruv has two separate translation systems. Identify which one applies before making changes.
| System | Where | Library | Languages |
|---|---|---|---|
| Admin Panel | apps/panel/, plugins/*/src/plugin-ui/ | i18next + react-i18next | en, pl (extendable) |
| Docs Landing | apps/docs/src/components/landing/ | Custom typed objects | en, pl |
NEVER import react-i18next directly. Always use useTranslation from @deenruv/react-ui-devkit:
// CORRECT
import { useTranslation } from "@deenruv/react-ui-devkit";
// WRONG — do NOT do this
import { useTranslation } from "react-i18next";
The devkit wrapper provides tEntity and binds to the correct i18next instance via window.__DEENRUV_SETTINGS__.i18n.
Create plugin-ui/translation-ns.ts using a Symbol-based namespace:
// plugins/<name>-plugin/src/plugin-ui/translation-ns.ts
export const translationNS = Symbol("<name>-plugin").toString();
The Symbol pattern ensures namespace uniqueness across all plugins. Use the plugin directory name as the Symbol label.
Create locale directories with JSON translations for both en and pl:
plugin-ui/locales/
├── en/
│ ├── index.ts # Barrel export
│ └── <feature>.json # English translations
└── pl/
├── index.ts # Barrel export
└── <feature>.json # Polish translations
JSON file (locales/en/<feature>.json):
{
"nav": {
"group": "My Feature",
"link": "Feature Settings"
},
"title": "Feature Configuration",
"form": {
"name": "Name",
"description": "Description",
"save": "Save Changes"
},
"modal": {
"add": { "title": "Add item", "action": "Add", "success": "Added successfully" },
"remove": { "title": "Remove item", "action": "Remove" },
"cancel": "Cancel"
}
}
Barrel file (locales/en/index.ts):
import feature from "./<feature>.json";
export default [feature];
The barrel must export a default array of JSON modules. If you have multiple JSON files per language, include them all:
export default [feature, settings];
Wire translations into createDeenruvUIPlugin:
// plugin-ui/index.tsx
import { createDeenruvUIPlugin } from "@deenruv/react-ui-devkit";
import pl from "./locales/pl";
import en from "./locales/en";
import { translationNS } from "./translation-ns.js";
export const MyFeatureUIPlugin = createDeenruvUIPlugin({
version: "1.0.0",
name: "my-feature-plugin-ui",
translations: { ns: translationNS, data: { en, pl } },
// pages, navMenuGroups, navMenuLinks, etc.
});
The framework calls i18next.addResourceBundle(lng, ns, translations) for each language at plugin registration time.
import { useTranslation } from "@deenruv/react-ui-devkit";
import { translationNS } from "../translation-ns.js";
export const FeaturePage = () => {
const { t } = useTranslation(translationNS);
return (
<div>
<h1>{t("title")}</h1>
<p>{t("form.description")}</p>
<button>{t("form.save")}</button>
</div>
);
};
Nav menu labels use labelId keys that the framework auto-prefixes with the plugin namespace:
navMenuGroups: [
{ id: "my-feature", labelId: "nav.group", placement: { groupId: "settings" } },
],
navMenuLinks: [
{ groupId: "my-feature", id: "feature-settings", labelId: "nav.link", href: "", icon: SettingsIcon },
],
The framework resolves labelId to ${ns}.nav.group automatically — just use the key relative to your JSON root.
The tEntity helper handles pluralized entity actions:
const { t, tEntity } = useTranslation("common");
tEntity("action.delete", "product", "one"); // "Delete product"
tEntity("action.delete", "product", "many"); // "Delete products"
tEntity("action.delete", "product", 5); // "Delete products" (count > 1)
Internally it translates the entity name via entity.<name> keys from the common namespace and injects it as a value interpolation variable.
File: apps/docs/src/components/landing/translations.ts
Translations are plain typed objects keyed by section, then by language:
export const translations = {
mySection: {
en: {
title: 'English Title',
description: 'English description',
},
pl: {
title: 'Polski Tytuł',
description: 'Opis po polsku',
},
},
};
Use the exported t() helper function:
import { t } from './translations';
// In component (lang comes from URL params or context)
const text = t('mySection', lang);
return <h1>{text.title}</h1>;
The t() function signature is t(section, lang) and falls back to en if the language key is missing.
Langexport type Lang = 'en' | 'pl';
When adding a new language, update this union type.
locales/de/) with matching JSON files and barrel index.tstranslations.data: { en, pl, de }translations.ts; update the Lang type unionreact-i18next directly instead of @deenruv/react-ui-devkittranslations in createDeenruvUIPluginuseTranslation(ns) — without it you get keys from "common" onlyen translations — always provide both en and plexport default [json1, json2]"form.save" not "formSave")translation-ns.ts with Symbol pattern)en and plindex.ts files export default arrays of JSON importstranslations property set in createDeenruvUIPlugin()useTranslation(translationNS) from @deenruv/react-ui-devkitlabelId keys match JSON key paths