> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/RicardoAlejandroSantillan/dev-showcase/llms.txt
> Use this file to discover all available pages before exploring further.

# Localization System

> Multilingual content management, language switching, and JSON-based translations

## Overview

Dev Showcase implements a **client-side localization system** using JSON files for English and Spanish translations. The system supports dynamic language switching without page reloads and persists user language preferences using cookies.

## Architecture Components

<CardGroup cols={3}>
  <Card title="Language Files" icon="file-code">
    JSON files containing all translatable content
  </Card>

  <Card title="Cookie Persistence" icon="cookie">
    Stores user's language preference
  </Card>

  <Card title="JavaScript Loader" icon="js">
    Dynamically loads and applies translations
  </Card>
</CardGroup>

## Language Files Structure

Translations are stored in JSON files located in `wwwroot/languages/`:

```
wwwroot/
└── languages/
    ├── en.json    # English translations
    └── es.json    # Spanish translations
```

### JSON Structure

Each language file contains hierarchically organized content:

```json wwwroot/languages/en.json:1 theme={null}
{
  "typewriter": {
    "prefix": "I'm ",
    "phrases": [
      "Ricardo Alejandro Pérez Santillán",
      "a Data enthusiast",
      "an automation addict"
    ]
  },
  "header": {
    "hello": "#HELLO WORLD",
    "name": "I'm Ricardo Alejandro Pérez Santillán"
  },
  "navigation": {
    "introduction": "Introduction",
    "skills": "Skills",
    "projects": "Projects & Experience",
    "education": "Education",
    "downloadCV": "Download CV"
  }
}
```

### Profile-Specific Content

The system supports different content based on the selected profile:

```json wwwroot/languages/en.json:31 theme={null}
"introduction": {
  "aboutTitle": "About Me",
  "aboutContent_dataScience": "I am a software developer with a recent focus on Data Science...",
  "aboutContent_webDev": "Web developer with a background in Information Technology...",
  "aboutContent_dataAnalyst": "Data Analyst with practical experience in data analysis..."
}
```

<Info>
  Content keys use the pattern `{key}_{profile}` to load profile-specific translations dynamically.
</Info>

## Language Switching Mechanism

### Server-Side: SetLanguage Endpoint

The `HomeController` provides an endpoint to persist language preferences:

```csharp Controllers/HomeController.cs:29 theme={null}
[HttpPost]
public IActionResult SetLanguage(string lang)
{
    Response.Cookies.Append("lang", lang ?? "es", new CookieOptions
    {
        Expires = DateTimeOffset.UtcNow.AddYears(1),
        IsEssential = true
    });
    return Ok();
}
```

### Cookie Configuration

<ParamField path="Expires" type="DateTimeOffset">
  Cookie expires after 1 year
</ParamField>

<ParamField path="IsEssential" type="bool">
  Set to `true` - cookie is required for site functionality
</ParamField>

<ParamField path="lang" type="string" default="es">
  Defaults to Spanish if no language specified
</ParamField>

<Warning>
  The cookie is marked as essential because language selection is fundamental to the user experience. This means it will be set even if users decline non-essential cookies.
</Warning>

### Client-Side: JavaScript Implementation

The language switching is handled by JavaScript in `translation.js`:

```javascript wwwroot/js/translation.js theme={null}
// Simplified example of the translation system
let currentLang = getCookie('lang') || 'es';
let translations = {};

async function loadLanguage(lang) {
    const response = await fetch(`/languages/${lang}.json`);
    translations = await response.json();
    applyTranslations();
}

function applyTranslations() {
    document.querySelectorAll('[data-i18n]').forEach(element => {
        const key = element.getAttribute('data-i18n');
        const value = getNestedValue(translations, key);
        if (value) element.textContent = value;
    });
}

function switchLanguage(newLang) {
    fetch('/Home/SetLanguage', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `lang=${newLang}`
    });
    
    currentLang = newLang;
    loadLanguage(newLang);
}
```

## How Language Loading Works

