Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions cypress/e2e/toolbar.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,41 @@ describe('Testing the Toolbar', () => {
.and('include', 'Dibujar Marcador de Círculo');
});

it('Sets 3-letter language code correctly to fallback when not available', () => {
cy.window().then(({ map, L }) => {
map.pm.setLang('jam'); // 'jam' translations don't exist
expect(L.PM.activeLang).to.equal('en'); // Should fallback to 'en'
});
});

it('Sets 3-letter language code with region correctly to fallback when not available', () => {
cy.window().then(({ map, L }) => {
map.pm.setLang('jam-JM'); // 'jam' translations don't exist
expect(L.PM.activeLang).to.equal('en'); // Should fallback to 'en'
});
});

it('Sets 2-letter language code with region correctly to base when available', () => {
cy.window().then(({ map, L }) => {
map.pm.setLang('fr-CA'); // 'fr' translations exist
expect(L.PM.activeLang).to.equal('fr'); // Should use 'fr'
});
});

it('Sets 2-letter language code ', () => {
cy.window().then(({ map, L }) => {
map.pm.setLang('de'); // 'de' translations exist
expect(L.PM.activeLang).to.equal('de'); // Should use 'de'
});
});

it('Handles non-existent language code correctly by falling back', () => {
cy.window().then(({ map, L }) => {
map.pm.setLang('xyz'); // 'xyz' translations don't exist
expect(L.PM.activeLang).to.equal('en'); // Should fallback to 'en'
});
});

it('has functioning actions', () => {
cy.toolbarButton('polygon').click();

Expand Down
13 changes: 12 additions & 1 deletion cypress/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

module.exports = () => {
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config

// Define the 'log' task
on('task', {
log(message) {
console.log('CYPRESS TASK LOG: ', message);
return null; // Task must return null or a promise
},
});

// Return the config object or plugins might not work
return config;
};
90 changes: 58 additions & 32 deletions src/js/L.PM.Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,45 +44,71 @@ const Map = L.Class.extend({
this.Keyboard._initKeyListener(map);
},
// eslint-disable-next-line default-param-last
setLang(lang = 'en', override, fallback = 'en') {
// Normalize the language code to lowercase and trim any whitespace
lang = lang.trim().toLowerCase();
setLang(langInput = 'en', override, fallback = 'en') {
// 1. Normalize input and determine the intended language code
const normalizedInput = langInput?.trim().toLowerCase() || '';
const originalFallbackLang = fallback?.trim().toLowerCase() || 'en';
let intendedLang = originalFallbackLang; // Default intention is the fallback

// First, check if the input is already in the expected format (e.g., 'fr')
if (/^[a-z]{2}$/.test(lang)) {
// No further processing needed for single-letter codes
} else {
// Handle formats like 'fr-FR', 'FR', 'fr-fr', 'fr_FR'
const normalizedLang = lang
.replace(/[-_\s]/g, '-')
.replace(/^(\w{2})$/, '$1-');
const match = normalizedLang.match(/([a-z]{2})-?([a-z]{2})?/);
const match = normalizedInput.match(/^([a-z]{2,3})(?:[-_]([a-z]{2}))?.*$/);
let baseLang = null;
if (match) {
[, baseLang] = match;
} else if (/^[a-z]{2,3}$/.test(normalizedInput)) {
baseLang = normalizedInput;
}

if (match) {
// Construct potential keys to search for in the translations object
const potentialKeys = [
`${match[1]}_${match[2]}`, // e.g., 'fr_BR'
`${match[1]}`, // e.g., 'fr'
];
if (baseLang) {
intendedLang = baseLang; // If input gives a valid base code, that's the intention
} else if (!normalizedInput) {
intendedLang = 'en'; // If input is empty/invalid, intention defaults to 'en'
}
// Now, intendedLang is the primary code derived from input, or the fallback, or 'en'.

// Search through the translations object for a matching key
for (const key of potentialKeys) {
if (translations[key]) {
lang = key; // Set lang to the matching key
break; // Exit the loop once a match is found
}
}
}
const oldLang = L.PM.activeLang || 'en'; // Store the previous language, default to 'en' if undefined

// 2. Calculate merged translations for the event *if* override is provided
let translationsForEvent = null; // Initialize
if (override && typeof override === 'object') {
// Find the best base for merging: intended lang -> fallback -> 'en' -> empty
const baseForMerge =
translations[intendedLang] ||
translations[originalFallbackLang] ||
translations.en ||
{};
// Calculate the merged result, but DO NOT assign back to global translations[intendedLang]
translationsForEvent = merge({}, baseForMerge, override);
}

const oldLang = L.PM.activeLang;
if (override) {
translations[lang] = merge(translations[fallback], override);
// 3. Determine the final active language BASED ON ORIGINAL translations
let finalLang = 'en'; // Default final language to 'en'
// Check the *original* translations, ignoring any temporary override merge
if (translations[intendedLang]) {
finalLang = intendedLang; // Use intended if it now exists (due to override or initially)
} else if (translations[originalFallbackLang]) {
finalLang = originalFallbackLang; // Otherwise, use fallback if it exists
}
// If neither intended nor fallback exists, finalLang remains 'en'

L.PM.activeLang = lang;
this.map.pm.Toolbar.reinit();
this._fireLangChange(oldLang, lang, fallback, translations[lang]);
// 4. Set active language
L.PM.activeLang = finalLang;
// 5. Reinitialize toolbar and fire event if the language actually changed
if (oldLang !== L.PM.activeLang) {
this.map.pm.Toolbar.reinit();
// Use the potentially merged translations *only* for the event payload
// Fallback to the standard active translations if no override was performed
const activeTranslations =
translationsForEvent ||
translations[L.PM.activeLang] ||
translations.en ||
{};
this._fireLangChange(
oldLang,
L.PM.activeLang,
originalFallbackLang,
activeTranslations
);
}
},
addControls(options) {
this.Toolbar.addControls(options);
Expand Down
Loading