Skip to content

Commit a8d5c4d

Browse files
authored
Merge pull request #457 from esokullu/main
fix locale loading and language routing
2 parents 257359a + 2a49524 commit a8d5c4d

26 files changed

Lines changed: 17065 additions & 59 deletions

src/chrome/src/ui/locales/fr.js

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -873,27 +873,27 @@ export default {
873873
'st.provider.field.cache_write_cost_per_million': "Coût estimé d’écriture du cache (5 min) ($ / 1M tokens)",
874874
'st.provider.field.cache_write_1h_cost_per_million': "Coût estimé d’écriture du cache (1 h) ($ / 1M tokens)",
875875

876-
'st.skills.cws.heading': 'Configuration de publication Chrome Web Store',
877-
'st.skills.cws.desc_html': 'Ces paramètres n'apparaissent que lorsque la compétence de publication packagée est activée. Créez une application web OAuth dans Google Cloud, activez l'API Chrome Web Store et enregistrez <code>http://localhost:1457/auth/callback</code> comme URI de redirection autorisée. Les identifiants, jetons et octets ZIP restent stockés localement dans l'extension et ne sont jamais inclus dans les requêtes au modèle ni les traces.',
878-
'st.skills.cws.publisher_id': 'ID de l'éditeur',
879-
'st.skills.cws.item_id': 'ID de l'élément (ID d'extension de 32 caractères)',
880-
'st.skills.cws.oauth_client_id': 'ID client OAuth Google',
881-
'st.skills.cws.oauth_client_secret': 'Secret client OAuth Google',
882-
'st.skills.cws.redirect_help': 'URI de redirection autorisée : http://localhost:1457/auth/callback',
883-
'st.skills.cws.save': 'Enregistrer la configuration',
884-
'st.skills.cws.connect': 'Connecter Google',
885-
'st.skills.cws.signout': 'Déconnecter',
886-
'st.skills.cws.saved': 'Configuration Chrome Web Store enregistrée localement.',
887-
'st.skills.cws.connected': 'Google OAuth est connecté pour l'accès à Chrome Web Store.',
888-
'st.skills.cws.disconnected': 'Google OAuth n'est pas connecté.',
889-
'st.skills.cws.signed_out': 'Jetons Google OAuth supprimés.',
890-
'st.skills.cws.connect_failed': 'Impossible de se connecter à Google OAuth.',
891-
'st.skills.cws.package': 'Package de publication (.zip)',
892-
'st.skills.cws.package_empty': 'Aucun fichier ZIP de publication sélectionné.',
893-
'st.skills.cws.package_selected': '{name} · {size} Ko · stocké localement',
894-
'st.skills.cws.package_ready': 'Le fichier ZIP de publication est prêt pour l'outil de téléchargement fiable.',
895-
'st.skills.cws.clear_package': 'Effacer le ZIP sélectionné',
896-
'st.skills.cws.package_cleared': 'ZIP de publication sélectionné supprimé du stockage local.',
897-
'st.skills.cws.package_zip_only': 'Choisissez un package de publication au format .zip.',
898-
'st.skills.cws.package_too_large': 'Le fichier ZIP doit faire entre 1 octet et 100 Mo.',,
876+
'st.skills.cws.heading': "Configuration de publication Chrome Web Store",
877+
'st.skills.cws.desc_html': "Ces paramètres n'apparaissent que lorsque la compétence de publication packagée est activée. Créez une application web OAuth dans Google Cloud, activez l'API Chrome Web Store et enregistrez <code>http://localhost:1457/auth/callback</code> comme URI de redirection autorisée. Les identifiants, jetons et octets ZIP restent stockés localement dans l'extension et ne sont jamais inclus dans les requêtes au modèle ni les traces.",
878+
'st.skills.cws.publisher_id': "ID de l'éditeur",
879+
'st.skills.cws.item_id': "ID de l'élément (ID d'extension de 32 caractères)",
880+
'st.skills.cws.oauth_client_id': "ID client OAuth Google",
881+
'st.skills.cws.oauth_client_secret': "Secret client OAuth Google",
882+
'st.skills.cws.redirect_help': "URI de redirection autorisée : http://localhost:1457/auth/callback",
883+
'st.skills.cws.save': "Enregistrer la configuration",
884+
'st.skills.cws.connect': "Connecter Google",
885+
'st.skills.cws.signout': "Déconnecter",
886+
'st.skills.cws.saved': "Configuration Chrome Web Store enregistrée localement.",
887+
'st.skills.cws.connected': "Google OAuth est connecté pour l'accès à Chrome Web Store.",
888+
'st.skills.cws.disconnected': "Google OAuth n'est pas connecté.",
889+
'st.skills.cws.signed_out': "Jetons Google OAuth supprimés.",
890+
'st.skills.cws.connect_failed': "Impossible de se connecter à Google OAuth.",
891+
'st.skills.cws.package': "Package de publication (.zip)",
892+
'st.skills.cws.package_empty': "Aucun fichier ZIP de publication sélectionné.",
893+
'st.skills.cws.package_selected': "{name} · {size} Ko · stocké localement",
894+
'st.skills.cws.package_ready': "Le fichier ZIP de publication est prêt pour l'outil de téléchargement fiable.",
895+
'st.skills.cws.clear_package': "Effacer le ZIP sélectionné",
896+
'st.skills.cws.package_cleared': "ZIP de publication sélectionné supprimé du stockage local.",
897+
'st.skills.cws.package_zip_only': "Choisissez un package de publication au format .zip.",
898+
'st.skills.cws.package_too_large': "Le fichier ZIP doit faire entre 1 octet et 100 Mo.",
899899
};

test/run.js

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11688,16 +11688,16 @@ test('all locales cover English keys and preserve interpolation placeholders', a
1168811688
}
1168911689
});
1169011690

