Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Export DomainsConfig #1175

Merged
merged 2 commits into from
Jul 8, 2024
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
4 changes: 3 additions & 1 deletion docs/pages/docs/routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ If you want to serve your localized content based on different domains, you can
- `ca.example.com/fr`

```tsx filename="config.ts"
import {DomainsConfig} from 'next-intl/routing';

export const locales = ['en', 'fr'] as const;

export const domains: DomainsConfig<typeof locales> = [
Expand All @@ -342,4 +344,4 @@ export const domains: DomainsConfig<typeof locales> = [
**Note that:**

1. You can optionally remove the locale prefix in pathnames by changing the [`localePrefix`](#locale-prefix) setting.
2. If no domain matches, the middleware will fall back to the [`defaultLocale`](/docs/routing/middleware#default-locale) (e.g. on `localhost`).
2. If no domain matches, the middleware will fall back to the [`defaultLocale`](/docs/routing/middleware#default-locale) (e.g. on `localhost`).
8 changes: 4 additions & 4 deletions packages/next-intl/src/middleware/middleware.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function createMiddleware<
? domain.defaultLocale === locale
: locale === config.defaultLocale;

const domainConfigs =
const domainsConfig =
config.domains?.filter((curDomain) =>
isLocaleSupportedOnDomain(locale, curDomain)
) || [];
Expand All @@ -64,11 +64,11 @@ export default function createMiddleware<
function redirect(url: string, redirectDomain?: string) {
const urlObj = new URL(normalizeTrailingSlash(url), request.url);

if (domainConfigs.length > 0 && !redirectDomain) {
if (domainsConfig.length > 0 && !redirectDomain) {
const bestMatchingDomain = getBestMatchingDomain(
domain,
locale,
domainConfigs
domainsConfig
);
if (bestMatchingDomain) {
redirectDomain = bestMatchingDomain.domain;
Expand Down Expand Up @@ -237,7 +237,7 @@ export default function createMiddleware<
const pathDomain = getBestMatchingDomain(
domain,
pathnameMatch.locale,
domainConfigs
domainsConfig
);

if (domain?.domain !== pathDomain?.domain && !hasUnknownHost) {
Expand Down
9 changes: 7 additions & 2 deletions packages/next-intl/src/middleware/resolveLocale.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import {match} from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';
import {RequestCookies} from 'next/dist/server/web/spec-extension/cookies';
import {Locales, DomainConfig, Pathnames} from '../routing/types';
import {
Locales,
Pathnames,
DomainsConfig,
DomainConfig
} from '../routing/types';
import {COOKIE_LOCALE_NAME} from '../shared/constants';
import {MiddlewareRoutingConfig} from './config';
import {getHost, getPathnameMatch, isLocaleSupportedOnDomain} from './utils';

function findDomainFromHost<AppLocales extends Locales>(
requestHeaders: Headers,
domains: Array<DomainConfig<AppLocales>>
domains: DomainsConfig<AppLocales>
) {
let host = getHost(requestHeaders);

Expand Down
11 changes: 6 additions & 5 deletions packages/next-intl/src/middleware/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
Locales,
LocalePrefixConfigVerbose,
DomainConfig,
Pathnames
Pathnames,
DomainsConfig
} from '../routing/types';
import {
getLocalePrefix,
Expand Down Expand Up @@ -238,7 +239,7 @@ export function isLocaleSupportedOnDomain<AppLocales extends Locales>(
export function getBestMatchingDomain<AppLocales extends Locales>(
curHostDomain: DomainConfig<AppLocales> | undefined,
locale: string,
domainConfigs: Array<DomainConfig<AppLocales>>
domainsConfig: DomainsConfig<AppLocales>
) {
let domainConfig;

Expand All @@ -249,12 +250,12 @@ export function getBestMatchingDomain<AppLocales extends Locales>(

// Prio 2: Use alternative domain with matching default locale
if (!domainConfig) {
domainConfig = domainConfigs.find((cur) => cur.defaultLocale === locale);
domainConfig = domainsConfig.find((cur) => cur.defaultLocale === locale);
}

// Prio 3: Use alternative domain with restricted matching locale
if (!domainConfig) {
domainConfig = domainConfigs.find(
domainConfig = domainsConfig.find(
(cur) => cur.locales != null && cur.locales.includes(locale)
);
}
Expand All @@ -266,7 +267,7 @@ export function getBestMatchingDomain<AppLocales extends Locales>(

// Prio 5: Use alternative domain that supports all locales
if (!domainConfig) {
domainConfig = domainConfigs.find((cur) => !cur.locales);
domainConfig = domainsConfig.find((cur) => !cur.locales);
}

return domainConfig;
Expand Down
6 changes: 3 additions & 3 deletions packages/next-intl/src/routing/config.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
Locales,
DomainConfig,
LocalePrefix,
LocalePrefixConfigVerbose
LocalePrefixConfigVerbose,
DomainsConfig
} from './types';

/**
Expand All @@ -15,7 +15,7 @@ export type RoutingBaseConfigInput<AppLocales extends Locales> = {
/** @see https://next-intl-docs.vercel.app/docs/routing#locale-prefix */
localePrefix?: LocalePrefix<AppLocales>;
/** Can be used to change the locale handling per domain. */
domains?: Array<DomainConfig<AppLocales>>;
domains?: DomainsConfig<AppLocales>;
};

export function receiveLocalePrefixConfig<AppLocales extends Locales>(
Expand Down
3 changes: 1 addition & 2 deletions packages/next-intl/src/routing/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export type {LocalePrefix} from './types';
export type {Pathnames} from './types';
export type {Pathnames, LocalePrefix, DomainsConfig} from './types';
6 changes: 5 additions & 1 deletion packages/next-intl/src/routing/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ export type DomainConfig<AppLocales extends Locales> = {
domain: string;

/** Optionally restrict which locales are available on this domain. */
locales?: AppLocales;
locales?: Array<AppLocales[number]>;
};

export type DomainsConfig<AppLocales extends Locales> = Array<
DomainConfig<AppLocales>
>;
95 changes: 62 additions & 33 deletions packages/next-intl/test/routing/types.test.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,69 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {it} from 'vitest';
import {LocalePrefix} from '../../src/routing/types';
import {describe, it} from 'vitest';
import {LocalePrefix, DomainConfig} from '../../src/routing/types';

it('does not require a type param for simple values', () => {
const config: LocalePrefix = 'always';
});
describe('LocalePrefix', () => {
it('does not require a type param for simple values', () => {
const config: LocalePrefix = 'always';
});

it('provides strict typing for locales', () => {
const locales = ['en', 'de'] as const;
const config: LocalePrefix<typeof locales> = {
mode: 'always',
prefixes: {
en: '/en',
// @ts-expect-error
unknown: '/unknown'
}
};
});
it('provides strict typing for locales', () => {
const locales = ['en', 'de'] as const;
const config: LocalePrefix<typeof locales> = {
mode: 'always',
prefixes: {
en: '/en',
// @ts-expect-error
unknown: '/unknown'
}
};
});

it('allows partial config', () => {
const locales = ['en', 'de'] as const;
const config: LocalePrefix<typeof locales> = {
mode: 'always',
prefixes: {
en: '/en'
}
};
});

it('allows partial config', () => {
const locales = ['en', 'de'] as const;
const config: LocalePrefix<typeof locales> = {
mode: 'always',
prefixes: {
en: '/en'
}
};
it('provides optional typing for locales in prefixes', () => {
const config: LocalePrefix = {
mode: 'always',
prefixes: {
de: '/de',
en: '/en',
unknown: '/unknown'
}
};
});
});

it('provides optional typing for locales in prefixes', () => {
const config: LocalePrefix = {
mode: 'always',
prefixes: {
de: '/de',
en: '/en',
unknown: '/unknown'
}
};
describe('DomainConfig', () => {
it('allows to handle all locales', () => {
const config: DomainConfig<['en', 'de']> = {
defaultLocale: 'en',
domain: 'example.com'
};
});

it('allows to restrict locales', () => {
const config: DomainConfig<['en', 'de']> = {
defaultLocale: 'en',
domain: 'example.com',
locales: ['en']
};
});

it('errors for unknown locales', () => {
const config: DomainConfig<['en', 'de']> = {
// @ts-expect-error
defaultLocale: 'unknown',
domain: 'example.com',
// @ts-expect-error
locales: ['unknown']
};
});
});
Loading