# KoalaSync Browser Extension v2.0 - i18n Technical Implementation Plan Welcome, future Antigravity AI agent! This document is placed directly in the codebase at `/extension/locales/i18n_plan.md` to serve as a comprehensive architectural handbook for the next steps in adding **full internationalization (i18n) support to the browser extension itself** while maintaining 100% video-sync and background communication safety. --- ## π Context & Audited Scope KoalaSync is a lightweight, premium browser extension (Chrome & Firefox Manifest V3) for synchronized video playback. The landing pages are already compiled dynamically in 6 languages: * English (`en`) * German (`de`) * French (`fr`) * Spanish (`es`) * Portuguese (Brazil) (`pt-BR`) * Russian (`ru`) Our goal is to build an identical, premium translation engine for the extension itself. --- ## ποΈ Architectural Choice: Custom JSON Dictionary Engine (Approach B) We evaluated two paths: 1. **Approach A (Native `chrome.i18n` API):** Uses `_locales/` directory. Too rigidβcannot support real-time dynamic switching inside the extension Settings dropdown without closing/re-opening the popup. 2. **Approach B (Custom Unified JSON Engine):** Uses flat JSON files matching our website files (`"KEY": "Value"`). Dynamically merges the target dictionary with baseline English `en.json` at runtime, programmatically providing an **airtight English fallback** and **real-time DOM translations** without popup reload. We chose **Approach B** for maximum compatibility, premium real-time toggling, and clean fallback safety. --- ## π Resolve, Load, & State Flow 1. **On launch:** Look for saved language in `chrome.storage.sync.get('locale')`. 2. **Fallback Autodetect:** If no saved language, read `navigator.language` or `chrome.i18n.getUILanguage()`. * If the detected locale is supported, set as active. * If not supported, default to English (`en`). 3. **Dictionary Resolution:** * Asynchronously load the English baseline dictionary (`/extension/locales/en.json`). * If the target language is different, load target JSON (e.g. `/extension/locales/de.json`) and execute `Object.assign({}, enDict, targetDict)`. This guarantees dynamic translation while cleanly falling back to English for any missing keys. 4. **DOM Replacements:** Scan for `data-i18n`, `data-i18n-title`, and `data-i18n-placeholder` attributes, and translate them on the fly. 5. **Persistence:** Save dynamic selection modifications from the dropdown into `chrome.storage.sync`. Trigger instant DOM re-translation on change. --- ## π Proposed File Structure ``` KoalaPlay/ βββ extension/ βββ locales/ # [NEW] Contains flat translation maps β βββ i18n_plan.md # This roadmap file β βββ en.json # Flat English baseline keys β βββ de.json # German keys β βββ fr.json # French keys β βββ es.json # Spanish keys β βββ pt-BR.json # Portuguese (Brasil) keys β βββ ru.json # Russian keys βββ i18n.js # [NEW] ESM translation engine module βββ popup.html # Modified with data-i18n attributes βββ popup.js # Modified to initialize locales and update variables βββ background.js # Modified to push localized notification alerts ``` --- ## π οΈ Draft Code Snippets ### 1. `i18n.js` (Zero-Dependency Engine Module) ```javascript // extension/i18n.js export const SUPPORTED_LANGUAGES = ['en', 'de', 'fr', 'es', 'pt-BR', 'ru']; export const DEFAULT_LANGUAGE = 'en'; let activeDictionary = {}; export async function loadLocale(langCode) { const resolvedLang = SUPPORTED_LANGUAGES.includes(langCode) ? langCode : DEFAULT_LANGUAGE; try { const enResponse = await fetch(chrome.runtime.getURL(`locales/${DEFAULT_LANGUAGE}.json`)); const enDict = await enResponse.json(); if (resolvedLang === DEFAULT_LANGUAGE) { activeDictionary = enDict; return; } const targetResponse = await fetch(chrome.runtime.getURL(`locales/${resolvedLang}.json`)); const targetDict = await targetResponse.json(); activeDictionary = Object.assign({}, enDict, targetDict); } catch (err) { console.error('[i18n] Failed to load dictionary. Falling back to English:', err); const rescue = await fetch(chrome.runtime.getURL(`locales/${DEFAULT_LANGUAGE}.json`)); activeDictionary = await rescue.json(); } } export function getMessage(key) { return activeDictionary[key] || key; } export function translateDOM() { // Translate text nodes document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.getAttribute('data-i18n'); const translated = getMessage(key); const img = el.querySelector('img'); if (img) { el.innerHTML = ''; el.appendChild(img); el.appendChild(document.createTextNode(' ' + translated)); } else { el.textContent = translated; } }); // Translate tooltips document.querySelectorAll('[data-i18n-title]').forEach(el => { const key = el.getAttribute('data-i18n-title'); el.setAttribute('title', getMessage(key)); }); // Translate placeholders document.querySelectorAll('[data-i18n-placeholder]').forEach(el => { const key = el.getAttribute('data-i18n-placeholder'); el.setAttribute('placeholder', getMessage(key)); }); } ``` ### 2. Markup Changes (`popup.html`) * Annotate text elements: `` * Add Language Dropdown in the Settings Panel: ```html