Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/astro/src/core/errors/errors-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,18 @@ export const MissingMiddlewareForInternationalization = {
"Your configuration setting `i18n.routing: 'manual'` requires you to provide your own i18n `middleware` file.",
} satisfies ErrorData;

/**
* @docs
* @description
* An invalid i18n middleware configuration was detected.
*/
export const InvalidI18nMiddlewareConfiguration = {
name: 'InvalidI18nMiddlewareConfiguration',
title: 'Invalid internationalization middleware configuration',
message:
'The option `redirectToDefaultLocale` can be enabled only when `prefixDefaultLocale` is also set to `true`, otherwise redirects might cause infinite loops. Enable the option `prefixDefaultLocale` to continue to use `redirectToDefaultLocale`, or ensure both are set to `false`.',
} satisfies ErrorData;

/**
* @docs
* @description
Expand Down
30 changes: 26 additions & 4 deletions packages/astro/src/virtual-modules/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import * as config from 'astro:config/server';
import { toFallbackType } from '../core/app/common.js';
import { toRoutingStrategy } from '../core/app/index.js';
import type { SSRManifest } from '../core/app/types.js';
import { IncorrectStrategyForI18n } from '../core/errors/errors-data.js';
import {
IncorrectStrategyForI18n,
InvalidI18nMiddlewareConfiguration,
} from '../core/errors/errors-data.js';
import { AstroError } from '../core/errors/index.js';
import type { RedirectToFallback } from '../i18n/index.js';
import * as I18nInternals from '../i18n/index.js';
Expand Down Expand Up @@ -339,7 +342,19 @@ if (i18n?.routing === 'manual') {
}

type OnlyObject<T> = T extends object ? T : never;
type NewAstroRoutingConfigWithoutManual = OnlyObject<NonNullable<AstroConfig['i18n']>['routing']>;

export type I18nMiddlewareOptions = {
fallbackType: OnlyObject<NonNullable<AstroConfig['i18n']>['routing']>['fallbackType'];
} & (
| {
prefixDefaultLocale: false;
redirectToDefaultLocale: false;
}
| {
prefixDefaultLocale: true;
redirectToDefaultLocale: boolean;
}
);

/**
* @param {AstroConfig['i18n']['routing']} customOptions
Expand Down Expand Up @@ -371,10 +386,17 @@ type NewAstroRoutingConfigWithoutManual = OnlyObject<NonNullable<AstroConfig['i1
*
* ```
*/
export let middleware: (customOptions: NewAstroRoutingConfigWithoutManual) => MiddlewareHandler;
export let middleware: (customOptions: I18nMiddlewareOptions) => MiddlewareHandler;

if (i18n?.routing === 'manual') {
middleware = (customOptions: NewAstroRoutingConfigWithoutManual) => {
middleware = (customOptions) => {
if (
customOptions.prefixDefaultLocale === false &&
// @ts-expect-error types do not allow this but we also check at runtime
customOptions.redirectToDefaultLocale === true
) {
throw new AstroError(InvalidI18nMiddlewareConfiguration);
}
strategy = toRoutingStrategy(customOptions, {});
fallbackType = toFallbackType(customOptions);
const manifest: SSRManifest['i18n'] = {
Expand Down
29 changes: 29 additions & 0 deletions packages/astro/test/types/astro-i18n-virtual-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, it } from 'node:test';
import '../../client.d.ts';
import type { I18nMiddlewareOptions } from 'astro:i18n';

describe('astro:i18n', () => {
it('middleware', () => {
({
fallbackType: 'rewrite',
prefixDefaultLocale: false,
redirectToDefaultLocale: false,
}) satisfies I18nMiddlewareOptions;
({
fallbackType: 'rewrite',
prefixDefaultLocale: false,
redirectToDefaultLocale: true,
// @ts-expect-error invalid combination
}) satisfies I18nMiddlewareOptions;
({
fallbackType: 'rewrite',
prefixDefaultLocale: true,
redirectToDefaultLocale: false,
}) satisfies I18nMiddlewareOptions;
({
fallbackType: 'rewrite',
prefixDefaultLocale: true,
redirectToDefaultLocale: true,
}) satisfies I18nMiddlewareOptions;
});
});
Loading