Skip to content

Multi-Language for Static HTML #230

Description

@Fujio-Turner

Multi-Language Implementation Strategy for Couchbase Query Analyzer

Executive Summary

This document outlines the recommended approach for implementing multi-language support for /index.html and /analysis_hub.html in the Couchbase Query Analyzer project.

Recommended Approach: Hybrid static HTML with JavaScript-based auto-detection and fallback UI


Current State Analysis

Existing Architecture

The project already has a proven multi-language system in place:

/de/              # German versions
/en/              # English versions  
/es/              # Spanish versions
/pt/              # Portuguese versions
/liquid_snake/    # Shared JavaScript assets
/settings/
  └── translations.json    # 900+ translation mappings
  └── LOCALIZATION_GUIDE.md
/python/
  └── apply_comprehensive_translations.py

Current Features:

  • ✅ Separate static HTML files per language
  • ✅ TEXT_CONSTANTS in JavaScript for dynamic content
  • ✅ 900+ translation mappings in settings/translations.json
  • ✅ Automated translation workflow with Python scripts

Files Requiring Multi-Language Support

Root Files (Currently English-Only):

  • /index.html - Main query analyzer landing page
  • /analysis_hub.html - Analysis hub landing page

Recommended Solution

Architecture Decision: Static HTML with Language Detection

Why Separate HTML Files (Not JavaScript-Only Translation):

Factor Static HTML per Language JavaScript-Only Translation
SEO ✅ Each language has unique URL ❌ Single URL, content hidden from crawlers
Performance ✅ Faster initial load (one language) ❌ Loads all languages, switches with JS
Accessibility ✅ Works without JavaScript ❌ Requires JavaScript to function
Maintenance ✅ Clear separation, proven workflow ❌ Complex JS logic, harder debugging
Static Hosting ✅ Works on Cloudflare Pages, GitHub Pages ⚠️ May require additional configuration
Future-Proof ✅ Scales easily, already implemented ❌ Technical debt increases over time

Decision: Continue with separate static HTML files per language (proven approach)


Implementation Plan

Proposed Directory Structure

/ (root)
├── index.html                    # Language detection + redirect
├── analysis_hub.html             # Language detection + redirect
├── /en/
│   ├── index.html               # English analyzer (no redirect)
│   └── analysis_hub.html        # English hub (no redirect)
├── /es/
│   ├── index.html               # Spanish analyzer (no redirect)
│   └── analysis_hub.html        # Spanish hub (no redirect)
├── /de/
│   ├── index.html               # German analyzer (no redirect)
│   └── analysis_hub.html        # German hub (no redirect)
├── /pt/
│   ├── index.html               # Portuguese analyzer (no redirect)
│   └── analysis_hub.html        # Portuguese hub (no redirect)
└── /liquid_snake/
    └── assets/js/...            # Shared JavaScript assets

Language Detection Logic

Root Landing Pages (/index.html, /analysis_hub.html)

Purpose: Auto-detect language and redirect to appropriate version

Detection Priority:

  1. User's manual choice (stored in localStorage.preferredLanguage)
  2. Browser language (from navigator.language or navigator.languages[0])
  3. Default fallback (/en/index.html)

Implementation Code

Root /index.html Example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Couchbase Query Analyzer - Select Language</title>
    <meta name="description" content="Multi-language Couchbase query analyzer. Select your preferred language.">
    <link rel="icon" type="image/svg+xml" href="img/favicon.svg">
    
    <style>
        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            margin: 0;
            background: linear-gradient(135deg, #007acc 0%, #00b4d8 100%);
            color: white;
        }
        h1 {
            font-size: 2.5em;
            margin-bottom: 40px;
            text-align: center;
        }
        .language-buttons {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 20px;
            max-width: 600px;
        }
        .language-buttons a {
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 30px 40px;
            background: white;
            color: #007acc;
            text-decoration: none;
            border-radius: 12px;
            font-size: 1.5em;
            font-weight: bold;
            box-shadow: 0 6px 18px rgba(0,0,0,0.15);
            transition: transform 0.2s ease, box-shadow 0.2s ease;
        }
        .language-buttons a:hover {
            transform: translateY(-4px);
            box-shadow: 0 10px 24px rgba(0,0,0,0.25);
        }
        .language-buttons a span {
            margin-right: 10px;
            font-size: 1.3em;
        }
    </style>
    
    <script>
        // Auto-redirect if JavaScript enabled
        (function() {
            // 1. Check for user's manual language preference
            const userPreference = localStorage.getItem('preferredLanguage');
            
            // 2. Detect browser language
            const browserLang = navigator.language || navigator.languages[0];
            const langCode = browserLang.split('-')[0]; // "es-MX" → "es"
            
            // 3. Language to folder mapping
            const langMap = {
                'es': '/es/index.html',
                'de': '/de/index.html',
                'pt': '/pt/index.html',
                'en': '/en/index.html'
            };
            
            // 4. Redirect (preference > browser > default)
            const targetLang = userPreference || langCode;
            const targetUrl = langMap[targetLang] || '/en/index.html';
            
            // Immediate redirect
            window.location.href = targetUrl;
        })();
    </script>
