mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
fix(i18n): parse params and props correctly with fallback (#12709)
Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
This commit is contained in:
parent
358eae83b7
commit
e3bfd93969
5 changed files with 104 additions and 10 deletions
5
.changeset/selfish-paws-play.md
Normal file
5
.changeset/selfish-paws-play.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes a bug where Astro couldn't correctly parse `params` and `props` when receiving i18n fallback URLs
|
|
@ -47,7 +47,8 @@ export async function getProps(opts: GetParamsAndPropsOptions): Promise<Props> {
|
||||||
base,
|
base,
|
||||||
});
|
});
|
||||||
|
|
||||||
// The pathname used here comes from the server, which already encored.
|
if (!staticPaths.length) return {};
|
||||||
|
// The pathname used here comes from the server, which already encoded.
|
||||||
// Since we decided to not mess up with encoding anymore, we need to decode them back so the parameters can match
|
// Since we decided to not mess up with encoding anymore, we need to decode them back so the parameters can match
|
||||||
// the ones expected from the users
|
// the ones expected from the users
|
||||||
const params = getParams(route, decodeURI(pathname));
|
const params = getParams(route, decodeURI(pathname));
|
||||||
|
@ -77,7 +78,11 @@ export function getParams(route: RouteData, pathname: string): Params {
|
||||||
if (!route.params.length) return {};
|
if (!route.params.length) return {};
|
||||||
// The RegExp pattern expects a decoded string, but the pathname is encoded
|
// The RegExp pattern expects a decoded string, but the pathname is encoded
|
||||||
// when the URL contains non-English characters.
|
// when the URL contains non-English characters.
|
||||||
const paramsMatch = route.pattern.exec(pathname);
|
const paramsMatch =
|
||||||
|
route.pattern.exec(pathname) ||
|
||||||
|
route.fallbackRoutes
|
||||||
|
.map((fallbackRoute) => fallbackRoute.pattern.exec(pathname))
|
||||||
|
.find((x) => x);
|
||||||
if (!paramsMatch) return {};
|
if (!paramsMatch) return {};
|
||||||
const params: Params = {};
|
const params: Params = {};
|
||||||
route.params.forEach((key, i) => {
|
route.params.forEach((key, i) => {
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
---
|
---
|
||||||
|
// for SSR
|
||||||
|
const blogs = {
|
||||||
|
1: { content: "Hello world" },
|
||||||
|
2: { content: "Eat Something" },
|
||||||
|
3: { content: "How are you?" },
|
||||||
|
}
|
||||||
|
const id = Astro.params?.id;
|
||||||
|
const ssrContent = id && blogs[id]?.content;
|
||||||
|
|
||||||
|
// for SSG
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return [
|
return [
|
||||||
{params: {id: '1'}, props: { content: "Hello world" }},
|
{params: {id: '1'}, props: { content: "Hello world" }},
|
||||||
{params: {id: '2'}, props: { content: "Eat Something" }},
|
{params: {id: '2'}, props: { content: "Eat Something" }},
|
||||||
{params: {id: '3'}, props: { content: "How are you?" }},
|
{params: {id: '3'}, props: { content: "How are you?" }},
|
||||||
];
|
]
|
||||||
}
|
}
|
||||||
const { content } = Astro.props;
|
const { content } = Astro.props
|
||||||
---
|
---
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Astro</title>
|
<title>Astro</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{content}
|
{content || ssrContent}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
---
|
---
|
||||||
|
const blogs = {
|
||||||
|
1: { content: "Hola mundo" },
|
||||||
|
2: { content: "Eat Something" },
|
||||||
|
3: { content: "How are you?" },
|
||||||
|
}
|
||||||
|
const id = Astro.params?.id;
|
||||||
|
const ssrContent = id && blogs[id]?.content;
|
||||||
|
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return [
|
return [
|
||||||
{params: {id: '1'}, props: { content: "Hola mundo" }},
|
{params: {id: '1'}, props: { content: "Hola mundo" }},
|
||||||
|
@ -13,6 +21,6 @@ const { content } = Astro.props;
|
||||||
<title>Astro</title>
|
<title>Astro</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{content}
|
{content || ssrContent}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
import * as assert from 'node:assert/strict';
|
import * as assert from 'node:assert/strict';
|
||||||
import { after, afterEach, before, describe, it } from 'node:test';
|
import { after, afterEach, before, describe, it } from 'node:test';
|
||||||
import * as cheerio from 'cheerio';
|
|
||||||
import testAdapter from './test-adapter.js';
|
import testAdapter from './test-adapter.js';
|
||||||
import { loadFixture } from './test-utils.js';
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
@ -2000,12 +2000,14 @@ describe('Fallback rewrite dev server', () => {
|
||||||
root: './fixtures/i18n-routing-fallback/',
|
root: './fixtures/i18n-routing-fallback/',
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr', 'es', 'it', 'pt'],
|
||||||
routing: {
|
routing: {
|
||||||
prefixDefaultLocale: false,
|
prefixDefaultLocale: false,
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
fr: 'en',
|
fr: 'en',
|
||||||
|
it: 'en',
|
||||||
|
es: 'pt',
|
||||||
},
|
},
|
||||||
fallbackType: 'rewrite',
|
fallbackType: 'rewrite',
|
||||||
},
|
},
|
||||||
|
@ -2021,6 +2023,27 @@ describe('Fallback rewrite dev server', () => {
|
||||||
assert.match(html, /Hello/);
|
assert.match(html, /Hello/);
|
||||||
// assert.fail()
|
// assert.fail()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with path parameters correctly (fr)', async () => {
|
||||||
|
let response = await fixture.fetch('/fr/blog/1');
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const text = await response.text();
|
||||||
|
assert.match(text, /Hello world/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with path parameters correctly (es)', async () => {
|
||||||
|
let response = await fixture.fetch('/es/blog/1');
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const text = await response.text();
|
||||||
|
assert.match(text, /Hola mundo/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with query parameters correctly (it)', async () => {
|
||||||
|
let response = await fixture.fetch('/it/blog/1');
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const text = await response.text();
|
||||||
|
assert.match(text, /Hello world/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Fallback rewrite SSG', () => {
|
describe('Fallback rewrite SSG', () => {
|
||||||
|
@ -2032,13 +2055,15 @@ describe('Fallback rewrite SSG', () => {
|
||||||
root: './fixtures/i18n-routing-fallback/',
|
root: './fixtures/i18n-routing-fallback/',
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr', 'es', 'it', 'pt'],
|
||||||
routing: {
|
routing: {
|
||||||
prefixDefaultLocale: false,
|
prefixDefaultLocale: false,
|
||||||
fallbackType: 'rewrite',
|
fallbackType: 'rewrite',
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
fr: 'en',
|
fr: 'en',
|
||||||
|
it: 'en',
|
||||||
|
es: 'pt',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -2051,6 +2076,21 @@ describe('Fallback rewrite SSG', () => {
|
||||||
assert.match(html, /Hello/);
|
assert.match(html, /Hello/);
|
||||||
// assert.fail()
|
// assert.fail()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with path parameters correctly (fr)', async () => {
|
||||||
|
const html = await fixture.readFile('/fr/blog/1/index.html');
|
||||||
|
assert.match(html, /Hello world/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with path parameters correctly (es)', async () => {
|
||||||
|
const html = await fixture.readFile('/es/blog/1/index.html');
|
||||||
|
assert.match(html, /Hola mundo/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with query parameters correctly (it)', async () => {
|
||||||
|
const html = await fixture.readFile('/it/blog/1/index.html');
|
||||||
|
assert.match(html, /Hello world/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Fallback rewrite SSR', () => {
|
describe('Fallback rewrite SSR', () => {
|
||||||
|
@ -2066,13 +2106,15 @@ describe('Fallback rewrite SSR', () => {
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr', 'es', 'it', 'pt'],
|
||||||
routing: {
|
routing: {
|
||||||
prefixDefaultLocale: false,
|
prefixDefaultLocale: false,
|
||||||
fallbackType: 'rewrite',
|
fallbackType: 'rewrite',
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
fr: 'en',
|
fr: 'en',
|
||||||
|
it: 'en',
|
||||||
|
es: 'pt',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -2087,4 +2129,28 @@ describe('Fallback rewrite SSR', () => {
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
assert.match(html, /Hello/);
|
assert.match(html, /Hello/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with path parameters correctly (fr)', async () => {
|
||||||
|
let request = new Request('http://example.com/new-site/fr/blog/1');
|
||||||
|
let response = await app.render(request);
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const text = await response.text();
|
||||||
|
assert.match(text, /Hello world/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with path parameters correctly (es)', async () => {
|
||||||
|
let request = new Request('http://example.com/new-site/es/blog/1');
|
||||||
|
let response = await app.render(request);
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const text = await response.text();
|
||||||
|
assert.match(text, /Hola mundo/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fallback locale paths with query parameters correctly (it)', async () => {
|
||||||
|
let request = new Request('http://example.com/new-site/it/blog/1');
|
||||||
|
let response = await app.render(request);
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const text = await response.text();
|
||||||
|
assert.match(text, /Hello world/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue