mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 01:20:07 -05:00
Show country and language flags for every regional popup locale
Verify all 245 registered popup locales and every translation file. Resolve regional flags from base languages with explicit legacy and script exceptions; preserve country-before-language order in RTL interfaces. Unit wiring and actual-helper checks pass. Add and syntax-check browser coverage; browser execution remains pending. Thanks to xet7 !
This commit is contained in:
1 parent
cb68305053
commit
8549b6c4c6
6 files changed
+76
-3
No files matched your search
@@ -1280,3 +1280,11 @@ body.mobile-mode .pop-over .content-wrapper {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* Keep country then language flags in that visual order in RTL locales too. */
|
||||
.pop-over[data-popup='changeLanguagePopup'] .language-flags {
|
||||
direction: ltr;
|
||||
unicode-bidi: isolate;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
}
|
||||
@@ -243,7 +243,7 @@ template(name="changeLanguagePopup")
|
||||
each languages
|
||||
li(class="{{# if isCurrentLanguage}}active{{/if}}")
|
||||
a.js-set-language
|
||||
span.emoji-icon {{languageFlag}}
|
||||
span.emoji-icon.language-flags(dir="ltr" aria-hidden="true") {{languageFlag}}
|
||||
| {{name}}
|
||||
if rtl
|
||||
| (RTL)
|
||||
|
||||
@@ -444,7 +444,23 @@ Template.changeLanguagePopup.helpers({
|
||||
'kl': '🇬🇱', 'iu': '🇨🇦', 'chr': '🇺🇸', 'nah': '🇲🇽', 'bua': '🇷🇺', 'csb': '🇵🇱', 'szl': '🇵🇱', 'an': '🇪🇸', 'lld': '🇮🇹', 'rup': '🇬🇷',
|
||||
'mai': '🇮🇳', 'bho': '🇮🇳', 'kok': '🇮🇳', 'ks': '🇮🇳', 'ckb': '🇮🇶', 'tig': '🇪🇷', 'wal': '🇪🇹'
|
||||
};
|
||||
return flagMap[this.tag] || '🌐';
|
||||
// These legacy locale prefixes name different languages, not Venda/Walloon.
|
||||
const languageOverrides = { 've-CC': '🇮🇹', 've-PP': '🇷🇺', 'wa-RR': '🇵🇭' };
|
||||
const tag = this.tag;
|
||||
const base = tag.split(/[-_@]/)[0];
|
||||
const language = languageOverrides[tag] || flagMap[base] || '🌐';
|
||||
if (!/[-_@]/.test(tag)) return language;
|
||||
const countryOverrides = {
|
||||
'be-BE': '🇧🇾', 've-CC': '🇮🇹', 've-PP': '🇷🇺', 'vl-SS': '🇧🇪',
|
||||
'wa-RR': '🇵🇭', 'es-LA': '🌐', 'zh-GB': '🇨🇳', 'en-YS': '🌐',
|
||||
'az-LA': '🇦🇿', 'uz-LA': '🇺🇿', 'uz-AR': '🇺🇿',
|
||||
};
|
||||
const countries = new Set('ZA AU DZ EG AZ ES CZ GB AT CH DE GR BR IT MY AR CL CO MX PE PY EE IR BE CA NL IN IL JP KH KR PT RO RU SI UA UZ VN CN HK SG TW ID TR TM'.split(' '));
|
||||
const suffix = tag.split(/[-_@]/)[1];
|
||||
const country = countryOverrides[tag] || (countries.has(suffix)
|
||||
? String.fromCodePoint(...[...suffix].map(letter => 0x1f1e6 + letter.charCodeAt(0) - 65))
|
||||
: language);
|
||||
return `${country} ${language}`;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -34,3 +34,30 @@ test('language list is an auto-filling multi-column grid', () => {
|
||||
});
|
||||
|
||||
console.log(`\nAll ${passed} change-language-columns tests passed`);
|
||||
|
||||
// Evaluate the actual popup helpers against every registry entry, not a copied map.
|
||||
const vm = require('node:vm');
|
||||
const { parseLanguageMetadata } = require('./lib/languageRegistrySource.cjs');
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const rows = parseLanguageMetadata(fs.readFileSync(path.join(root, 'imports/i18n/languages.js'), 'utf8'));
|
||||
const header = fs.readFileSync(path.join(root, 'client/components/users/userHeader.js'), 'utf8');
|
||||
const start = header.indexOf('Template.changeLanguagePopup.helpers({');
|
||||
const end = header.indexOf('Template.changeLanguagePopup.events(', start);
|
||||
let helpers;
|
||||
vm.runInNewContext(header.slice(start, end), {
|
||||
Template: { changeLanguagePopup: { helpers(value) { helpers = value; } } },
|
||||
TAPi18n: { getSupportedLanguages: () => rows.map(row => ({tag: row[2], name: row[3], rtl: row[4]})), getLanguage: () => 'en' },
|
||||
});
|
||||
assert.deepEqual(Array.from(helpers.languages(), row => row.tag).sort(), rows.map(row => row[2]).sort(), 'all registered locales appear exactly once');
|
||||
for (const row of rows) {
|
||||
const flags = helpers.languageFlag.call({tag: row[2]});
|
||||
assert.ok(flags && !flags.includes('undefined'), row[2]);
|
||||
if (/[-_@]/.test(row[2])) assert.equal(flags.split(' ').length, 2, `country then language: ${row[2]}`);
|
||||
else if (!['eo', 'tlh', 'vo', 'ia'].includes(row[2])) assert.notEqual(flags, '🌐', row[2]);
|
||||
}
|
||||
for (const [tag, expected] of Object.entries({'es-CO':'🇨🇴 🇪🇸', 'es_CO':'🇨🇴 🇪🇸', 'fr-CA':'🇨🇦 🇫🇷', 'be-BE':'🇧🇾 🇧🇾', 've-PP':'🇷🇺 🇷🇺', 've-CC':'🇮🇹 🇮🇹', 'wa-RR':'🇵🇭 🇵🇭', 'uz-AR':'🇺🇿 🇺🇿', 'zh-GB':'🇨🇳 🇨🇳', 'eo':'🌐', 'tlh':'🌐'})) {
|
||||
assert.equal(helpers.languageFlag.call({tag}), expected, tag);
|
||||
}
|
||||
assert.notEqual(helpers.languageFlag.call({tag:'ve-PP'}), '🇿🇦 🇿🇦');
|
||||
assert.ok(fs.readFileSync(path.join(root, 'client/components/users/userHeader.jade'), 'utf8').includes('language-flags(dir="ltr"'));
|
||||
console.log(`changeLanguageColumns: all ${rows.length} popup locales and country/language flag ordering verified`);
|
||||
@@ -82,7 +82,7 @@ test('every language has a flag, or the deliberate globe', () => {
|
||||
.filter(t => !CONSTRUCTED.includes(t) && !new RegExp(`'${t}': '`).test(map));
|
||||
assert.ok(missing.length <= 12,
|
||||
`${missing.length} languages fall back to the globe: ${missing.join(' ')}`);
|
||||
assert.ok(/return flagMap\[this\.tag\] \|\| '🌐';/.test(header),
|
||||
assert.ok(header.includes("flagMap[base] || '🌐'"),
|
||||
'and the fallback is a globe, not a wrong country');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
const { test, expect } = require('../fixtures');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { parseLanguageMetadata } = require('../../lib/languageRegistrySource.cjs');
|
||||
const rows = parseLanguageMetadata(fs.readFileSync(path.resolve(__dirname, '../../../imports/i18n/languages.js'), 'utf8'));
|
||||
test('language popup lists every locale and orders country before language flags', async ({ boardPage }) => {
|
||||
await boardPage.locator('.js-open-header-member-menu').first().click();
|
||||
await boardPage.locator('.js-change-language').click();
|
||||
const popup = boardPage.locator(".pop-over[data-popup='changeLanguagePopup']");
|
||||
await expect(popup).toBeVisible();
|
||||
await expect(popup.locator('.js-set-language')).toHaveCount(rows.length);
|
||||
for (const [tag, name] of rows.map(row => [row[2], row[3]])) {
|
||||
const entry = popup.locator('.js-set-language').filter({ hasText: name });
|
||||
await expect(entry.first()).toBeAttached();
|
||||
}
|
||||
for (const [name, flags] of [['Español en Colombia','🇨🇴 🇪🇸'], ['Français (Canada)','🇨🇦 🇫🇷'], ['Vepsän kelʹ','🇷🇺 🇷🇺']]) {
|
||||
const entry = popup.locator('.js-set-language').filter({hasText:name});
|
||||
await expect(entry.locator('.language-flags')).toHaveText(flags);
|
||||
await expect(entry.locator('.language-flags')).toHaveAttribute('dir','ltr');
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user