summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Emanuele Stoppa <my.burning@gmail.com> 2023-11-16 08:54:10 -0600
committerGravatar GitHub <noreply@github.com> 2023-11-16 08:54:10 -0600
commite3dce215a5ea06bcff1b21027e5613e6518c69d4 (patch)
treebb86d2adeb5206ac46a6713d69350a4661fddc05
parent8366cd777530e6e0740f8a9c535f3c4382ffb053 (diff)
downloadastro-e3dce215a5ea06bcff1b21027e5613e6518c69d4.tar.gz
astro-e3dce215a5ea06bcff1b21027e5613e6518c69d4.tar.zst
astro-e3dce215a5ea06bcff1b21027e5613e6518c69d4.zip
feat(i18n): add `Astro.currentLocale` (#9101)
-rw-r--r--.changeset/quick-toes-peel.md5
-rw-r--r--packages/astro/src/@types/astro.ts10
-rw-r--r--packages/astro/src/core/app/index.ts8
-rw-r--r--packages/astro/src/core/build/generate.ts4
-rw-r--r--packages/astro/src/core/endpoint/index.ts28
-rw-r--r--packages/astro/src/core/middleware/index.ts2
-rw-r--r--packages/astro/src/core/pipeline.ts10
-rw-r--r--packages/astro/src/core/render/context.ts24
-rw-r--r--packages/astro/src/core/render/core.ts2
-rw-r--r--packages/astro/src/core/render/result.ts27
-rw-r--r--packages/astro/src/vite-plugin-astro-server/route.ts7
-rw-r--r--packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/en/start.astro5
-rw-r--r--packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/pt/start.astro4
-rw-r--r--packages/astro/test/fixtures/i18n-routing/src/pages/current-locale.astro12
-rw-r--r--packages/astro/test/fixtures/i18n-routing/src/pages/dynamic/[id].astro19
-rw-r--r--packages/astro/test/fixtures/i18n-routing/src/pages/pt/start.astro4
-rw-r--r--packages/astro/test/i18n-routing.test.js (renamed from packages/astro/test/i18-routing.test.js)67
17 files changed, 222 insertions, 16 deletions
diff --git a/.changeset/quick-toes-peel.md b/.changeset/quick-toes-peel.md
new file mode 100644
index 000000000..25d5c13c7
--- /dev/null
+++ b/.changeset/quick-toes-peel.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Add a new property `Astro.currentLocale`, available when `i18n` is enabled.
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index f15cf7d09..6477f7383 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -2112,6 +2112,11 @@ interface AstroSharedContext<
*/
preferredLocaleList: string[] | undefined;
+
+ /**
+ * The current locale computed from the URL of the request. It matches the locales in `i18n.locales`, and returns `undefined` otherwise.
+ */
+ currentLocale: string | undefined;
}
export interface APIContext<
@@ -2241,6 +2246,11 @@ export interface APIContext<
* [quality value]: https://developer.mozilla.org/en-US/docs/Glossary/Quality_values
*/
preferredLocaleList: string[] | undefined;
+
+ /**
+ * The current locale computed from the URL of the request. It matches the locales in `i18n.locales`, and returns `undefined` otherwise.
+ */
+ currentLocale: string | undefined;
}
export type EndpointOutput =
diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts
index ec7990119..b297171a4 100644
--- a/packages/astro/src/core/app/index.ts
+++ b/packages/astro/src/core/app/index.ts
@@ -234,7 +234,9 @@ export class App {
status,
env: this.#pipeline.env,
mod: handler as any,
- locales: this.#manifest.i18n ? this.#manifest.i18n.locales : undefined,
+ locales: this.#manifest.i18n?.locales,
+ routingStrategy: this.#manifest.i18n?.routingStrategy,
+ defaultLocale: this.#manifest.i18n?.defaultLocale,
});
} else {
const pathname = prependForwardSlash(this.removeBase(url.pathname));
@@ -269,7 +271,9 @@ export class App {
status,
mod,
env: this.#pipeline.env,
- locales: this.#manifest.i18n ? this.#manifest.i18n.locales : undefined,
+ locales: this.#manifest.i18n?.locales,
+ routingStrategy: this.#manifest.i18n?.routingStrategy,
+ defaultLocale: this.#manifest.i18n?.defaultLocale,
});
}
}
diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts
index 02837cf69..20854f779 100644
--- a/packages/astro/src/core/build/generate.ts
+++ b/packages/astro/src/core/build/generate.ts
@@ -558,7 +558,9 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli
route: pageData.route,
env: pipeline.getEnvironment(),
mod,
- locales: i18n ? i18n.locales : undefined,
+ locales: i18n?.locales,
+ routingStrategy: i18n?.routingStrategy,
+ defaultLocale: i18n?.defaultLocale,
});
let body: string | Uint8Array;
diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts
index 33c659dca..80af2358d 100644
--- a/packages/astro/src/core/endpoint/index.ts
+++ b/packages/astro/src/core/endpoint/index.ts
@@ -12,7 +12,11 @@ import { ASTRO_VERSION } from '../constants.js';
import { AstroCookies, attachCookiesToResponse } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { callMiddleware } from '../middleware/callMiddleware.js';
-import { computePreferredLocale, computePreferredLocaleList } from '../render/context.js';
+import {
+ computeCurrentLocale,
+ computePreferredLocale,
+ computePreferredLocaleList,
+} from '../render/context.js';
import { type Environment, type RenderContext } from '../render/index.js';
const encoder = new TextEncoder();
@@ -27,6 +31,8 @@ type CreateAPIContext = {
props: Record<string, any>;
adapterName?: string;
locales: string[] | undefined;
+ routingStrategy: 'prefix-always' | 'prefix-other-locales' | undefined;
+ defaultLocale: string | undefined;
};
/**
@@ -41,9 +47,12 @@ export function createAPIContext({
props,
adapterName,
locales,
+ routingStrategy,
+ defaultLocale,
}: CreateAPIContext): APIContext {
let preferredLocale: string | undefined = undefined;
let preferredLocaleList: string[] | undefined = undefined;
+ let currentLocale: string | undefined = undefined;
const context = {
cookies: new AstroCookies(request),
@@ -83,6 +92,16 @@ export function createAPIContext({
return undefined;
},
+ get currentLocale(): string | undefined {
+ if (currentLocale) {
+ return currentLocale;
+ }
+ if (locales) {
+ currentLocale = computeCurrentLocale(request, locales, routingStrategy, defaultLocale);
+ }
+
+ return currentLocale;
+ },
url: new URL(request.url),
get clientAddress() {
if (clientAddressSymbol in request) {
@@ -153,8 +172,7 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
mod: EndpointHandler,
env: Environment,
ctx: RenderContext,
- onRequest: MiddlewareHandler<MiddlewareResult> | undefined,
- locales: undefined | string[]
+ onRequest: MiddlewareHandler<MiddlewareResult> | undefined
): Promise<Response> {
const context = createAPIContext({
request: ctx.request,
@@ -162,7 +180,9 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
props: ctx.props,
site: env.site,
adapterName: env.adapterName,
- locales,
+ routingStrategy: ctx.routingStrategy,
+ defaultLocale: ctx.defaultLocale,
+ locales: ctx.locales,
});
let response;
diff --git a/packages/astro/src/core/middleware/index.ts b/packages/astro/src/core/middleware/index.ts
index 77da30aee..c02761351 100644
--- a/packages/astro/src/core/middleware/index.ts
+++ b/packages/astro/src/core/middleware/index.ts
@@ -35,6 +35,8 @@ function createContext({ request, params, userDefinedLocales = [] }: CreateConte
props: {},
site: undefined,
locales: userDefinedLocales,
+ defaultLocale: undefined,
+ routingStrategy: undefined,
});
}
diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts
index bd203b437..87f833ee5 100644
--- a/packages/astro/src/core/pipeline.ts
+++ b/packages/astro/src/core/pipeline.ts
@@ -128,6 +128,8 @@ export class Pipeline {
site: env.site,
adapterName: env.adapterName,
locales: renderContext.locales,
+ routingStrategy: renderContext.routingStrategy,
+ defaultLocale: renderContext.defaultLocale,
});
switch (renderContext.route.type) {
@@ -158,13 +160,7 @@ export class Pipeline {
}
}
case 'endpoint': {
- return await callEndpoint(
- mod as any as EndpointHandler,
- env,
- renderContext,
- onRequest,
- renderContext.locales
- );
+ return await callEndpoint(mod as any as EndpointHandler, env, renderContext, onRequest);
}
default:
throw new Error(`Couldn't find route of type [${renderContext.route.type}]`);
diff --git a/packages/astro/src/core/render/context.ts b/packages/astro/src/core/render/context.ts
index 851c41bc5..0f0bf39b0 100644
--- a/packages/astro/src/core/render/context.ts
+++ b/packages/astro/src/core/render/context.ts
@@ -29,6 +29,8 @@ export interface RenderContext {
props: Props;
locals?: object;
locales: string[] | undefined;
+ defaultLocale: string | undefined;
+ routingStrategy: 'prefix-always' | 'prefix-other-locales' | undefined;
}
export type CreateRenderContextArgs = Partial<
@@ -60,6 +62,8 @@ export async function createRenderContext(
params,
props,
locales: options.locales,
+ routingStrategy: options.routingStrategy,
+ defaultLocale: options.defaultLocale,
};
// We define a custom property, so we can check the value passed to locals
@@ -208,3 +212,23 @@ export function computePreferredLocaleList(request: Request, locales: string[])
return result;
}
+
+export function computeCurrentLocale(
+ request: Request,
+ locales: string[],
+ routingStrategy: 'prefix-always' | 'prefix-other-locales' | undefined,
+ defaultLocale: string | undefined
+): undefined | string {
+ const requestUrl = new URL(request.url);
+ for (const segment of requestUrl.pathname.split('/')) {
+ for (const locale of locales) {
+ if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
+ return locale;
+ }
+ }
+ }
+ if (routingStrategy === 'prefix-other-locales') {
+ return defaultLocale;
+ }
+ return undefined;
+}
diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts
index da9675f10..ed9ea7fdb 100644
--- a/packages/astro/src/core/render/core.ts
+++ b/packages/astro/src/core/render/core.ts
@@ -60,6 +60,8 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag
cookies,
locals: renderContext.locals ?? {},
locales: renderContext.locales,
+ defaultLocale: renderContext.defaultLocale,
+ routingStrategy: renderContext.routingStrategy,
});
// TODO: Remove in Astro 4.0
diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts
index 91dc545df..e9c8302a1 100644
--- a/packages/astro/src/core/render/result.ts
+++ b/packages/astro/src/core/render/result.ts
@@ -12,7 +12,11 @@ import { chunkToString } from '../../runtime/server/render/index.js';
import { AstroCookies } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import type { Logger } from '../logger/core.js';
-import { computePreferredLocale, computePreferredLocaleList } from './context.js';
+import {
+ computeCurrentLocale,
+ computePreferredLocale,
+ computePreferredLocaleList,
+} from './context.js';
const clientAddressSymbol = Symbol.for('astro.clientAddress');
const responseSentSymbol = Symbol.for('astro.responseSent');
@@ -47,6 +51,8 @@ export interface CreateResultArgs {
locals: App.Locals;
cookies?: AstroCookies;
locales: string[] | undefined;
+ defaultLocale: string | undefined;
+ routingStrategy: 'prefix-always' | 'prefix-other-locales' | undefined;
}
function getFunctionExpression(slot: any) {
@@ -148,6 +154,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
let cookies: AstroCookies | undefined = args.cookies;
let preferredLocale: string | undefined = undefined;
let preferredLocaleList: string[] | undefined = undefined;
+ let currentLocale: string | undefined = undefined;
// Create the result object that will be passed into the render function.
// This object starts here as an empty shell (not yet the result) but then
@@ -218,6 +225,24 @@ export function createResult(args: CreateResultArgs): SSRResult {
return undefined;
},
+ get currentLocale(): string | undefined {
+ if (currentLocale) {
+ return currentLocale;
+ }
+ if (args.locales) {
+ currentLocale = computeCurrentLocale(
+ request,
+ args.locales,
+ args.routingStrategy,
+ args.defaultLocale
+ );
+ if (currentLocale) {
+ return currentLocale;
+ }
+ }
+
+ return undefined;
+ },
params,
props,
locals,
diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts
index 7468f8819..48f89db04 100644
--- a/packages/astro/src/vite-plugin-astro-server/route.ts
+++ b/packages/astro/src/vite-plugin-astro-server/route.ts
@@ -215,6 +215,9 @@ export async function handleRoute({
env,
mod,
route,
+ locales: manifest.i18n?.locales,
+ routingStrategy: manifest.i18n?.routingStrategy,
+ defaultLocale: manifest.i18n?.defaultLocale,
});
} else {
return handle404Response(origin, incomingRequest, incomingResponse);
@@ -271,7 +274,9 @@ export async function handleRoute({
route: options.route,
mod,
env,
- locales: i18n ? i18n.locales : undefined,
+ locales: i18n?.locales,
+ routingStrategy: i18n?.routingStrategy,
+ defaultLocale: i18n?.defaultLocale,
});
}
diff --git a/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/en/start.astro b/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/en/start.astro
index 990baecd9..92e189636 100644
--- a/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/en/start.astro
+++ b/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/en/start.astro
@@ -1,8 +1,13 @@
+---
+const currentLocale = Astro.currentLocale;
+---
<html>
<head>
<title>Astro</title>
</head>
<body>
Start
+Current Locale: {currentLocale ? currentLocale : "none"}
+
</body>
</html>
diff --git a/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/pt/start.astro b/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/pt/start.astro
index 5a4a84c2c..6f82c3790 100644
--- a/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/pt/start.astro
+++ b/packages/astro/test/fixtures/i18n-routing-prefix-always/src/pages/pt/start.astro
@@ -1,8 +1,12 @@
+---
+const currentLocale = Astro.currentLocale;
+---
<html>
<head>
<title>Astro</title>
</head>
<body>
Oi essa e start
+Current Locale: {currentLocale ? currentLocale : "none"}
</body>
</html>
diff --git a/packages/astro/test/fixtures/i18n-routing/src/pages/current-locale.astro b/packages/astro/test/fixtures/i18n-routing/src/pages/current-locale.astro
new file mode 100644
index 000000000..64af0118b
--- /dev/null
+++ b/packages/astro/test/fixtures/i18n-routing/src/pages/current-locale.astro
@@ -0,0 +1,12 @@
+---
+const currentLocale = Astro.currentLocale;
+---
+
+<html>
+<head>
+ <title>Astro</title>
+</head>
+<body>
+ Current Locale: {currentLocale ? currentLocale : "none"}
+</body>
+</html>
diff --git a/packages/astro/test/fixtures/i18n-routing/src/pages/dynamic/[id].astro b/packages/astro/test/fixtures/i18n-routing/src/pages/dynamic/[id].astro
new file mode 100644
index 000000000..58141fec0
--- /dev/null
+++ b/packages/astro/test/fixtures/i18n-routing/src/pages/dynamic/[id].astro
@@ -0,0 +1,19 @@
+
+---
+export function getStaticPaths() {
+ return [
+ { id: "lorem" }
+ ]
+}
+const currentLocale = Astro.currentLocale;
+
+---
+
+<html>
+<head>
+ <title>Astro</title>
+</head>
+<body>
+Current Locale: {currentLocale ? currentLocale : "none"}
+</body>
+</html>
diff --git a/packages/astro/test/fixtures/i18n-routing/src/pages/pt/start.astro b/packages/astro/test/fixtures/i18n-routing/src/pages/pt/start.astro
index 15a63a7b8..9a37428ca 100644
--- a/packages/astro/test/fixtures/i18n-routing/src/pages/pt/start.astro
+++ b/packages/astro/test/fixtures/i18n-routing/src/pages/pt/start.astro
@@ -1,8 +1,12 @@
+---
+const currentLocale = Astro.currentLocale;
+---
<html>
<head>
<title>Astro</title>
</head>
<body>
Hola
+Current Locale: {currentLocale ? currentLocale : "none"}
</body>
</html>
diff --git a/packages/astro/test/i18-routing.test.js b/packages/astro/test/i18n-routing.test.js
index a7e8b318d..f305a5747 100644
--- a/packages/astro/test/i18-routing.test.js
+++ b/packages/astro/test/i18n-routing.test.js
@@ -991,6 +991,73 @@ describe('[SSR] i18n routing', () => {
});
});
});
+
+ describe('current locale', () => {
+ describe('with [prefix-other-locales]', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/i18n-routing/',
+ output: 'server',
+ adapter: testAdapter(),
+ });
+ await fixture.build();
+ app = await fixture.loadTestAdapterApp();
+ });
+
+ it('should return the default locale', async () => {
+ let request = new Request('http://example.com/current-locale', {});
+ let response = await app.render(request);
+ expect(response.status).to.equal(200);
+ expect(await response.text()).includes('Current Locale: en');
+ });
+
+ 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);
+ expect(response.status).to.equal(200);
+ expect(await response.text()).includes('Current Locale: pt');
+ });
+
+ it('should return the default locale when a route is dynamic', async () => {
+ let request = new Request('http://example.com/dynamic/lorem', {});
+ let response = await app.render(request);
+ expect(response.status).to.equal(200);
+ expect(await response.text()).includes('Current Locale: en');
+ });
+ });
+
+ describe('with [prefix-always]', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/i18n-routing-prefix-always/',
+ output: 'server',
+ adapter: testAdapter(),
+ });
+ await fixture.build();
+ app = await fixture.loadTestAdapterApp();
+ });
+
+ it('should return the locale of the current URL (en)', async () => {
+ let request = new Request('http://example.com/en/start', {});
+ let response = await app.render(request);
+ expect(response.status).to.equal(200);
+ expect(await response.text()).includes('Current Locale: en');
+ });
+
+ it('should return the locale of the current URL (pt)', async () => {
+ let request = new Request('http://example.com/pt/start', {});
+ let response = await app.render(request);
+ expect(response.status).to.equal(200);
+ expect(await response.text()).includes('Current Locale: pt');
+ });
+ });
+ });
});
describe('i18n routing does not break assets and endpoints', () => {