From 2abfcbe2177a9db68e76a26e426fb5ce8f2d6ac1 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:36:36 +0100 Subject: [PATCH] auto-claude: subtask-1-3 - Document how to add new translation keys Added comprehensive documentation for adding translation keys: - Step-by-step guide with namespace identification - Detailed JSON structure explanation with real examples - Namespace organization patterns (meta, common, navigation, pages, etc.) - Code examples using useTranslations() hook - Advanced usage: dynamic keys, string interpolation, rich content - Best practices for key naming and structure consistency - Examples for arrays, objects, and nested translations Co-Authored-By: Claude Sonnet 4.5 --- docs/i18n/README.md | 457 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 446 insertions(+), 11 deletions(-) diff --git a/docs/i18n/README.md b/docs/i18n/README.md index 35bdaee..ffcd631 100644 --- a/docs/i18n/README.md +++ b/docs/i18n/README.md @@ -51,30 +51,465 @@ export default function MyComponent() { ### Adding a New Translation Key -1. Open all three locale files: - - `src/messages/de.json` - - `src/messages/en.json` - - `src/messages/sr.json` +#### Step-by-Step Guide -2. Add your key to the same namespace in each file: +Follow this process to add new translation keys to the application: + +##### 1. Identify the Appropriate Namespace + +Before adding a new translation key, determine where it should live based on its purpose: + +- **`meta`** - Site metadata, author information, SEO keywords +- **`common`** - Shared UI elements (buttons, navigation, errors, cookies) +- **`navigation`** - All navigation-related text (main menu, footer links, mobile menu) +- **`footer`** - Footer content (sections, legal links, copyright) +- **`pages`** - Page-specific content organized by route + - `pages.home` - Homepage content + - `pages.about` - About page content + - `pages.contact` - Contact page content + - `pages.privacy` - Privacy policy content + - `pages.terms` - Terms and conditions content + - `pages.notfound` - 404 page content +- **`portfolio`** - Portfolio page content and filters +- **`blog`** - Blog page content and UI elements +- **`login`** - Login page content +- **`dashboard`** - Dashboard content and metrics + +**Best Practice:** Use `common` for elements that appear across multiple pages. Use page-specific namespaces for content unique to a single page. + +##### 2. Open All Three Translation Files + +You must update all three locale files to maintain consistency: + +```bash +# Open in your editor: +src/messages/de.json # German (default) +src/messages/en.json # English +src/messages/sr.json # Serbian +``` + +##### 3. Add the Translation Key + +Add your key to the **exact same location** in each file. The JSON structure must be identical across all locales. + +**Example: Adding a new action button** ```json +// de.json { "common": { "actions": { - "newAction": "Neue Aktion" // de - "newAction": "New Action" // en - "newAction": "Nova Akcija" // sr + "readMore": "Weiterlesen", + "viewMore": "Mehr anzeigen", + "viewAll": "Alle anzeigen", + "download": "Herunterladen" // ← New key + } + } +} + +// en.json +{ + "common": { + "actions": { + "readMore": "Read More", + "viewMore": "View More", + "viewAll": "View All", + "download": "Download" // ← New key + } + } +} + +// sr.json +{ + "common": { + "actions": { + "readMore": "Procitaj vise", + "viewMore": "Prikazi vise", + "viewAll": "Prikazi sve", + "download": "Preuzmi" // ← New key } } } ``` -3. Use it in your component: +**Example: Adding a new page section** + +```json +// de.json +{ + "pages": { + "services": { // ← New namespace + "hero": { + "title": "Unsere Dienstleistungen", + "subtitle": "Maßgeschneiderte Lösungen für Ihr Unternehmen" + } + } + } +} + +// en.json +{ + "pages": { + "services": { // ← New namespace + "hero": { + "title": "Our Services", + "subtitle": "Tailored solutions for your business" + } + } + } +} + +// sr.json +{ + "pages": { + "services": { // ← New namespace + "hero": { + "title": "Naše usluge", + "subtitle": "Prilagođena rešenja za vaše poslovanje" + } + } + } +} +``` + +##### 4. Use the Translation in Your Component + +Import the `useTranslations` hook and specify the namespace: ```tsx -const t = useTranslations('common.actions'); -return ; +import { useTranslations } from 'next-intl'; + +export default function MyComponent() { + // Option 1: Access top-level namespace + const t = useTranslations('common.actions'); + + return ( + + ); +} +``` + +**Multiple namespaces in one component:** + +```tsx +import { useTranslations } from 'next-intl'; + +export default function ServicesPage() { + const tHero = useTranslations('pages.services.hero'); + const tCommon = useTranslations('common.actions'); + + return ( +
+

{tHero('title')}

+

{tHero('subtitle')}

+ +
+ ); +} +``` + +##### 5. Verify Your Changes + +1. **Build the application** to ensure no TypeScript errors: + ```bash + npm run build + ``` + +2. **Test in all three languages:** + - German: `http://localhost:3000/de` + - English: `http://localhost:3000/en` + - Serbian: `http://localhost:3000/sr` + +3. **Check for missing translations** - The browser console will warn if a key is missing + +#### JSON Structure & Organization + +##### Hierarchical Namespace Pattern + +Translation files use a **deeply nested JSON structure** to organize translations logically: + +```json +{ + "namespace": { // Top-level category + "subnamespace": { // Feature or section + "key": "value", // Leaf node (actual translation) + "nested": { + "key": "value" // Deeper nesting allowed + } + } + } +} +``` + +**Real example from the codebase:** + +```json +{ + "pages": { // Namespace: Page content + "home": { // Subnamespace: Homepage + "hero": { // Section: Hero section + "title": "FULLSTACK DEVELOPER", + "subtitle": "AI AGENTS | VOICE AI", + "navigation": { // Nested object + "experience": "EXPERIENCE", + "skills": "SKILLS" + } + } + } + } +} +``` + +**Access in code:** + +```tsx +const t = useTranslations('pages.home.hero'); +console.log(t('title')); // "FULLSTACK DEVELOPER" +console.log(t('navigation.experience')); // "EXPERIENCE" +``` + +##### Special Data Types + +**Arrays** - For lists of items: + +```json +{ + "meta": { + "site": { + "keywords": [ + "Fullstack Developer", + "AI Developer Germany", + "Process Automation" + ] + } + } +} +``` + +**Usage:** + +```tsx +const t = useTranslations('meta.site'); +const keywords = t.raw('keywords'); // Returns array +``` + +**Objects** - For structured data: + +```json +{ + "pages": { + "home": { + "experience": { + "positions": [ + { + "role": "Process Automation Specialist", + "company": "Everlast Consulting GmbH", + "period": "12/2024 - PRESENT", + "highlights": [ + "Development of AI agents", + "Web scraping solutions" + ] + } + ] + } + } + } +} +``` + +**Usage:** + +```tsx +const t = useTranslations('pages.home.experience'); +const positions = t.raw('positions'); // Returns array of objects + +positions.map((position) => ( +
+

