From 27d3e86e4c8d04101113ab7a53477f26a4fb0619 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 30 Nov 2023 05:58:37 -0500 Subject: [PATCH] refactor(i18n): breakdown routing strategy (#9236) * refactor(i18n): breakdown routing strategy * changelog * changeset * chore: fix rebase * fix changeset * chore: update test * Apply suggestions from code review Co-authored-by: Chris Swithinbank * Update packages/astro/test/i18n-routing.test.js Co-authored-by: Chris Swithinbank --------- Co-authored-by: Chris Swithinbank --- .changeset/young-trains-shout.md | 31 +++++++++++ packages/astro/src/@types/astro.ts | 52 ++++++++++++++----- packages/astro/src/core/app/index.ts | 4 +- packages/astro/src/core/app/types.ts | 2 +- packages/astro/src/core/build/generate.ts | 4 +- .../src/core/build/plugins/plugin-manifest.ts | 2 +- packages/astro/src/core/config/schema.ts | 26 ++++++++-- packages/astro/src/core/endpoint/index.ts | 2 +- packages/astro/src/core/pipeline.ts | 2 +- packages/astro/src/core/render/context.ts | 4 +- packages/astro/src/core/render/core.ts | 2 +- .../astro/src/core/routing/manifest/create.ts | 2 +- packages/astro/src/i18n/middleware.ts | 6 +-- .../src/vite-plugin-astro-server/plugin.ts | 2 +- .../src/vite-plugin-astro-server/route.ts | 4 +- .../i18n-routing-base/astro.config.mjs | 4 +- .../astro.config.mjs | 4 +- .../astro.config.mjs | 1 - packages/astro/test/i18n-routing.test.js | 12 +++-- 19 files changed, 123 insertions(+), 43 deletions(-) create mode 100644 .changeset/young-trains-shout.md diff --git a/.changeset/young-trains-shout.md b/.changeset/young-trains-shout.md new file mode 100644 index 0000000000..2a635eb1eb --- /dev/null +++ b/.changeset/young-trains-shout.md @@ -0,0 +1,31 @@ +--- +'astro': patch +--- + +The configuration `i18n.routingStrategy` has been replaced with an object called `routing`. + +```diff +export default defineConfig({ + experimental: { + i18n: { +- routingStrategy: "prefix-always", ++ routing: { ++ prefixDefaultLocale: true, ++ } + } + } +}) +``` + +```diff +export default defineConfig({ + experimental: { + i18n: { +- routingStrategy: "prefix-other-locales", ++ routing: { ++ prefixDefaultLocale: false, ++ } + } + } +}) +``` diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 2f2e9f75a1..91d8d181e6 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1519,23 +1519,43 @@ export interface AstroUserConfig { /** * @docs * @kind h4 - * @name experimental.i18n.routingStrategy - * @type {'prefix-always' | 'prefix-other-locales'} - * @default 'prefix-other-locales' - * @version 3.5.0 + * @name experimental.i18n.routing + * @type {Routing} + * @version 3.7.0 * @description * - * Controls the routing strategy to determine your site URLs. Set this based on your folder/URL path configuration for your default language: - * - * - `prefix-other-locales`(default): Only non-default languages will display a language prefix. - * The `defaultLocale` will not show a language prefix and content files do not exist in a localized folder. - * URLs will be of the form `example.com/[locale]/content/` for all non-default languages, but `example.com/content/` for the default locale. - * - `prefix-always`: All URLs will display a language prefix. - * URLs will be of the form `example.com/[locale]/content/` for every route, including the default language. - * Localized folders are used for every language, including the default. - * + * Controls the routing strategy to determine your site URLs. Set this based on your folder/URL path configuration for your default language. */ - routingStrategy?: 'prefix-always' | 'prefix-other-locales'; + routing?: { + /** + * @docs + * @name experimental.i18n.routing.prefixDefaultLocale + * @type {boolean} + * @default `false` + * @version 3.7.0 + * @description + * + * When `false`, only non-default languages will display a language prefix. + * The `defaultLocale` will not show a language prefix and content files do not exist in a localized folder. + * URLs will be of the form `example.com/[locale]/content/` for all non-default languages, but `example.com/content/` for the default locale. + * + * When `true`, all URLs will display a language prefix. + * URLs will be of the form `example.com/[locale]/content/` for every route, including the default language. + * Localized folders are used for every language, including the default. + */ + prefixDefaultLocale: boolean; + + /** + * @name experimental.i18n.routing.strategy + * @type {"pathname"} + * @default `"pathname"` + * @version 3.7.0 + * @description + * + * - `"pathanme": The strategy is applied to the pathname of the URLs + */ + strategy: 'pathname'; + }; }; /** * @docs @@ -2253,6 +2273,10 @@ export interface APIContext< currentLocale: string | undefined; } +type Routing = { + prefixDefaultLocale: boolean; + strategy: 'pathname'; +}; export type EndpointOutput = | { body: Body; diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index f069c34776..3210a2d4fa 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -243,7 +243,7 @@ export class App { env: this.#pipeline.env, mod: handler as any, locales: this.#manifest.i18n?.locales, - routingStrategy: this.#manifest.i18n?.routingStrategy, + routing: this.#manifest.i18n?.routing, defaultLocale: this.#manifest.i18n?.defaultLocale, }); } else { @@ -280,7 +280,7 @@ export class App { mod, env: this.#pipeline.env, locales: this.#manifest.i18n?.locales, - routingStrategy: this.#manifest.i18n?.routingStrategy, + routing: this.#manifest.i18n?.routing, defaultLocale: this.#manifest.i18n?.defaultLocale, }); } diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 9f9d80f445..67ed771231 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -55,7 +55,7 @@ export type SSRManifest = { export type SSRManifestI18n = { fallback?: Record; - routingStrategy?: 'prefix-always' | 'prefix-other-locales'; + routing?: 'prefix-always' | 'prefix-other-locales'; locales: string[]; defaultLocale: string; }; diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 35f8ecb667..454f29b404 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -579,7 +579,7 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli env: pipeline.getEnvironment(), mod, locales: i18n?.locales, - routingStrategy: i18n?.routingStrategy, + routing: i18n?.routing, defaultLocale: i18n?.defaultLocale, }); @@ -655,7 +655,7 @@ export function createBuildManifest( if (settings.config.experimental.i18n) { i18nManifest = { fallback: settings.config.experimental.i18n.fallback, - routingStrategy: settings.config.experimental.i18n.routingStrategy, + routing: settings.config.experimental.i18n.routing, defaultLocale: settings.config.experimental.i18n.defaultLocale, locales: settings.config.experimental.i18n.locales, }; diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index 83065ecacb..c1205a3380 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -245,7 +245,7 @@ function buildManifest( if (settings.config.experimental.i18n) { i18nManifest = { fallback: settings.config.experimental.i18n.fallback, - routingStrategy: settings.config.experimental.i18n.routingStrategy, + routing: settings.config.experimental.i18n.routing, locales: settings.config.experimental.i18n.locales, defaultLocale: settings.config.experimental.i18n.defaultLocale, }; diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index eaa11786a5..70ef7a86aa 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -65,6 +65,8 @@ const ASTRO_CONFIG_DEFAULTS = { }, } satisfies AstroUserConfig & { server: { open: boolean } }; +type RoutingStrategies = 'prefix-always' | 'prefix-other-locales'; + export const AstroConfigSchema = z.object({ root: z .string() @@ -346,11 +348,25 @@ export const AstroConfigSchema = z.object({ defaultLocale: z.string(), locales: z.string().array(), fallback: z.record(z.string(), z.string()).optional(), - // TODO: properly add default when the feature goes of experimental - routingStrategy: z - .enum(['prefix-always', 'prefix-other-locales']) - .optional() - .default('prefix-other-locales'), + routing: z + .object({ + prefixDefaultLocale: z.boolean().default(false), + strategy: z.enum(['pathname']).default('pathname'), + }) + .default({}) + .transform((routing) => { + let strategy: RoutingStrategies; + switch (routing.strategy) { + case 'pathname': { + if (routing.prefixDefaultLocale === true) { + strategy = 'prefix-always'; + } else { + strategy = 'prefix-other-locales'; + } + } + } + return strategy; + }), }) .optional() .superRefine((i18n, ctx) => { diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts index 80af2358d1..a18e01c902 100644 --- a/packages/astro/src/core/endpoint/index.ts +++ b/packages/astro/src/core/endpoint/index.ts @@ -180,7 +180,7 @@ export async function callEndpoint props: ctx.props, site: env.site, adapterName: env.adapterName, - routingStrategy: ctx.routingStrategy, + routingStrategy: ctx.routing, defaultLocale: ctx.defaultLocale, locales: ctx.locales, }); diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts index 87f833ee5c..c748bc99da 100644 --- a/packages/astro/src/core/pipeline.ts +++ b/packages/astro/src/core/pipeline.ts @@ -128,7 +128,7 @@ export class Pipeline { site: env.site, adapterName: env.adapterName, locales: renderContext.locales, - routingStrategy: renderContext.routingStrategy, + routingStrategy: renderContext.routing, defaultLocale: renderContext.defaultLocale, }); diff --git a/packages/astro/src/core/render/context.ts b/packages/astro/src/core/render/context.ts index 0f0bf39b04..efb73d7666 100644 --- a/packages/astro/src/core/render/context.ts +++ b/packages/astro/src/core/render/context.ts @@ -30,7 +30,7 @@ export interface RenderContext { locals?: object; locales: string[] | undefined; defaultLocale: string | undefined; - routingStrategy: 'prefix-always' | 'prefix-other-locales' | undefined; + routing: 'prefix-always' | 'prefix-other-locales' | undefined; } export type CreateRenderContextArgs = Partial< @@ -62,7 +62,7 @@ export async function createRenderContext( params, props, locales: options.locales, - routingStrategy: options.routingStrategy, + routing: options.routing, defaultLocale: options.defaultLocale, }; diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index 25271a94d6..270bda0625 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -61,7 +61,7 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag locals: renderContext.locals ?? {}, locales: renderContext.locales, defaultLocale: renderContext.defaultLocale, - routingStrategy: renderContext.routingStrategy, + routingStrategy: renderContext.routing, }); // TODO: Remove in Astro 4.0 diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 44482fdcbb..2fe9fd9ee0 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -532,7 +532,7 @@ export function createRouteManifest( // Work done, now we start creating "fallback" routes based on the configuration - if (i18n.routingStrategy === 'prefix-always') { + if (i18n.routing === 'prefix-always') { // we attempt to retrieve the index page of the default locale const defaultLocaleRoutes = routesByLocale.get(i18n.defaultLocale); if (defaultLocaleRoutes) { diff --git a/packages/astro/src/i18n/middleware.ts b/packages/astro/src/i18n/middleware.ts index 81523c25ac..e0269ace08 100644 --- a/packages/astro/src/i18n/middleware.ts +++ b/packages/astro/src/i18n/middleware.ts @@ -48,14 +48,14 @@ export function createI18nMiddleware( const separators = url.pathname.split('/'); const pathnameContainsDefaultLocale = url.pathname.includes(`/${defaultLocale}`); const isLocaleFree = checkIsLocaleFree(url.pathname, i18n.locales); - if (i18n.routingStrategy === 'prefix-other-locales' && pathnameContainsDefaultLocale) { + if (i18n.routing === 'prefix-other-locales' && pathnameContainsDefaultLocale) { const newLocation = url.pathname.replace(`/${defaultLocale}`, ''); response.headers.set('Location', newLocation); return new Response(null, { status: 404, headers: response.headers, }); - } else if (i18n.routingStrategy === 'prefix-always') { + } else if (i18n.routing === 'prefix-always') { if (url.pathname === base + '/' || url.pathname === base) { if (trailingSlash === 'always') { return context.redirect(`${appendForwardSlash(joinPaths(base, i18n.defaultLocale))}`); @@ -82,7 +82,7 @@ export function createI18nMiddleware( let newPathname: string; // If a locale falls back to the default locale, we want to **remove** the locale because // the default locale doesn't have a prefix, but _only_ if prefix-always is false - if (fallbackLocale === defaultLocale && i18n.routingStrategy !== 'prefix-always') { + if (fallbackLocale === defaultLocale && i18n.routing !== 'prefix-always') { newPathname = url.pathname.replace(`/${urlLocale}`, ``); } else { newPathname = url.pathname.replace(`/${urlLocale}`, `/${fallbackLocale}`); diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index b8f4ab661a..d324dfdc6e 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -90,7 +90,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest if (settings.config.experimental.i18n) { i18nManifest = { fallback: settings.config.experimental.i18n.fallback, - routingStrategy: settings.config.experimental.i18n.routingStrategy, + routing: settings.config.experimental.i18n.routing, defaultLocale: settings.config.experimental.i18n.defaultLocale, locales: settings.config.experimental.i18n.locales, }; diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index e7f8fd1e4c..bf70c1baf2 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -217,7 +217,7 @@ export async function handleRoute({ mod, route, locales: manifest.i18n?.locales, - routingStrategy: manifest.i18n?.routingStrategy, + routing: manifest.i18n?.routing, defaultLocale: manifest.i18n?.defaultLocale, }); } else { @@ -276,7 +276,7 @@ export async function handleRoute({ mod, env, locales: i18n?.locales, - routingStrategy: i18n?.routingStrategy, + routing: i18n?.routing, defaultLocale: i18n?.defaultLocale, }); } diff --git a/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs b/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs index d20245efb0..1aae961c7c 100644 --- a/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs +++ b/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs @@ -8,7 +8,9 @@ export default defineConfig({ locales: [ 'en', 'pt', 'it' ], - routingStrategy: "prefix-always" + routing: { + prefixDefaultLocale: true + } } } }) diff --git a/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs b/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs index f2aac5899c..6152d10393 100644 --- a/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs +++ b/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs @@ -7,7 +7,9 @@ export default defineConfig({ locales: [ 'en', 'pt', 'it' ], - routingStrategy: "prefix-always" + routing: { + prefixDefaultLocale: true + } } }, base: "/new-site" diff --git a/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs b/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs index 3b8911f6d4..4eb0abf1bf 100644 --- a/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs +++ b/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs @@ -7,7 +7,6 @@ export default defineConfig({ locales: [ 'en', 'pt', 'it' ], - routingStrategy: "prefix-other-locales" }, }, diff --git a/packages/astro/test/i18n-routing.test.js b/packages/astro/test/i18n-routing.test.js index 7fd5946e6c..53f82d16fd 100644 --- a/packages/astro/test/i18n-routing.test.js +++ b/packages/astro/test/i18n-routing.test.js @@ -291,7 +291,9 @@ describe('[DEV] i18n routing', () => { fallback: { it: 'en', }, - routingStrategy: 'prefix-other-locales', + routing: { + prefixDefaultLocale: false, + }, }, }, }); @@ -661,7 +663,9 @@ describe('[SSG] i18n routing', () => { fallback: { it: 'en', }, - routingStrategy: 'prefix-always', + routing: { + prefixDefaultLocale: true, + }, }, }, }); @@ -975,7 +979,9 @@ describe('[SSR] i18n routing', () => { fallback: { it: 'en', }, - routingStrategy: 'prefix-always', + routing: { + prefixDefaultLocale: true, + }, }, }, });