</head>
<body>
    <!-- Fallback UI (only visible if JavaScript disabled or redirect blocked) -->
    <h1>🌍 Select Your Preferred Language</h1>
    <div class="language-buttons">
        <a href="/en/index.html">
            <span>🇺🇸</span> English
        </a>
        <a href="/es/index.html">
            <span>🇪🇸</span> Español
        </a>
        <a href="/de/index.html">
            <span>🇩🇪</span> Deutsch
        </a>
        <a href="/pt/index.html">
            <span>🇵🇹</span> Português
        </a>
    </div>
    
    <noscript>
        <p style="margin-top: 40px; text-align: center; font-size: 1.1em;">
            JavaScript is disabled. Please select your language above.
        </p>
    </noscript>
</body>
</html>

Root /analysis_hub.html Example:

Same structure, but redirect to:

  • /en/analysis_hub.html
  • /es/analysis_hub.html
  • /de/analysis_hub.html
  • /pt/analysis_hub.html

Language Switching in Language-Specific Files

Language Dropdown Component

Add to all language-specific files (/en/index.html, /es/index.html, etc.):

<!-- Language Selector Dropdown (Add to header) -->
<div class="language-selector">
    <label for="language-dropdown">🌍 Language:</label>
    <select id="language-dropdown" onchange="changeLanguage(this.value)">
        <option value="en" selected>English</option>
        <option value="es">Español</option>
        <option value="de">Deutsch</option>
        <option value="pt">Português</option>
    </select>
</div>

<script>
    // Language switching function
    function changeLanguage(langCode) {
        // Store user preference
        localStorage.setItem('preferredLanguage', langCode);
        
        // Get current page name
        const currentPage = window.location.pathname.split('/').pop(); // "index.html" or "analysis_hub.html"
        
        // Build target URL
        const targetUrl = `/${langCode}/${currentPage}`;
        
        // Redirect to selected language
        window.location.href = targetUrl;
    }
    
    // Set dropdown to current language on page load
    (function() {
        const currentLang = window.location.pathname.split('/')[1]; // "/en/index.html" → "en"
        const dropdown = document.getElementById('language-dropdown');
        if (dropdown && currentLang) {
            dropdown.value = currentLang;
        }
    })();
</script>

<style>
    .language-selector {
        position: absolute;
        top: 20px;
        right: 20px;
        display: flex;
        align-items: center;
        gap: 10px;
        background: rgba(255,255,255,0.95);
        padding: 10px 15px;
        border-radius: 8px;
        box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    }
    .language-selector label {
        font-weight: bold;
        color: #007acc;
    }
    .language-selector select {
        padding: 8px 12px;
        border: 1px solid #007acc;
        border-radius: 6px;
        background: white;
        color: #007acc;
        font-size: 14px;
        cursor: pointer;
    }
</style>

Critical Implementation Rules

✅ DO: Root Landing Pages Only

Files that SHOULD redirect:

  • /index.html (root)
  • /analysis_hub.html (root)

Purpose: Auto-detect language preference and redirect once


❌ DON'T: Language-Specific Files

Files that MUST NOT redirect:

  • /en/index.html
  • /es/index.html
  • /de/index.html
  • /pt/index.html
  • /en/analysis_hub.html
  • /es/analysis_hub.html
  • /de/analysis_hub.html
  • /pt/analysis_hub.html

Reason: Prevents infinite redirect loops