{position.role}

+

{position.company}

+ {position.period} +
    + {position.highlights.map((highlight) => ( +
  • {highlight}
  • + ))} +
+
+)); +``` + +#### Advanced Usage Examples + +##### Dynamic Translation Keys + +Access translations dynamically based on variables: + +```tsx +import { useTranslations } from 'next-intl'; + +export default function CategoryFilter({ categories }: { categories: string[] }) { + const t = useTranslations('portfolio.filters.categories'); + + return ( +
+ {categories.map((category) => ( + + ))} +
+ ); +} + +// If categories = ["Data Science", "AI Development"] +// Output: "Data Science" → "Data Science" (en) or "Data Science" (de) +// "AI Development" → "AI Development" (en) or "KI-Entwicklung" (de) +``` + +##### String Interpolation + +Use variables in translations: + +```tsx +import { useTranslations } from 'next-intl'; + +export default function Pagination({ start, end, total }: PaginationProps) { + const t = useTranslations('blog.ui.pagination'); + + // Translation: "Showing {start}-{end} of {total} articles" + return ( +

+ {t('showing', { start, end, total })} +

+ ); +} +``` + +**In JSON:** + +```json +{ + "blog": { + "ui": { + "pagination": { + "showing": "Showing {start}-{end} of {total} articles" + } + } + } +} +``` + +##### Accessing Rich Content + +For translations containing HTML or rich content: + +```tsx +import { useTranslations } from 'next-intl'; + +export default function RichText() { + const t = useTranslations('pages.about'); + + return ( +
+ {/* Simple text */} +

{t('hero.title')}

+ + {/* Raw access for objects/arrays */} +
+ {t.raw('hero.expertise.processAutomation')} +
+
+ ); +} +``` + +#### Common Patterns & Best Practices + +##### ✅ DO: Keep Structure Consistent + +```json +// Good: Same structure across all locales +// de.json +{ "common": { "actions": { "save": "Speichern" } } } + +// en.json +{ "common": { "actions": { "save": "Save" } } } + +// sr.json +{ "common": { "actions": { "save": "Sačuvaj" } } } +``` + +##### ❌ DON'T: Mix Structures + +```json +// Bad: Different structures +// de.json +{ "common": { "actions": { "save": "Speichern" } } } + +// en.json +{ "common": { "save": "Save" } } // ❌ Missing "actions" level +``` + +##### ✅ DO: Use Semantic Naming + +```json +{ + "common": { + "actions": { + "submit": "Submit", // ✅ Clear, semantic + "cancel": "Cancel", + "delete": "Delete" + } + } +} +``` + +##### ❌ DON'T: Use Generic Names + +```json +{ + "common": { + "button1": "Submit", // ❌ Non-descriptive + "btn": "Cancel", // ❌ Abbreviated + "text1": "Delete" // ❌ Generic + } +} +``` + +##### ✅ DO: Group Related Translations + +```json +{ + "pages": { + "contact": { + "contactForm": { // ✅ Grouped by feature + "title": "Contact Me", + "name": { "label": "Name", "placeholder": "Your name" }, + "email": { "label": "Email", "placeholder": "your@email.com" }, + "submit": "Send Message" + } + } + } +} +``` + +##### ✅ DO: Reuse Common Translations + +```tsx +// Good: Reuse common translations +import { useTranslations } from 'next-intl'; + +export default function MyForm() { + const tCommon = useTranslations('common.actions'); + + return ( +
+ + +
+ ); +} ``` ## Documentation Structure