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 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 06:36:36 +01:00
co-authored by Claude Sonnet 4.5
parent 1b148fa574
commit 2abfcbe217
+446 -11
View File
@@ -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 <button>{t('newAction')}</button>;
import { useTranslations } from 'next-intl';
export default function MyComponent() {
// Option 1: Access top-level namespace
const t = useTranslations('common.actions');
return (
<button className="btn-primary">
{t('download')}
</button>
);
}
```
**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 (
<div>
<h1>{tHero('title')}</h1>
<p>{tHero('subtitle')}</p>
<button>{tCommon('readMore')}</button>
</div>
);
}
```
##### 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) => (
<div key={position.role}>
<h3>{position.role}</h3>
<p>{position.company}</p>
<span>{position.period}</span>
<ul>
{position.highlights.map((highlight) => (
<li key={highlight}>{highlight}</li>
))}
</ul>
</div>
));
```
#### 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 (
<div>
{categories.map((category) => (
<button key={category}>
{t(category as any)} // Dynamic key lookup
</button>
))}
</div>
);
}
// 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 (
<p>
{t('showing', { start, end, total })}
</p>
);
}
```
**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 (
<div>
{/* Simple text */}
<h1>{t('hero.title')}</h1>
{/* Raw access for objects/arrays */}
<div>
{t.raw('hero.expertise.processAutomation')}
</div>
</div>
);
}
```
#### 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 (
<form>
<button type="submit">{tCommon('submit')}</button>
<button type="button">{tCommon('cancel')}</button>
</form>
);
}
```
## Documentation Structure