User Experience Flow

Scenario 1: First-Time User (JavaScript Enabled)

  1. User visits https://cb.fuj.io/
  2. JavaScript detects browser language: navigator.language = "es-MX"
  3. Auto-redirect to /es/index.html (< 0.1 seconds)
  4. User sees Spanish interface immediately
  5. User can switch language via dropdown (stored in localStorage)

User Experience: Seamless, instant


Scenario 2: Returning User (JavaScript Enabled)

  1. User previously selected German via dropdown
  2. localStorage.preferredLanguage = "de"
  3. User visits https://cb.fuj.io/
  4. JavaScript checks localStorage first
  5. Auto-redirect to /de/index.html
  6. User sees German interface (their preference)

User Experience: Remembers preference


Scenario 3: JavaScript Disabled or Blocked

  1. User visits https://cb.fuj.io/
  2. JavaScript redirect doesn't execute
  3. User sees fallback UI: 4 language buttons
  4. User manually clicks "🇪🇸 Español"
  5. Navigates to /es/index.html
  6. Language dropdown still works (basic HTML links)

User Experience: Still functional, fully accessible


Scenario 4: Direct Link to Language Version

  1. User receives link: https://cb.fuj.io/de/index.html
  2. Loads German version directly
  3. No redirect occurs (already in correct language)
  4. User can switch via dropdown if needed

User Experience: Direct access, no unnecessary redirects


SEO Considerations

URL Structure Benefits

Current URLs (After Implementation):

https://cb.fuj.io/en/index.html           # English
https://cb.fuj.io/es/index.html           # Spanish
https://cb.fuj.io/de/index.html           # German
https://cb.fuj.io/pt/index.html           # Portuguese
https://cb.fuj.io/en/analysis_hub.html    # English Hub
https://cb.fuj.io/es/analysis_hub.html    # Spanish Hub

SEO Advantages:

  • ✅ Each language has unique, crawlable URL
  • <html lang="en|es|de|pt"> matches content language
  • ✅ Canonical URLs per language
  • ✅ Hreflang tags can be added for international SEO
  • ✅ Search engines can index all language versions

Recommended Meta Tags

Add to each language version:

<!-- English version: /en/index.html -->
<link rel="canonical" href="https://cb.fuj.io/en/index.html" />
<link rel="alternate" hreflang="en" href="https://cb.fuj.io/en/index.html" />
<link rel="alternate" hreflang="es" href="https://cb.fuj.io/es/index.html" />
<link rel="alternate" hreflang="de" href="https://cb.fuj.io/de/index.html" />
<link rel="alternate" hreflang="pt" href="https://cb.fuj.io/pt/index.html" />
<link rel="alternate" hreflang="x-default" href="https://cb.fuj.io/en/index.html" />

Translation Workflow

Step 1: Create Language Versions

For /index.html:

# Copy root index.html to language folders
cp /index.html /en/index.html
cp /index.html /es/index.html
cp /index.html /de/index.html
cp /index.html /pt/index.html

# Remove redirect script from language-specific files
# Add language dropdown component
# Update <html lang="..."> attribute

For /analysis_hub.html:

# Same process
cp /analysis_hub.html /en/analysis_hub.html
cp /analysis_hub.html /es/analysis_hub.html
cp /analysis_hub.html /de/analysis_hub.html
cp /analysis_hub.html /pt/analysis_hub.html

Step 2: Apply Translations

Use existing Python workflow:

# Apply comprehensive translations from settings/translations.json
python3 python/apply_comprehensive_translations.py es
python3 python/apply_comprehensive_translations.py de
python3 python/apply_comprehensive_translations.py pt

# Validate JavaScript syntax
python3 python/validate_js_syntax.py

# Validate HTML attributes
python3 python/validate_html_attributes.py

Step 3: Update settings/translations.json

Add any new translatable strings from /index.html and /analysis_hub.html to settings/translations.json:

{
  "Select Your Preferred Language": {
    "de": "Wählen Sie Ihre bevorzugte Sprache",
    "es": "Seleccione su idioma preferido",
    "pt": "Selecione seu idioma preferido"
  },
  "Language:": {
    "de": "Sprache:",
    "es": "Idioma:",
    "pt": "Idioma:"
  }
}

Implementation Checklist

