diff --git a/src/js/i18n/i18n.ts b/src/js/i18n/i18n.ts index 4307b74f..1ee31a2b 100644 --- a/src/js/i18n/i18n.ts +++ b/src/js/i18n/i18n.ts @@ -61,6 +61,14 @@ export const languageNames: Record = { 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; diff --git a/src/tests/i18n.test.ts b/src/tests/i18n.test.ts index ae6b7e1a..123583b6 100644 --- a/src/tests/i18n.test.ts +++ b/src/tests/i18n.test.ts @@ -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,55 @@ 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 = + 'tools' + + 'de' + + 'pl'; + + rewriteLinks(); + + const links = document.querySelectorAll('a'); + expect(links[0].getAttribute('href')).toBe('/pl/tools'); + expect(links[1].getAttribute('href')).toBe('/de/merge-pdf'); + expect(links[2].getAttribute('href')).toBe('/pl/merge-pdf'); + }); });