<Steps>
  <Step title="Page loads">
    JavaScript checks for the `lang` cookie
  </Step>

  <Step title="Determine language">
    Use cookie value if present, otherwise default to Spanish (`es`)
  </Step>

  <Step title="Fetch JSON file">
    Make async request to `/languages/{lang}.json`
  </Step>

  <Step title="Parse translations">
    Convert JSON response to JavaScript object
  </Step>

  <Step title="Apply to DOM">
    Find all elements with `data-i18n` attributes and update their content
  </Step>

  <Step title="Update profile content">
    Load profile-specific content using the current profile from `ViewData`
  </Step>
</Steps>

### Visual Flow

```mermaid theme={null}
sequenceDiagram
    participant Browser
    participant JS as JavaScript
    participant Server
    participant Cookie
    
    Browser->>JS: Page loads
    JS->>Cookie: Read lang cookie
    Cookie-->>JS: Return language (es/en)
    JS->>Server: GET /languages/en.json
    Server-->>JS: Return JSON content
    JS->>Browser: Update DOM with translations
    
    Note over Browser,Cookie: User clicks language toggle
    
    Browser->>JS: switchLanguage('es')
    JS->>Server: POST /Home/SetLanguage (lang=es)
    Server->>Cookie: Set lang=es (expires 1 year)
    Server-->>JS: 200 OK
    JS->>Server: GET /languages/es.json
    Server-->>JS: Return JSON content
    JS->>Browser: Update DOM with new translations
```

## Using Translations in HTML

To make content translatable, use the `data-i18n` attribute:

```html theme={null}
<!-- Simple translation -->
<h2 data-i18n="navigation.skills"></h2>
<!-- Renders: "Skills" (en) or "Habilidades" (es) -->

<!-- Nested property -->
<p data-i18n="introduction.aboutTitle"></p>
<!-- Renders: "About Me" (en) or "Acerca de Mí" (es) -->

<!-- Profile-specific content -->
<div data-i18n="introduction.aboutContent_dataScience"></div>
<!-- Uses current profile to load correct content -->
```

## Content Organization

The JSON files are organized into logical sections:

### Main Sections

<AccordionGroup>
  <Accordion title="typewriter">
    Animated text for the typewriter effect on the homepage
  </Accordion>

  <Accordion title="header">
    Main header content and greeting
  </Accordion>

  <Accordion title="navigation">
    Menu items and navigation labels
  </Accordion>

  <Accordion title="sidebar">
    Social media section and tooltips
  </Accordion>

  <Accordion title="introduction">
    About me section with profile-specific content
  </Accordion>

  <Accordion title="skills">
    Skills section labels, categories, and progress bars
  </Accordion>

  <Accordion title="projects">
    Work experience and personal projects descriptions
  </Accordion>

  <Accordion title="education">
    Academic history and certificates
  </Accordion>

  <Accordion title="chartsData">
    Numerical data for vocational charts (RIASEC, cognitive scores)
  </Accordion>
</AccordionGroup>

## Adding a New Language

To add support for a new language (e.g., French):

<Steps>
  <Step title="Create language file">
    Create `wwwroot/languages/fr.json` with all translated content:

    ```json theme={null}
    {
      "typewriter": {
        "prefix": "Je suis ",
        "phrases": [
          "Ricardo Alejandro Pérez Santillán",
          "un passionné de données",
          "accro à l'automatisation"
        ]
      },
      "navigation": {
        "introduction": "Introduction",
        "skills": "Compétences",
        "projects": "Projets & Expérience",
        "education": "Éducation"
      }
    }
    ```
  </Step>

  <Step title="Update route constraints">
    Add "fr" to the language regex in `Program.cs`:

    ```csharp theme={null}
    app.MapGet("/{lang:regex(^(es|en|fr)$)}", (string lang, HttpContext context) =>
    {
        context.Response.Redirect($"/{lang}/dataScience", permanent: false);
        return Task.CompletedTask;
    });

    // Also update in MapControllerRoute constraints
    constraints: new
    {
        lang = "^(es|en|fr)$",
        profile = @"^(dataScience|webDev|dataAnalyst|DataAnalysis)$"
    }
    ```
  </Step>

  <Step title="Update UI language selector">
    Add a button/option for French in your language switcher UI:

    ```html theme={null}
    <button onclick="switchLanguage('fr')">Français</button>
    ```
  </Step>

  <Step title="Test thoroughly">
    Ensure all content sections have French translations and display correctly
  </Step>