Phase 1: File Structure Setup

  • Create /en/, /es/, /de/, /pt/ language folders (if not exist)
  • Copy /index.html to all language folders
  • Copy /analysis_hub.html to all language folders
  • Update root /index.html with language detection redirect
  • Update root /analysis_hub.html with language detection redirect

Phase 2: Language-Specific File Updates

  • Remove redirect script from language-specific files
  • Add language dropdown component to all language versions
  • Update <html lang="..."> attribute per language
  • Add hreflang meta tags for SEO
  • Update canonical URLs

Phase 3: Translation Application

  • Update settings/translations.json with new strings
  • Run apply_comprehensive_translations.py for each language
  • Run validate_js_syntax.py to check for errors
  • Run validate_html_attributes.py to check HTML integrity

Phase 4: Testing

  • Test auto-detection (change browser language and clear localStorage)
  • Test manual language switching via dropdown
  • Test localStorage persistence (reload page, check language)
  • Test JavaScript disabled scenario (verify fallback UI works)
  • Test direct URL access (/es/index.html should not redirect)
  • Test all 4 languages for each page (8 total URLs)
  • Verify no redirect loops occur

Phase 5: SEO & Documentation

  • Add sitemap entries for all language versions
  • Update README.md with new URL structure
  • Update AGENTS.md with multi-language architecture notes
  • Test hreflang tags with Google Search Console
  • Verify canonical URLs are correct

Browser Language Code Mapping

Common Language Codes

Browser Code Mapped To Example
en, en-US, en-GB /en/ English (any variant)
es, es-MX, es-ES /es/ Spanish (any variant)
de, de-DE, de-AT /de/ German (any variant)
pt, pt-BR, pt-PT /pt/ Portuguese (any variant)
Any other /en/ Default to English

Potential Issues & Solutions

Issue 1: Redirect Loop

Symptom: Page keeps redirecting endlessly

Cause: Language-specific files (/en/index.html) have redirect script

Solution: Ensure ONLY root files (/index.html) have redirect logic


Issue 2: Language Preference Not Persisting

Symptom: User selects Spanish, but next visit shows English

Cause: localStorage.setItem() not executing in dropdown onchange

Solution: Verify changeLanguage() function is present in language-specific files


Issue 3: SEO Duplicate Content

Symptom: Google shows duplicate content warnings

Cause: Missing canonical URLs or hreflang tags

Solution: Add proper canonical and hreflang meta tags to all language versions


Issue 4: Broken Links After Migration

Symptom: Links to /index.html or /analysis_hub.html show language selector

Cause: External links pointing to root URLs

Solution:

  1. This is expected behavior (auto-redirects work)
  2. Update internal links to use language-specific URLs
  3. External links will auto-detect and redirect

Benefits Summary

Benefit Description
SEO-Friendly Each language has unique URL for search engine indexing
Performance Browser only loads one language (faster initial load)
Accessibility Works without JavaScript (fallback UI)
User Experience Auto-detects preference, remembers choice
Maintenance Proven workflow already in place
Scalability Easy to add new languages in the future
Progressive Enhancement Degrades gracefully (JS disabled → manual selection)
Static Hosting Works on Cloudflare Pages, GitHub Pages, Netlify

Future Enhancements

Optional Improvements

  1. Analytics Integration: Track language preference distribution
  2. Geolocation Fallback: Use IP-based location if browser language unavailable
  3. Language Detection Library: Use i18n-iso-countries for better mapping
  4. A/B Testing: Test different language selector UI placements
  5. Loading Animation: Show brief animation during redirect (< 0.1s)

References

  • AGENTS.md - Project architecture and conventions
  • settings/LOCALIZATION_GUIDE.md - Translation workflow and validation
  • settings/translations.json - 900+ translation mappings
  • python/apply_comprehensive_translations.py - Automated translation script

Questions for Investigation

  • Should we add Portuguese variants (pt-BR vs pt-PT)?
  • Do we need right-to-left (RTL) language support in future?
  • Should language selector be in header or footer?
  • Should we track language switching events in analytics?
  • Do we need server-side redirects (Cloudflare Workers)?

Document Version: 1.0
Last Updated: 2025-11-07
Status: Ready for Implementation
Estimated Effort: 4-6 hours (includes translation, testing, validation)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions