diff --git a/.changeset/shy-wolves-ring.md b/.changeset/shy-wolves-ring.md new file mode 100644 index 000000000000..f48a8de75e30 --- /dev/null +++ b/.changeset/shy-wolves-ring.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Fixes a bug in `Astro.currentLocale` that wasn't returning the correct locale when a locale is configured via `path` diff --git a/.changeset/six-fishes-beg.md b/.changeset/six-fishes-beg.md new file mode 100644 index 000000000000..e753dc59ccac --- /dev/null +++ b/.changeset/six-fishes-beg.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Fixes a regression in `Astro.currentLocale` where it stopped working properly with dynamic routes diff --git a/packages/astro/src/core/render/context.ts b/packages/astro/src/core/render/context.ts index faba9b86a266..8511942f348a 100644 --- a/packages/astro/src/core/render/context.ts +++ b/packages/astro/src/core/render/context.ts @@ -249,20 +249,32 @@ export function computeCurrentLocale( if (!routeData) { return defaultLocale; } - - for (const segment of routeData.route.split('/')) { + // Typically, RouteData::pathname has the correct information in SSR, but it's not available in SSG, so we fall back + // to use the pathname from the Request + const pathname = routeData.pathname ?? new URL(request.url).pathname; + for (const segment of pathname.split('/').filter(Boolean)) { for (const locale of locales) { if (typeof locale === 'string') { + // we skip ta locale that isn't present in the current segment + + if (!segment.includes(locale)) continue; if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) { return locale; } } else { if (locale.path === segment) { return locale.codes.at(0); + } else { + for (const code of locale.codes) { + if (normalizeTheLocale(code) === normalizeTheLocale(segment)) { + return code; + } + } } } } } + if ( routingStrategy === 'pathname-prefix-other-locales' || routingStrategy === 'domains-prefix-other-locales' diff --git a/packages/astro/test/fixtures/i18n-routing/src/pages/blog/[...lang]/index.astro b/packages/astro/test/fixtures/i18n-routing/src/pages/blog/[...lang]/index.astro new file mode 100644 index 000000000000..f67085be8a5b --- /dev/null +++ b/packages/astro/test/fixtures/i18n-routing/src/pages/blog/[...lang]/index.astro @@ -0,0 +1,22 @@ +--- +const currentLocale = Astro.currentLocale; + + +export async function getStaticPaths() { + return [ + { params: { lang: undefined } }, + { params: { lang: 'es' } } + ] +} + +--- + + + + + Astro + + +Current Locale: {currentLocale ? currentLocale : "none"} + + diff --git a/packages/astro/test/fixtures/i18n-routing/src/pages/spanish/index.astro b/packages/astro/test/fixtures/i18n-routing/src/pages/spanish/index.astro new file mode 100644 index 000000000000..052be7d8cd85 --- /dev/null +++ b/packages/astro/test/fixtures/i18n-routing/src/pages/spanish/index.astro @@ -0,0 +1,13 @@ +--- +const currentLocale = Astro.currentLocale; + +--- + + + + Astro + + +Current Locale: {currentLocale ? currentLocale : "none"} + + diff --git a/packages/astro/test/i18n-routing.test.js b/packages/astro/test/i18n-routing.test.js index b3d496ff41ba..2e8256cac2ab 100644 --- a/packages/astro/test/i18n-routing.test.js +++ b/packages/astro/test/i18n-routing.test.js @@ -52,7 +52,6 @@ describe('astro:i18n virtual module', () => { let html = await response.text(); let $ = cheerio.load(html); - console.log(html); expect($('body').text()).includes("Virtual module doesn't break"); expect($('body').text()).includes('Absolute URL pt: https://example.pt/about'); expect($('body').text()).includes('Absolute URL it: http://it.example.com/'); @@ -1005,6 +1004,67 @@ describe('[SSG] i18n routing', () => { expect(html).to.include('Redirecting to: /new-site/'); }); }); + + describe('current locale', () => { + describe('with [prefix-other-locales]', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/i18n-routing/', + }); + await fixture.build(); + }); + + it('should return the default locale', async () => { + const html = await fixture.readFile('/current-locale/index.html'); + expect(html).includes('Current Locale: en'); + }); + + it('should return the default locale when rendering a route with spread operator', async () => { + const html = await fixture.readFile('/blog/es/index.html'); + expect(html).includes('Current Locale: es'); + }); + + it('should return the default locale of the current URL', async () => { + const html = await fixture.readFile('/pt/start/index.html'); + expect(html).includes('Current Locale: pt'); + }); + + it('should return the default locale when a route is dynamic', async () => { + const html = await fixture.readFile('/dynamic/lorem/index.html'); + expect(html).includes('Current Locale: en'); + }); + + it('should returns the correct locale when requesting a locale via path', async () => { + const html = await fixture.readFile('/spanish/index.html'); + expect(html).includes('Current Locale: es'); + }); + }); + + describe('with [pathname-prefix-always]', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/i18n-routing-prefix-always/', + }); + await fixture.build(); + }); + + it('should return the locale of the current URL (en)', async () => { + const html = await fixture.readFile('/en/start/index.html'); + expect(html).includes('Current Locale: en'); + }); + + it('should return the locale of the current URL (pt)', async () => { + const html = await fixture.readFile('/pt/start/index.html'); + expect(html).includes('Current Locale: pt'); + }); + }); + }); }); describe('[SSR] i18n routing', () => { let app; @@ -1525,6 +1585,13 @@ describe('[SSR] i18n routing', () => { expect(await response.text()).includes('Current Locale: en'); }); + it('should return the default locale when rendering a route with spread operator', async () => { + let request = new Request('http://example.com/blog/es', {}); + let response = await app.render(request); + expect(response.status).to.equal(200); + expect(await response.text()).includes('Current Locale: es'); + }); + it('should return the default locale of the current URL', async () => { let request = new Request('http://example.com/pt/start', {}); let response = await app.render(request);