</Steps>

<Tip>
  Use a tool like [i18next-scanner](https://github.com/i18next/i18next-scanner) or create a script to identify missing translation keys across language files.
</Tip>

## Content Keys Convention

Follow these naming conventions for consistency:

| Pattern                  | Example                            | Usage             |
| ------------------------ | ---------------------------------- | ----------------- |
| `section.key`            | `navigation.skills`                | Simple property   |
| `section.key_profile`    | `introduction.aboutContent_webDev` | Profile-specific  |
| `section.subsection.key` | `skills.panels.webDev`             | Nested properties |
| `section.list[]`         | `typewriter.phrases[0]`            | Array values      |

## Dynamic Content Loading

For content that changes based on user interaction:

```javascript theme={null}
// Load profile-specific content
function loadProfileContent(profile) {
    const aboutKey = `introduction.aboutContent_${profile}`;
    const content = getNestedValue(translations, aboutKey);
    document.querySelector('#about-text').textContent = content;
}

// Load when profile changes
document.addEventListener('profileChanged', (event) => {
    loadProfileContent(event.detail.profile);
});
```

## Benefits of This Approach

<CardGroup cols={2}>
  <Card title="No Page Reload" icon="bolt">
    Language changes happen instantly without refreshing the page
  </Card>

  <Card title="Easy Maintenance" icon="wrench">
    All translations in JSON files - no need to rebuild or redeploy
  </Card>

  <Card title="SEO-Friendly URLs" icon="magnifying-glass">
    URLs like `/en/dataScience` are clear and indexable
  </Card>

  <Card title="Scalable" icon="arrows-maximize">
    Adding new languages is straightforward - just add a JSON file
  </Card>
</CardGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Keep Keys Consistent" icon="key">
    Use the same key structure across all language files. Missing keys will result in blank content.
  </Accordion>

  <Accordion title="Validate JSON" icon="check">
    Use a JSON validator to ensure files are properly formatted before deployment.
  </Accordion>

  <Accordion title="Use Fallbacks" icon="arrow-rotate-right">
    Implement fallback logic to show default language if translation is missing:

    ```javascript theme={null}
    const value = translations[lang]?.[key] || translations['es']?.[key] || key;
    ```
  </Accordion>

  <Accordion title="Version Control" icon="code-branch">
    Track translation changes in git to see content evolution over time.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Content not updating" icon="circle-exclamation">
    **Issue**: Changed JSON but content still shows old values

    **Solution**: Clear browser cache or do a hard refresh (Ctrl+F5)
  </Accordion>

  <Accordion title="Cookie not persisting" icon="cookie-bite">
    **Issue**: Language resets on every page load

    **Solution**: Check browser cookie settings and ensure cookies are enabled. Verify `IsEssential = true` in the cookie configuration.
  </Accordion>

  <Accordion title="Missing translations" icon="language">
    **Issue**: Some elements show blank or keys instead of translated text

    **Solution**: Compare JSON files to find missing keys. Use a diff tool to identify discrepancies between language files.
  </Accordion>

  <Accordion title="JSON parse error" icon="triangle-exclamation">
    **Issue**: Translations fail to load

    **Solution**: Validate JSON syntax. Common issues:

    * Missing commas
    * Trailing commas (invalid in JSON)
    * Unescaped quotes in strings
    * Incorrect UTF-8 encoding
  </Accordion>
</AccordionGroup>

## Performance Considerations

<Note>
  The language JSON files are loaded once per session and cached by the browser. However, consider these optimizations for larger applications:

  * **Lazy loading**: Only load sections as needed
  * **Compression**: Enable gzip compression for JSON files
  * **CDN**: Serve language files from a CDN for global users
  * **LocalStorage**: Cache translations in localStorage to reduce network requests
</Note>

## Next Steps

<CardGroup cols={2}>
  <Card title="Routing" icon="route" href="/architecture/routing">
    Learn how language codes are integrated into URL routing
  </Card>

  <Card title="Architecture Overview" icon="sitemap" href="/architecture/overview">
    Return to the architecture overview
  </Card>
</CardGroup>
