diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index bfcc860..d70944b 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -4,6 +4,17 @@ All notable changes to the KoalaSync browser extension and relay server.
---
+## [v2.2.2] — 2026-06-09
+
+### Added
+- **Chrome Web Store i18n Support**: Added `default_locale: "en"` to manifest and created `_locales/*/messages.json` for all 13 supported languages. This unlocks the language selection dropdown in the Chrome Web Store dashboard, allowing translated store listings (title, description) per locale. The extension's own UI translations (`locales/*.json` + `i18n.js`) remain unchanged.
+- **Locale test coverage**: Extended `scripts/test-locales.js` to validate all `_locales/*/messages.json` files (correct format, required keys, no duplicates) and verify `default_locale` is set in the manifest.
+
+### Fixed
+- **Copy Logs button alignment**: Removed stray `margin-top: 8px` inherited from `.secondary` class that pushed the button 8px down in the connection status row.
+
+---
+
## [v2.2.1] — 2026-06-09
### Added
diff --git a/extension/_locales/de/messages.json b/extension/_locales/de/messages.json
new file mode 100644
index 0000000..8b84354
--- /dev/null
+++ b/extension/_locales/de/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Synchronisiere die Videowiedergabe auf YouTube, Netflix, Emby, Jellyfin und jeder HTML5-Seite in Echtzeit mit Freunden."
+ }
+}
diff --git a/extension/_locales/en/messages.json b/extension/_locales/en/messages.json
new file mode 100644
index 0000000..3132906
--- /dev/null
+++ b/extension/_locales/en/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Synchronize video playback on YouTube, Netflix, Emby, Jellyfin, and any HTML5 site in real-time with friends."
+ }
+}
diff --git a/extension/_locales/es/messages.json b/extension/_locales/es/messages.json
new file mode 100644
index 0000000..9ed8d3b
--- /dev/null
+++ b/extension/_locales/es/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Sincroniza la reproducción de video en YouTube, Netflix, Emby, Jellyfin y cualquier sitio HTML5 en tiempo real con amigos."
+ }
+}
diff --git a/extension/_locales/fr/messages.json b/extension/_locales/fr/messages.json
new file mode 100644
index 0000000..37cbe71
--- /dev/null
+++ b/extension/_locales/fr/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Synchronisez la lecture vidéo sur YouTube, Netflix, Emby, Jellyfin et tout site HTML5 en temps réel avec vos amis."
+ }
+}
diff --git a/extension/_locales/it/messages.json b/extension/_locales/it/messages.json
new file mode 100644
index 0000000..118ff90
--- /dev/null
+++ b/extension/_locales/it/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Sincronizza la riproduzione video su YouTube, Netflix, Emby, Jellyfin e qualsiasi sito HTML5 in tempo reale con gli amici."
+ }
+}
diff --git a/extension/_locales/ja/messages.json b/extension/_locales/ja/messages.json
new file mode 100644
index 0000000..5833c2f
--- /dev/null
+++ b/extension/_locales/ja/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "YouTube、Netflix、Emby、Jellyfin、その他HTML5サイトでの動画再生を友達とリアルタイムで同期します。"
+ }
+}
diff --git a/extension/_locales/ko/messages.json b/extension/_locales/ko/messages.json
new file mode 100644
index 0000000..12f71e4
--- /dev/null
+++ b/extension/_locales/ko/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "YouTube, Netflix, Emby, Jellyfin 및 모든 HTML5 사이트에서 친구들과 실시간으로 비디오 재생을 동기화하세요."
+ }
+}
diff --git a/extension/_locales/nl/messages.json b/extension/_locales/nl/messages.json
new file mode 100644
index 0000000..ab97481
--- /dev/null
+++ b/extension/_locales/nl/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Synchroniseer video-afspelen op YouTube, Netflix, Emby, Jellyfin en elke HTML5-site in realtime met vrienden."
+ }
+}
diff --git a/extension/_locales/pl/messages.json b/extension/_locales/pl/messages.json
new file mode 100644
index 0000000..e13b068
--- /dev/null
+++ b/extension/_locales/pl/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Synchronizuj odtwarzanie wideo na YouTube, Netflix, Emby, Jellyfin i dowolnej stronie HTML5 w czasie rzeczywistym ze znajomymi."
+ }
+}
diff --git a/extension/_locales/pt_BR/messages.json b/extension/_locales/pt_BR/messages.json
new file mode 100644
index 0000000..bb6a136
--- /dev/null
+++ b/extension/_locales/pt_BR/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Sincronize a reprodução de vídeo no YouTube, Netflix, Emby, Jellyfin e qualquer site HTML5 em tempo real com amigos."
+ }
+}
diff --git a/extension/_locales/pt_PT/messages.json b/extension/_locales/pt_PT/messages.json
new file mode 100644
index 0000000..bb6a136
--- /dev/null
+++ b/extension/_locales/pt_PT/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Sincronize a reprodução de vídeo no YouTube, Netflix, Emby, Jellyfin e qualquer site HTML5 em tempo real com amigos."
+ }
+}
diff --git a/extension/_locales/ru/messages.json b/extension/_locales/ru/messages.json
new file mode 100644
index 0000000..ff73697
--- /dev/null
+++ b/extension/_locales/ru/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "Синхронизируйте воспроизведение видео на YouTube, Netflix, Emby, Jellyfin и любых HTML5-сайтах в реальном времени с друзьями."
+ }
+}
diff --git a/extension/_locales/tr/messages.json b/extension/_locales/tr/messages.json
new file mode 100644
index 0000000..7641824
--- /dev/null
+++ b/extension/_locales/tr/messages.json
@@ -0,0 +1,8 @@
+{
+ "appName": {
+ "message": "KoalaSync"
+ },
+ "appDesc": {
+ "message": "YouTube, Netflix, Emby, Jellyfin ve herhangi bir HTML5 sitesinde video oynatmayı arkadaşlarınızla gerçek zamanlı olarak senkronize edin."
+ }
+}
diff --git a/extension/manifest.base.json b/extension/manifest.base.json
index 468ec74..366fcb3 100644
--- a/extension/manifest.base.json
+++ b/extension/manifest.base.json
@@ -1,5 +1,6 @@
{
"manifest_version": 3,
+ "default_locale": "en",
"name": "KoalaSync",
"version": "2.2.1",
"description": "Synchronize video playback on YouTube, Netflix, Emby, Jellyfin, and any HTML5 site in real-time with friends.",
diff --git a/extension/popup.html b/extension/popup.html
index a7c78fa..44f967f 100644
--- a/extension/popup.html
+++ b/extension/popup.html
@@ -600,7 +600,7 @@
Disconnected
-
+
diff --git a/scripts/test-locales.js b/scripts/test-locales.js
index e45069c..81bac1a 100644
--- a/scripts/test-locales.js
+++ b/scripts/test-locales.js
@@ -98,11 +98,128 @@ for (const file of localeFiles) {
}
}
+// ──────────────────────────────────────
+// Verify Chrome _locales/*/messages.json
+// ──────────────────────────────────────
+console.log('\nVerifying Chrome _locales/messages.json structure...\n');
+
+const chromeLocalesDir = path.join(__dirname, '..', 'extension', '_locales');
+
+// Map custom locale codes to Chrome's underscore format
+const chromeLocaleMap = {
+ 'en': 'en', 'de': 'de', 'fr': 'fr', 'es': 'es', 'it': 'it',
+ 'ja': 'ja', 'ko': 'ko', 'nl': 'nl', 'pl': 'pl',
+ 'pt-BR': 'pt_BR', 'pt': 'pt_PT', 'ru': 'ru', 'tr': 'tr'
+};
+
+// Read SUPPORTED_LANGUAGES from i18n.js
+const i18nContent = fs.readFileSync(i18nPath, 'utf8');
+const langMatch = i18nContent.match(/export const SUPPORTED_LANGUAGES = \[(.*?)\];/);
+const supportedLangs = langMatch
+ ? langMatch[1].split(',').map(s => s.trim().replace(/['"]/g, ''))
+ : [];
+
+// Verify default_locale in manifest.base.json
+const manifestPath = path.join(__dirname, '..', 'extension', 'manifest.base.json');
+try {
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
+ if (!manifest.default_locale) {
+ hasError = true;
+ console.error('❌ manifest.base.json is missing "default_locale"');
+ } else if (manifest.default_locale !== 'en') {
+ hasError = true;
+ console.error(`❌ manifest.base.json default_locale is "${manifest.default_locale}", expected "en"`);
+ } else {
+ console.log('✓ manifest.base.json has default_locale: "en"');
+ }
+} catch (err) {
+ hasError = true;
+ console.error('❌ Failed to read manifest.base.json:', err.message);
+}
+
+// Verify _locales structure for each supported language
+const expectedKeys = ['appName', 'appDesc'];
+for (const lang of supportedLangs) {
+ const chromeLocale = chromeLocaleMap[lang];
+ if (!chromeLocale) {
+ hasError = true;
+ console.error(`❌ No Chrome locale mapping for "${lang}" in chromeLocaleMap`);
+ continue;
+ }
+
+ const msgPath = path.join(chromeLocalesDir, chromeLocale, 'messages.json');
+
+ if (!fs.existsSync(msgPath)) {
+ hasError = true;
+ console.error(`❌ Missing _locales/${chromeLocale}/messages.json for language "${lang}"`);
+ continue;
+ }
+
+ try {
+ const raw = fs.readFileSync(msgPath, 'utf8');
+ const messages = JSON.parse(raw);
+ const keys = Object.keys(messages);
+
+ // Detect duplicate top-level keys via regex (JSON.parse silently keeps last value)
+ const topKeyRe = /^\s{2}"(\w+)"\s*:/gm;
+ const topSeen = {};
+ let topDupes = [];
+ let tm;
+ while ((tm = topKeyRe.exec(raw)) !== null) {
+ if (topSeen[tm[1]]) topDupes.push(tm[1]);
+ topSeen[tm[1]] = true;
+ }
+ if (topDupes.length > 0) {
+ hasError = true;
+ console.error(`❌ _locales/${chromeLocale}/messages.json has duplicate keys: ${[...new Set(topDupes)].join(', ')}`);
+ continue;
+ }
+
+ // Validate each entry has a "message" field
+ for (const key of keys) {
+ if (!messages[key].message || typeof messages[key].message !== 'string') {
+ hasError = true;
+ console.error(`❌ _locales/${chromeLocale}/messages.json: "${key}" is missing a valid "message" field`);
+ }
+ }
+
+ // Check for missing or extra keys vs expected baseline
+ const missing = expectedKeys.filter(k => !keys.includes(k));
+ const extra = keys.filter(k => !expectedKeys.includes(k));
+ if (missing.length > 0) {
+ hasError = true;
+ console.error(`❌ _locales/${chromeLocale}/messages.json missing keys: ${missing.join(', ')}`);
+ }
+ if (extra.length > 0) {
+ console.log(`ℹ️ _locales/${chromeLocale}/messages.json has extra keys (ok): ${extra.join(', ')}`);
+ }
+ if (missing.length === 0) {
+ console.log(`✓ _locales/${chromeLocale}/messages.json is valid and complete`);
+ }
+ } catch (err) {
+ hasError = true;
+ console.error(`❌ Failed to parse _locales/${chromeLocale}/messages.json:`, err.message);
+ }
+}
+
+// Detect orphan _locales directories (no matching supported language)
+if (fs.existsSync(chromeLocalesDir)) {
+ const chromeCodes = supportedLangs.map(l => chromeLocaleMap[l]).filter(Boolean);
+ const dirs = fs.readdirSync(chromeLocalesDir);
+ for (const dir of dirs) {
+ const dirPath = path.join(chromeLocalesDir, dir);
+ if (fs.statSync(dirPath).isDirectory() && dir !== '.git' && !chromeCodes.includes(dir)) {
+ hasError = true;
+ console.error(`❌ Orphan _locales/${dir}/ directory exists but no matching language in SUPPORTED_LANGUAGES`);
+ }
+ }
+}
+
console.log('');
if (hasError) {
console.error('❌ Locale consistency check failed! Please fix the errors listed above.');
process.exit(1);
} else {
- console.log('🎉 All locale files are perfectly synchronized and consistent!');
+ console.log('🎉 All locale files (locales/ and _locales/) are valid and consistent!');
process.exit(0);
}