fix(i18n): detect Polish and derive language routing from supportedLanguages

Polish (pl) was missing from the hand-maintained language regexes, so
getLanguageFromUrl() returned en for /pl/... pages and the UI fell back
to English after the initial server-rendered Polish content.

Derive the pattern from supportedLanguages so new locales cannot be
missed again, and cover Polish in getLanguageFromUrl(), changeLanguage()
and rewriteLinks() with regression tests.

Refs #972
This commit is contained in:
Athanasios Panos committed 2026-11-10 23:16:37 +01:00
1 parent 18c4aac8bc
commit edd824e1e3
2 files changed
+77 -7

No files matched your search

+11 -7
View File
@@ -61,6 +61,14 @@ export const languageNames: Record<SupportedLanguage, string> = {
fa: 'فارسی',
};
// Longest codes first so multi-part tags like zh-TW win over their prefix (zh).
const LANGUAGE_PATTERN = [...supportedLanguages]
.sort((a, b) => b.length - a.length)
.join('|');
const URL_LANGUAGE_REGEX = new RegExp(`^/(${LANGUAGE_PATTERN})(?:/|$)`);
const LANGUAGE_PREFIX_REGEX = new RegExp(`^/(${LANGUAGE_PATTERN})(/.*)?$`);
export const getLanguageFromUrl = (): SupportedLanguage => {
const basePath = import.meta.env.BASE_URL.replace(/\/$/, '');
let path = window.location.pathname;
@@ -73,9 +81,7 @@ export const getLanguageFromUrl = (): SupportedLanguage => {
path = '/' + path;
}
const langMatch = path.match(
/^\/(en|ar|fr|es|de|zh|zh-TW|vi|tr|id|it|pt|nl|be|da|ko|sv|ru|ja|uk|sk|ca|fa)(?:\/|$)/
);
const langMatch = path.match(URL_LANGUAGE_REGEX);
if (
langMatch &&
supportedLanguages.includes(langMatch[1] as SupportedLanguage)
@@ -211,9 +217,7 @@ export const changeLanguage = (lang: SupportedLanguage): void => {
}
let pagePathWithoutLang = relativePath;
const langPrefixMatch = relativePath.match(
/^\/(en|ar|fr|es|de|zh|zh-TW|vi|tr|id|it|pt|nl|be|da|ko|sv|ru|ja|uk|sk|ca|fa)(\/.*)?$/
);
const langPrefixMatch = relativePath.match(LANGUAGE_PREFIX_REGEX);
if (langPrefixMatch) {
pagePathWithoutLang = langPrefixMatch[2] || '/';
}
@@ -320,7 +324,7 @@ export const rewriteLinks = (): void => {
}
const langPrefixRegex = new RegExp(
`^(${basePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})?/?(en|ar|fr|es|de|zh|zh-TW|vi|tr|id|it|pt|nl|be|da|ko|sv|ru|ja|uk|sk|ca|fa)(/|$|[?#])`
`^(${basePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})?/?(${LANGUAGE_PATTERN})(/|$|[?#])`
);
if (langPrefixRegex.test(href)) {
return;
+66
View File
@@ -3,6 +3,8 @@ import {
changeLanguage,
getLanguageFromUrl,
LANGUAGE_CHOICE_KEY,
rewriteLinks,
supportedLanguages,
} from '@/js/i18n/i18n';
describe('getLanguageFromUrl', () => {
@@ -75,6 +77,22 @@ describe('getLanguageFromUrl', () => {
window.location.pathname = '/zh-TW/merge-pdf';
expect(getLanguageFromUrl()).toBe('zh-TW');
});
it('should match every supported language from the URL', () => {
supportedLanguages.forEach((lang) => {
window.location.pathname = `/${lang}/`;
expect(getLanguageFromUrl()).toBe(lang);
window.location.pathname = `/${lang}/merge-pdf`;
expect(getLanguageFromUrl()).toBe(lang);
});
});
it('should match Polish from the URL', () => {
window.location.pathname = '/pl/';
expect(getLanguageFromUrl()).toBe('pl');
window.location.pathname = '/pl/merge-pdf';
expect(getLanguageFromUrl()).toBe('pl');
});
});
describe('changeLanguage', () => {
@@ -143,4 +161,52 @@ describe('changeLanguage', () => {
changeLanguage('ja');
expect(target).toBe('/pdf/ja/merge-pdf');
});
it('swaps the Polish prefix instead of nesting it', () => {
at('/pl/merge-pdf');
changeLanguage('de');
expect(target).toBe('/de/merge-pdf');
at('/pl/');
changeLanguage('en');
expect(target).toBe('/');
});
});
describe('rewriteLinks', () => {
const originalLocation = window.location;
function at(pathname: string) {
Object.defineProperty(window, 'location', {
value: { ...originalLocation, pathname },
writable: true,
configurable: true,
});
}
beforeEach(() => {
document.body.innerHTML = '';
vi.stubEnv('BASE_URL', '/');
});
afterEach(() => {
Object.defineProperty(window, 'location', {
value: originalLocation,
writable: true,
configurable: true,
});
document.body.innerHTML = '';
vi.unstubAllEnvs();
});
it('prefixes links on Polish pages and leaves language links alone', () => {
at('/pl/merge-pdf');
document.body.innerHTML =
'<a href="/tools">tools</a><a href="/de/merge-pdf">de</a>';
rewriteLinks();
const links = document.querySelectorAll('a');
expect(links[0].getAttribute('href')).toBe('/pl/tools');
expect(links[1].getAttribute('href')).toBe('/de/merge-pdf');
});
});