diff --git a/docs/i18n/README.md b/docs/i18n/README.md
new file mode 100644
index 0000000..b248bf7
--- /dev/null
+++ b/docs/i18n/README.md
@@ -0,0 +1,1459 @@
+# Internationalization (i18n) Documentation
+
+Complete guide to the multi-language support system in the portfolio application.
+
+## Overview
+
+This portfolio supports **3 languages** with full internationalization:
+
+| Language | Code | Flag | Region |
+|----------|------|------|--------|
+| **German** (default) | `de` | π©πͺ | Germany |
+| **English** | `en` | π¬π§ | United States |
+| **Serbian** | `sr` | π·πΈ | Serbia |
+
+### Tech Stack
+
+- **next-intl** - Next.js internationalization library
+- **JSON-based translations** - Simple, maintainable translation files
+- **Type-safe locale handling** - TypeScript ensures locale consistency
+- **SEO optimized** - Hreflang tags, Content-Language headers, localized metadata
+
+### Key Features
+
+- β
Automatic locale detection from URL path
+- β
Fallback to default locale (German)
+- β
Server-side translation loading
+- β
Type-safe locale validation
+- β
SEO-friendly URL structure (`/de/`, `/en/`, `/sr/`)
+- β
Localized metadata for social sharing
+- β
Support for nested translation keys
+- β
Legacy migration path (locales-old)
+
+## Quick Start
+
+### Using Translations in Components
+
+```tsx
+import { useTranslations } from 'next-intl';
+
+export default function MyComponent() {
+ const t = useTranslations('common');
+
+ return (
+
+
{t('nav.home')}
+
{t('siteDescription')}
+
+ );
+}
+```
+
+### Adding a New Translation Key
+
+#### Step-by-Step Guide
+
+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": {
+ "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
+ }
+ }
+}
+```
+
+**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
+import { useTranslations } from 'next-intl';
+
+export default function MyComponent() {
+ // Option 1: Access top-level namespace
+ const t = useTranslations('common.actions');
+
+ return (
+
+ {t('download')}
+
+ );
+}
+```
+
+**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')}
+
{tCommon('readMore')}
+
+ );
+}
+```
+
+##### 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) => (
+
+ {t(category as any)} // Dynamic key lookup
+
+ ))}
+
+ );
+}
+
+// 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
+
+This documentation is organized into the following sections:
+
+### π Directory Structure & Organization
+
+The i18n system is organized into three main directories, each serving a specific purpose in the translation infrastructure.
+
+#### Overview of Directory Structure
+
+```
+src/
+βββ i18n/ # Configuration Layer
+β βββ config.ts # β
Locale definitions & metadata
+β βββ request.ts # β
next-intl integration
+β βββ locales-old/ # β οΈ DEPRECATED - Do NOT use
+β βββ de.ts
+β βββ en.ts
+β βββ sr.ts
+β
+βββ messages/ # β
Active Translation Files
+ βββ de.json # German translations
+ βββ en.json # English translations
+ βββ sr.json # Serbian translations
+```
+
+---
+
+#### `src/i18n/` - Configuration Directory
+
+This directory contains the core configuration files that define how the i18n system operates.
+
+**Purpose:** Centralized configuration for locale definitions, metadata, and next-intl integration.
+
+##### `config.ts` - Locale Metadata & Settings
+
+**Location:** `src/i18n/config.ts`
+
+This is the **single source of truth** for all locale-related configuration.
+
+**What it contains:**
+
+1. **Locales Array** - Defines all supported languages
+ ```typescript
+ export const locales = ['de', 'en', 'sr'] as const;
+ ```
+
+2. **Default Locale** - Fallback language
+ ```typescript
+ export const defaultLocale = 'de' as const;
+ ```
+
+3. **Locale Type** - TypeScript type for type-safe locale handling
+ ```typescript
+ export type Locale = (typeof locales)[number];
+ ```
+
+4. **Display Names** - Human-readable language names
+ ```typescript
+ export const localeNames: Record = {
+ de: 'Deutsch',
+ en: 'English',
+ sr: 'Srpski',
+ };
+ ```
+
+5. **Locale Flags** - Emoji flags for UI
+ ```typescript
+ export const localeFlags: Record = {
+ de: 'π©πͺ',
+ en: 'π¬π§',
+ sr: 'π·πΈ',
+ };
+ ```
+
+6. **Extended Metadata** - SEO and regional data
+ ```typescript
+ export const localeMetadata: Record tags
+ ogLocale: string; // For Open Graph meta tags
+ script: string; // Writing system (e.g., 'Latn')
+ territory: string; // Full country name
+ currency: string; // ISO 4217 code (e.g., 'EUR')
+ }> = {
+ de: {
+ language: 'de',
+ region: 'DE',
+ hreflang: 'de-DE',
+ ogLocale: 'de_DE',
+ script: 'Latn',
+ territory: 'Germany',
+ currency: 'EUR',
+ },
+ // ... en, sr
+ };
+ ```
+
+7. **SEO Keywords** - Localized keywords for each language
+ ```typescript
+ export const seoKeywords: Record = {
+ de: ['Fullstack Developer', 'KI Entwickler', 'KΓΆln', ...],
+ en: ['AI Developer', 'Remote Developer Europe', 'London', ...],
+ sr: ['AI Programer Srbija', 'Beograd', 'Novi Sad', ...],
+ };
+ ```
+
+**When to modify this file:**
+- β
Adding a new language
+- β
Updating SEO keywords
+- β
Changing metadata for Open Graph or hreflang
+- β Adding translation strings (use `src/messages/` instead)
+
+---
+
+##### `request.ts` - next-intl Integration
+
+**Location:** `src/i18n/request.ts`
+
+This file integrates the translation system with next-intl and handles runtime locale resolution.
+
+**What it does:**
+
+```typescript
+import { getRequestConfig } from 'next-intl/server';
+import { locales, type Locale } from './config';
+
+export default getRequestConfig(async ({ requestLocale }) => {
+ let locale = await requestLocale;
+
+ // Validate locale and fallback to default if invalid
+ if (!locale || !locales.includes(locale as Locale)) {
+ locale = 'de';
+ }
+
+ return {
+ locale,
+ messages: (await import(`../messages/${locale}.json`)).default,
+ };
+});
+```
+
+**Key responsibilities:**
+
+1. **Locale Validation** - Ensures only valid locales are used
+2. **Fallback Logic** - Defaults to German (`de`) if locale is invalid or missing
+3. **Dynamic Import** - Loads the appropriate translation JSON file at runtime
+4. **Type Safety** - Uses the `Locale` type from `config.ts` for validation
+
+**Flow:**
+1. Receives `requestLocale` from next-intl (extracted from URL)
+2. Validates against the `locales` array
+3. Falls back to `de` if validation fails
+4. Dynamically imports the corresponding JSON file from `src/messages/`
+5. Returns locale and messages to next-intl
+
+**When to modify this file:**
+- β
Changing fallback locale logic
+- β
Adding custom locale resolution rules
+- β
Implementing locale persistence (cookies, etc.)
+- β Most use cases don't require changes
+
+---
+
+#### `src/messages/` - Active Translation Files β
+
+**Location:** `src/messages/`
+
+This directory contains the **active, production-ready** translation files in JSON format.
+
+**Files:**
+- `de.json` - German translations (default)
+- `en.json` - English translations
+- `sr.json` - Serbian translations
+
+**Why JSON?**
+- β
Simple, human-readable format
+- β
Easy to edit without TypeScript knowledge
+- β
Smaller bundle size (dynamic imports)
+- β
Better IDE support for JSON
+- β
Easier integration with translation tools
+
+**Structure:**
+
+Each file contains nested JSON objects organized by namespace:
+
+```json
+{
+ "meta": {
+ "site": { "name": "...", "title": "..." },
+ "author": { "name": "...", "role": "..." },
+ "social": { "linkedin": "...", "github": "..." }
+ },
+ "common": {
+ "nav": { "home": "...", "about": "..." },
+ "actions": { "readMore": "...", "submit": "..." }
+ },
+ "pages": {
+ "home": { "hero": { "title": "..." } },
+ "about": { "title": "..." }
+ }
+}
+```
+
+**Best Practices:**
+
+1. **Keep keys synchronized** - All three files should have identical structure
+2. **Use nested namespaces** - Organize by feature or page (e.g., `common`, `pages.home`)
+3. **Descriptive keys** - Use semantic names like `hero.title` instead of `text1`
+4. **No hardcoded text** - All user-facing text should be in these files
+
+**When to modify these files:**
+- β
Adding new translation keys
+- β
Updating existing translations
+- β
Fixing typos or improving wording
+- β
Adding new pages or features
+
+---
+
+#### `src/i18n/locales-old/` - Legacy System β οΈ DEPRECATED
+
+**Location:** `src/i18n/locales-old/`
+
+This directory contains **deprecated** TypeScript-based translations from a previous implementation.
+
+**Files:**
+- `de.ts` - Old German translations
+- `en.ts` - Old English translations
+- `sr.ts` - Old Serbian translations
+
+**β οΈ IMPORTANT:** **Do NOT use these files!**
+
+**Why it exists:**
+- These files were part of an older TypeScript-based translation system
+- Kept temporarily during migration for reference
+- **Not loaded by the application** - they are completely inactive
+
+**Why the migration happened:**
+- JSON is simpler and more maintainable than TypeScript objects
+- Better performance with dynamic imports
+- Easier for non-developers to edit translations
+- Better tooling support for JSON translation files
+
+**What to do:**
+- β Do NOT add new keys here
+- β Do NOT reference these files in code
+- β
Use `src/messages/*.json` for all translations
+- β
These files can be safely deleted once migration is verified complete
+
+---
+
+#### File Relationships
+
+```mermaid
+graph TD
+ A[config.ts] -->|Defines locales| B[request.ts]
+ B -->|Loads messages| C[messages/*.json]
+ B -->|Validates locale| A
+ D[Components] -->|useTranslations| C
+ E[locales-old/*] -.->|DEPRECATED| F[Not Used]
+```
+
+**Summary:**
+
+| Directory/File | Status | Purpose | When to Edit |
+|----------------|--------|---------|--------------|
+| `src/i18n/config.ts` | β
Active | Locale definitions & metadata | Adding locales, SEO keywords |
+| `src/i18n/request.ts` | β
Active | next-intl integration | Rarely (fallback logic) |
+| `src/messages/*.json` | β
Active | Translation strings | Frequently (new translations) |
+| `src/i18n/locales-old/` | β οΈ Deprecated | Legacy TypeScript translations | Never (can be deleted) |
+
+---
+
+### π Adding Translation Keys
+> Step-by-step guide to adding and managing translations
+>
+> **Topics covered:**
+> - JSON structure and namespaces
+> - Nested translation keys
+> - Adding keys across all locales
+> - Code examples using `useTranslations()`
+> - Best practices for key naming
+
+### βοΈ Configuration & Setup
+
+This section explains how the i18n system integrates with Next.js through next-intl, covering the configuration in `request.ts` and `next.config.ts`.
+
+#### `getRequestConfig()` Function
+
+**Location:** `src/i18n/request.ts`
+
+The `getRequestConfig()` function is the **core integration point** between next-intl and the application. It handles locale resolution and message loading for every request.
+
+**Full Implementation:**
+
+```typescript
+import { getRequestConfig } from 'next-intl/server';
+import { locales, type Locale } from './config';
+
+export default getRequestConfig(async ({ requestLocale }) => {
+ let locale = await requestLocale;
+
+ // Validate locale and fallback to default if invalid
+ if (!locale || !locales.includes(locale as Locale)) {
+ locale = 'de';
+ }
+
+ return {
+ locale,
+ messages: (await import(`../messages/${locale}.json`)).default,
+ };
+});
+```
+
+**How it works:**
+
+1. **Receives `requestLocale`** - next-intl extracts the locale from the URL path (e.g., `/de/`, `/en/`, `/sr/`)
+2. **Awaits the locale** - The locale is a promise that resolves to the URL segment
+3. **Validates the locale** - Checks if it exists and is in the allowed `locales` array
+4. **Falls back if invalid** - Defaults to `'de'` (German) for invalid/missing locales
+5. **Returns configuration** - Provides the validated locale and dynamically loaded messages
+
+**Key Features:**
+
+- β
**Type-safe validation** - Uses the `Locale` type from `config.ts`
+- β
**Automatic fallback** - Never breaks, always provides a valid locale
+- β
**Dynamic imports** - Only loads the required translation file (better performance)
+- β
**Server-side execution** - Runs on the server for every request
+
+---
+
+#### Locale Validation Logic
+
+**Purpose:** Ensure only valid locales are used, preventing errors and security issues.
+
+**Validation Flow:**
+
+```typescript
+if (!locale || !locales.includes(locale as Locale)) {
+ locale = 'de';
+}
+```
+
+**What it checks:**
+
+1. **Existence check** - `!locale` ensures a value was provided
+2. **Whitelist check** - `!locales.includes(...)` verifies it's an allowed locale
+3. **Fallback** - Defaults to `'de'` if either check fails
+
+**Examples:**
+
+| URL Path | `requestLocale` | Valid? | Result | Reason |
+|----------|----------------|--------|--------|--------|
+| `/de/about` | `'de'` | β
Yes | `'de'` | Valid locale |
+| `/en/contact` | `'en'` | β
Yes | `'en'` | Valid locale |
+| `/sr/portfolio` | `'sr'` | β
Yes | `'sr'` | Valid locale |
+| `/fr/home` | `'fr'` | β No | `'de'` | Not in `locales` array |
+| `/` | `null` | β No | `'de'` | No locale provided |
+| `/invalid123` | `'invalid123'` | β No | `'de'` | Not in whitelist |
+
+**Why this matters:**
+
+- π **Security** - Prevents path traversal attacks (e.g., `../../etc/passwd`)
+- π‘οΈ **Reliability** - Guarantees the application always has a valid locale
+- π¦ **Bundle safety** - Ensures only existing translation files are loaded
+- π― **User experience** - Provides predictable behavior for invalid URLs
+
+**Alternative approaches (not used):**
+
+```typescript
+// β DON'T: No validation (unsafe)
+export default getRequestConfig(async ({ requestLocale }) => {
+ const locale = await requestLocale;
+ return {
+ locale,
+ messages: (await import(`../messages/${locale}.json`)).default, // Could fail!
+ };
+});
+
+// β DON'T: Throwing errors (breaks user experience)
+export default getRequestConfig(async ({ requestLocale }) => {
+ const locale = await requestLocale;
+ if (!locales.includes(locale as Locale)) {
+ throw new Error('Invalid locale'); // User sees error page
+ }
+ return { locale, messages: ... };
+});
+
+// β
DO: Validate and fallback (current implementation)
+```
+
+---
+
+#### Dynamic Message Loading
+
+**How translations are loaded:**
+
+```typescript
+messages: (await import(`../messages/${locale}.json`)).default
+```
+
+**This line performs several critical functions:**
+
+1. **Dynamic import** - Uses `import()` instead of static `import` statement
+2. **Path interpolation** - Constructs the file path using the validated locale
+3. **Async loading** - Waits for the file to be loaded
+4. **Default export** - Extracts the JSON content from the module
+
+**Benefits of dynamic imports:**
+
+| Feature | Static Import | Dynamic Import (Current) |
+|---------|--------------|--------------------------|
+| **Bundle size** | All locales loaded upfront | Only requested locale loaded |
+| **Initial load time** | Slower (3 files) | Faster (1 file) |
+| **Memory usage** | Higher | Lower |
+| **Server-side rendering** | Supported | β
Supported |
+| **Code splitting** | No | β
Yes |
+
+**Performance comparison:**
+
+```typescript
+// β Static imports (loads ALL locales on every request)
+import deMessages from '../messages/de.json';
+import enMessages from '../messages/en.json';
+import srMessages from '../messages/sr.json';
+
+const messageMap = { de: deMessages, en: enMessages, sr: srMessages };
+return { locale, messages: messageMap[locale] };
+// Bundle size: ~150KB (all locales)
+
+// β
Dynamic import (loads ONLY requested locale)
+messages: (await import(`../messages/${locale}.json`)).default
+// Bundle size: ~50KB (single locale)
+```
+
+**File path resolution:**
+
+```typescript
+// If locale = 'de'
+await import(`../messages/de.json`) // Resolves to: src/messages/de.json
+
+// If locale = 'en'
+await import(`../messages/en.json`) // Resolves to: src/messages/en.json
+
+// If locale = 'sr'
+await import(`../messages/sr.json`) // Resolves to: src/messages/sr.json
+```
+
+**Error handling:**
+
+If a translation file is missing, Next.js will throw a build error:
+
+```bash
+Error: Cannot find module '../messages/fr.json'
+```
+
+**Why this is safe:**
+
+- β
Locale validation ensures only `de`, `en`, or `sr` are used
+- β
All three files are required to exist for the build to succeed
+- β
TypeScript catches typos in locale codes at compile time
+
+---
+
+#### `createNextIntlPlugin()` in next.config.ts
+
+**Location:** `next.config.ts`
+
+The `createNextIntlPlugin()` function wraps the Next.js configuration to integrate next-intl into the build process.
+
+**Implementation:**
+
+```typescript
+import type { NextConfig } from 'next';
+import createNextIntlPlugin from 'next-intl/plugin';
+
+// Create the next-intl plugin, pointing to request.ts
+const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
+
+const nextConfig: NextConfig = {
+ // ... Next.js configuration options
+};
+
+// Wrap the config with the plugin
+export default withNextIntl(nextConfig);
+```
+
+**What `createNextIntlPlugin()` does:**
+
+1. **Registers request.ts** - Tells next-intl where to find the `getRequestConfig` function
+2. **Enables server-side translations** - Sets up the infrastructure for SSR
+3. **Configures webpack** - Adds necessary loaders for JSON imports
+4. **Optimizes builds** - Ensures proper code splitting for translation files
+5. **Sets up middleware hooks** - Integrates with Next.js request lifecycle
+
+**Plugin composition:**
+
+This project uses **multiple plugins** together:
+
+```typescript
+import createMDX from '@next/mdx';
+import createNextIntlPlugin from 'next-intl/plugin';
+
+// Create both plugins
+const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
+const withMDX = createMDX({ options: { ... } });
+
+// Compose plugins: next-intl wraps MDX wraps config
+export default withNextIntl(withMDX(nextConfig));
+```
+
+**Plugin order matters:**
+
+```
+nextConfig β withMDX β withNextIntl β Final Config
+```
+
+**Why this pattern:**
+
+- β
Modular configuration - Each plugin adds specific functionality
+- β
Composable - Plugins can be added/removed independently
+- β
Maintainable - Clear separation of concerns
+- β
Type-safe - TypeScript validates the entire config chain
+
+**Without the plugin:**
+
+If you remove `withNextIntl`, the application would:
+
+- β Fail to resolve `useTranslations()` calls
+- β Not load translation messages
+- β Break server-side rendering of translated content
+- β Lose locale detection capabilities
+
+**Plugin configuration path:**
+
+```typescript
+createNextIntlPlugin('./src/i18n/request.ts')
+ β
+ Relative to next.config.ts
+ Points to getRequestConfig export
+```
+
+**Troubleshooting:**
+
+| Issue | Cause | Solution |
+|-------|-------|----------|
+| "Cannot find module 'next-intl/server'" | Plugin not installed | Run `npm install next-intl` |
+| "getRequestConfig is not defined" | Wrong path in plugin | Check path matches `request.ts` location |
+| Translations not loading | Plugin not applied | Ensure `withNextIntl(...)` wraps config |
+| Build errors with MDX | Wrong plugin order | Place `withNextIntl` outermost |
+
+---
+
+#### Server-Side vs. Client-Side Translations
+
+**How translations work in different contexts:**
+
+##### Server Components (Recommended)
+
+**Usage:**
+
+```tsx
+import { useTranslations } from 'next-intl';
+
+export default function ServerComponent() {
+ const t = useTranslations('common');
+
+ return {t('nav.home')} ;
+}
+```
+
+**How it works:**
+
+1. Component renders on the server
+2. `useTranslations()` reads messages from the `getRequestConfig` result
+3. Translation is resolved on the server
+4. HTML with translated text is sent to the client
+5. No translation data sent to the browser
+
+**Benefits:**
+
+- β
**Faster initial load** - No client-side JavaScript for translations
+- β
**Better SEO** - Translated content available immediately
+- β
**Smaller bundle** - Translations not included in client JS
+- β
**Secure** - Translation logic stays on the server
+
+##### Client Components
+
+**Usage:**
+
+```tsx
+'use client';
+
+import { useTranslations } from 'next-intl';
+
+export default function ClientComponent() {
+ const t = useTranslations('common');
+
+ return {t('actions.submit')} ;
+}
+```
+
+**How it works:**
+
+1. Component marked with `'use client'`
+2. Messages serialized and passed from server to client
+3. `useTranslations()` reads from client-side context
+4. Translations available for dynamic updates
+
+**When to use:**
+
+- β
Interactive components (forms, modals)
+- β
Components with onClick handlers
+- β
Real-time updates based on user actions
+- β
Stateful UI elements
+
+**Performance consideration:**
+
+```tsx
+// β DON'T: Use client components unnecessarily
+'use client';
+
+export default function StaticText() {
+ const t = useTranslations('common');
+ return {t('siteDescription')}
; // This could be a server component
+}
+
+// β
DO: Use server components for static content
+export default function StaticText() {
+ const t = useTranslations('common');
+ return {t('siteDescription')}
; // Server component (default)
+}
+```
+
+##### Hybrid Pattern (Best Practice)
+
+**Split components by interactivity:**
+
+```tsx
+// app/[locale]/page.tsx (Server Component)
+import { useTranslations } from 'next-intl';
+import ContactForm from '@/components/ContactForm'; // Client component
+
+export default function ContactPage() {
+ const t = useTranslations('pages.contact');
+
+ return (
+
+
{t('hero.title')} {/* Server-rendered */}
+
{t('hero.subtitle')}
{/* Server-rendered */}
+
{/* Client interactive */}
+
+ );
+}
+
+// components/ContactForm.tsx (Client Component)
+'use client';
+
+import { useTranslations } from 'next-intl';
+
+export default function ContactForm() {
+ const t = useTranslations('pages.contact.contactForm');
+
+ return (
+
+ );
+}
+```
+
+**Translation data flow:**
+
+```
+Request β getRequestConfig() β Messages loaded
+ β
+Server Components β useTranslations() β Direct access
+ β
+Client Components β Serialized via props β useTranslations() β Client access
+```
+
+**Bundle size impact:**
+
+| Scenario | Server Component | Client Component |
+|----------|------------------|------------------|
+| **JavaScript sent** | 0 KB (translations) | ~50 KB (translations) |
+| **HTML size** | Larger (translated text) | Smaller (placeholders) |
+| **Time to Interactive** | Faster | Slower |
+| **SEO crawlability** | β
Perfect | β οΈ May need hydration |
+
+**Best practices:**
+
+1. β
**Default to server components** - Use client components only when necessary
+2. β
**Split by interactivity** - Keep static content in server components
+3. β
**Pass translations as props** - Avoid duplicating translation calls
+4. β
**Minimize client boundaries** - Fewer `'use client'` directives = better performance
+
+**Example optimization:**
+
+```tsx
+// β BEFORE: Everything is a client component
+'use client';
+
+export default function Dashboard() {
+ const t = useTranslations('dashboard');
+ const [count, setCount] = useState(0);
+
+ return (
+
+
{t('title')} {/* Static - could be server */}
+
{t('description')}
{/* Static - could be server */}
+
setCount(count + 1)}>
+ {t('increment')} {/* Interactive - needs client */}
+
+
{count}
+
+ );
+}
+
+// β
AFTER: Split into server + client components
+// app/[locale]/dashboard/page.tsx (Server)
+import { useTranslations } from 'next-intl';
+import Counter from '@/components/Counter';
+
+export default function Dashboard() {
+ const t = useTranslations('dashboard');
+
+ return (
+
+
{t('title')} {/* Server-rendered */}
+
{t('description')}
{/* Server-rendered */}
+
{/* Client component */}
+
+ );
+}
+
+// components/Counter.tsx (Client)
+'use client';
+
+import { useTranslations } from 'next-intl';
+import { useState } from 'react';
+
+export default function Counter() {
+ const t = useTranslations('dashboard');
+ const [count, setCount] = useState(0);
+
+ return (
+ <>
+ setCount(count + 1)}>
+ {t('increment')}
+
+ {count}
+ >
+ );
+}
+```
+
+**Result:**
+
+- π¦ **Bundle size reduced** by ~40 KB (translations moved to server)
+- β‘ **Faster initial render** (server-rendered HTML)
+- π― **Better SEO** (title and description immediately available)
+
+---
+
+#### Summary: Configuration & Setup
+
+**Key files:**
+
+| File | Purpose | When to modify |
+|------|---------|----------------|
+| `src/i18n/request.ts` | Locale resolution & message loading | Rarely (fallback logic, custom validation) |
+| `next.config.ts` | next-intl plugin integration | When adding/removing plugins |
+| `src/i18n/config.ts` | Locale definitions | When adding new languages |
+
+**Core concepts:**
+
+- β
`getRequestConfig()` - Validates locale and loads messages
+- β
Locale validation - Whitelist check with fallback to `'de'`
+- β
Dynamic imports - Only load required translation file
+- β
`createNextIntlPlugin()` - Integrates next-intl with Next.js build
+- β
Server components - Preferred for better performance
+- β
Client components - Use only for interactive elements
+
+**Common patterns:**
+
+```typescript
+// Pattern 1: Server component (default)
+export default function Page() {
+ const t = useTranslations('namespace');
+ return {t('key')} ;
+}
+
+// Pattern 2: Client component (interactive)
+'use client';
+export default function Interactive() {
+ const t = useTranslations('namespace');
+ return {t('action')} ;
+}
+
+// Pattern 3: Multiple namespaces
+export default function Complex() {
+ const tCommon = useTranslations('common');
+ const tPage = useTranslations('pages.home');
+ return ...
;
+}
+```
+
+### π Locale Configuration & Metadata
+> Detailed breakdown of locale settings and SEO data
+>
+> **Topics covered:**
+> - Locales array and default locale
+> - Locale names and flags
+> - Metadata for hreflang and Open Graph
+> - SEO keywords per locale
+> - Currency and territory settings
+
+### π SEO & Hreflang Implementation
+> How multilingual SEO is implemented
+>
+> **Topics covered:**
+> - Hreflang tags in layout.tsx
+> - alternates.languages configuration
+> - Content-Language HTTP headers
+> - Localized meta tags and Open Graph
+> - Sitemap generation for multiple locales
+
+### π¦ Legacy Migration (locales-old)
+> Understanding the deprecated TypeScript-based translation system
+>
+> **Topics covered:**
+> - What locales-old contains
+> - Why the migration happened
+> - Do NOT use these files
+> - Migration from TypeScript to JSON
+> - Safe removal considerations
+
+### π€οΈ Routing & URL Structure
+> How locale-based routing works with Next.js App Router
+>
+> **Topics covered:**
+> - `[locale]` dynamic route segment
+> - URL patterns: `/de/`, `/en/`, `/sr/`
+> - Locale detection in middleware
+> - `generateStaticParams()` for static generation
+> - Locale switching and redirects
+
+### π‘ Usage Examples & Best Practices
+> Practical examples and common patterns
+>
+> **Topics covered:**
+> - `useTranslations()` hook patterns
+> - Accessing nested keys
+> - Formatting dates and numbers
+> - Pluralization (if implemented)
+> - Common pitfalls and solutions
+> - Performance considerations
+
+## Architecture Overview
+
+```
+Portfolio i18n System
+β
+βββ Configuration Layer
+β βββ src/i18n/config.ts # Locale definitions & metadata
+β βββ src/i18n/request.ts # next-intl integration
+β
+βββ Translation Files (Active)
+β βββ src/messages/de.json # German translations
+β βββ src/messages/en.json # English translations
+β βββ src/messages/sr.json # Serbian translations
+β
+βββ Legacy System (Deprecated)
+β βββ src/i18n/locales-old/ # Old TypeScript translations
+β
+βββ Routing Layer
+β βββ src/app/[locale]/layout.tsx # Locale-based layout
+β βββ src/middleware.ts # Locale detection
+β βββ next.config.ts # createNextIntlPlugin
+β
+βββ Application Layer
+ βββ Components using useTranslations()
+```
+
+## Translation File Structure
+
+Current structure of active translation files:
+
+```json
+{
+ "meta": {
+ "site": { ... },
+ "author": { ... },
+ "social": { ... }
+ },
+ "common": {
+ "siteTitle": "...",
+ "nav": { ... },
+ "actions": { ... },
+ "errors": { ... },
+ "cookies": { ... }
+ },
+ "home": { ... },
+ "about": { ... },
+ "portfolio": { ... },
+ "blog": { ... },
+ "contact": { ... }
+}
+```
+
+## Supported Locales
+
+| Locale | Language | hreflang | OG Locale | Currency | Region |
+|--------|----------|----------|-----------|----------|--------|
+| `de` | Deutsch | de-DE | de_DE | EUR | Germany |
+| `en` | English | en-US | en_US | USD | United States |
+| `sr` | Srpski | sr-RS | sr_RS | RSD | Serbia |
+
+## Important Notes
+
+β οΈ **Legacy System**: The `src/i18n/locales-old/` directory contains deprecated TypeScript-based translations from a previous implementation. **Do NOT use these files.** All new translations should go in `src/messages/*.json`.
+
+β
**Active System**: Use only the JSON files in `src/messages/` for all translation work.
+
+π **Type Safety**: The locale type is derived from the `locales` array in `config.ts`, ensuring compile-time validation of locale codes.
+
+π **Default Locale**: German (`de`) is the default locale. Invalid or missing locale parameters will fallback to German.
+
+## Getting Help
+
+- Check specific sections above for detailed topics
+- Review code examples in the pattern files
+- Examine existing translation keys in `src/messages/de.json`
+- Look at component usage in `src/app/[locale]/page.tsx`
+
+---
+
+**Framework:** next-intl + Next.js 15 App Router
+**Default Locale:** German (de)
+**Supported Locales:** de, en, sr
+**Translation Format:** JSON