11691-
test('locale helpers apply RTL direction for Arabic and Hebrew', () => {
11691+
test('locale helpers apply RTL direction for Arabic, Hebrew, and Persian', () => {
1169211692
for (const [label, rel] of [
1169311693
['chrome', 'src/chrome/src/ui/i18n.js'],
1169411694
['firefox', 'src/firefox/src/ui/i18n.js'],
1169511695
]) {
1169611696
const i18n = fs.readFileSync(path.join(ROOT, rel), 'utf8');
1169711697
assert.match(
1169811698
i18n,
11699-
/const RTL_LOCALES = new Set\(\['ar', 'he'\]\);/,
11700-
`${label}: Arabic and Hebrew should be registered as RTL locales`,
11699+
/const RTL_LOCALES = new Set\(\['ar', 'he', 'fa'\]\);/,
11700+
`${label}: Arabic, Hebrew, and Persian should be registered as RTL locales`,
1170111701
);
1170211702
assert.match(
1170311703
i18n,
@@ -11707,6 +11707,52 @@ test('locale helpers apply RTL direction for Arabic and Hebrew', () => {
1170711707
}
1170811708
});
1170911709

11710+
test('new locales are registered in extension and web language dropdowns', () => {
11711+
const addedLocales = [
11712+
['hi', 'हिन्दी'],
11713+
['pt', 'Português'],
11714+
['vi', 'Tiếng Việt'],
11715+
['bn', 'বাংলা'],
11716+
['fa', 'فارسی'],
11717+
];
11718+
11719+
for (const [label, rel] of [
11720+
['chrome', 'src/chrome/src/ui/i18n.js'],
11721+
['firefox', 'src/firefox/src/ui/i18n.js'],
11722+
]) {
11723+
const i18n = fs.readFileSync(path.join(ROOT, rel), 'utf8');
11724+
for (const [code, languageLabel] of addedLocales) {
11725+
assert.match(i18n, new RegExp(`import ${code} from '\\.\\/locales\\/${code}\\.js';`), `${label}: ${code} locale import missing`);
11726+
assert.ok(i18n.includes(`{ code: '${code}', label: '${languageLabel}' }`), `${label}: ${code} language option missing`);
11727+
}
11728+
}
11729+
11730+
const webBuild = fs.readFileSync(path.join(ROOT, 'web/build/build.mjs'), 'utf8');
11731+
const webTemplate = fs.readFileSync(path.join(ROOT, 'web/build/template.html'), 'utf8');
11732+
const privacy = fs.readFileSync(path.join(ROOT, 'web/privacy.html'), 'utf8');
11733+
for (const [code, languageLabel] of addedLocales) {
11734+
assert.match(webBuild, new RegExp(`\\{ code: '${code}', bcp47:`), `web build: ${code} locale missing`);
11735+
for (const [surface, html] of [['landing template', webTemplate], ['privacy page', privacy]]) {
11736+
const option = `<option value="${code}">${languageLabel}</option>`;
11737+
assert.equal(html.split(option).length - 1, 1, `${surface}: ${code} language option should appear exactly once`);
11738+
assert.match(html, new RegExp(`const LOCALES = \\[[^\\]]*'${code}'`), `${surface}: ${code} routing entry missing`);
11739+
}
11740+
}
11741+
});
11742+
11743+
test('web landing builder preserves template markers and HTML entity escaping', () => {
11744+
const build = fs.readFileSync(path.join(ROOT, 'web/build/build.mjs'), 'utf8');
11745+
assert.ok(build.includes('{{locale_code}}'), 'web build: locale placeholder documentation should remain intact');
11746+
assert.doesNotMatch(build, /\{\{(?:locale_[a-z0-9_]+|docs_url|hreflang_links|faq_jsonld)\)\}/, 'web build: malformed template placeholder found');
11747+
assert.ok(
11748+
build.includes(`'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',`),
11749+
'web build: plain-text substitutions must HTML-escape special characters',
11750+
);
11751+
for (const entity of ['&amp;', '&lt;', '&gt;', '&#39;', '&quot;']) {
11752+
assert.ok(build.includes(`.replace(/${entity}/g,`), `web build: htmlToPlain must decode ${entity}`);
11753+
}
11754+
});
11755+
1171011756
test('Hebrew is registered as a complete RTL locale in both builds', async () => {
1171111757
for (const [label, prefix] of [
1171211758
['chrome', 'src/chrome'],

web/ar/index.html

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
<link rel="alternate" hreflang="ms" href="https://webbrain.one/ms/">
4242
<link rel="alternate" hreflang="tl" href="https://webbrain.one/tl/">
4343
<link rel="alternate" hreflang="he" href="https://webbrain.one/he/">
44+
<link rel="alternate" hreflang="hi" href="https://webbrain.one/hi/">
45+
<link rel="alternate" hreflang="pt" href="https://webbrain.one/pt/">
46+
<link rel="alternate" hreflang="vi" href="https://webbrain.one/vi/">
47+
<link rel="alternate" hreflang="bn" href="https://webbrain.one/bn/">
48+
<link rel="alternate" hreflang="fa" href="https://webbrain.one/fa/">
4449
<link rel="alternate" hreflang="x-default" href="https://webbrain.one/">
4550
<!-- JSON-LD Structured Data -->
4651
<script type="application/ld+json">
@@ -2140,6 +2145,11 @@
21402145
<option value="ms">Bahasa Melayu</option>
21412146
<option value="tl">Filipino</option>
21422147
<option value="he">עברית</option>
2148+
<option value="hi">हिन्दी</option>
2149+
<option value="pt">Português</option>
2150+
<option value="vi">Tiếng Việt</option>
2151+
<option value="bn">বাংলা</option>
2152+
<option value="fa">فارسی</option>
21432153
</select>
21442154
<button class="theme-toggle" type="button" id="theme-toggle" aria-label="تبديل المظهر الفاتح / الداكن" title="تبديل المظهر الفاتح / الداكن">
21452155
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
@@ -3275,7 +3285,7 @@ <h3>انشر الكلمة، شارك الحبّ</h3>
32753285
instead of collapsing to the locale homepage. -->
32763286
<script>
32773287
(function () {
3278-
const LOCALES = ['en', 'es', 'fr', 'tr', 'zh', 'ru', 'uk', 'ar', 'ja', 'ko', 'id', 'th', 'ms', 'tl', 'he'];
3288+
const LOCALES = ['en', 'es', 'fr', 'tr', 'zh', 'ru', 'uk', 'ar', 'ja', 'ko', 'id', 'th', 'ms', 'tl', 'he', 'hi', 'pt', 'vi', 'bn', 'fa'];
32793289
const sel = document.getElementById('lang-dropdown');
32803290
if (!sel) return;
32813291
const current = sel.dataset.current || 'en';

0 commit comments

Comments
 (0)