diff --git a/docs/pages/docs/getting-started/app-router/with-i18n-routing.mdx b/docs/pages/docs/getting-started/app-router/with-i18n-routing.mdx index cfc8ba3cb..633ea2bea 100644 --- a/docs/pages/docs/getting-started/app-router/with-i18n-routing.mdx +++ b/docs/pages/docs/getting-started/app-router/with-i18n-routing.mdx @@ -107,7 +107,7 @@ To share the configuration between these two places, we'll set up `routing.ts`: ```ts filename="src/i18n/routing.ts" import {defineRouting} from 'next-intl/routing'; -import {createSharedPathnamesNavigation} from 'next-intl/navigation'; +import {createNavigation} from 'next-intl/navigation'; export const routing = defineRouting({ // A list of all locales that are supported @@ -120,7 +120,7 @@ export const routing = defineRouting({ // Lightweight wrappers around Next.js' navigation APIs // that will consider the routing configuration export const {Link, redirect, usePathname, useRouter} = - createSharedPathnamesNavigation(routing); + createNavigation(routing); ``` Depending on your requirements, you may wish to customize your routing configuration later—but let's finish with the setup first. diff --git a/docs/pages/docs/routing.mdx b/docs/pages/docs/routing.mdx index 4288001dc..5fa0e90d2 100644 --- a/docs/pages/docs/routing.mdx +++ b/docs/pages/docs/routing.mdx @@ -41,7 +41,7 @@ Depending on your routing needs, you may wish to consider further settings. In case you're building an app where locales can be added and removed at runtime, you can provide the routing configuration for the middleware [dynamically per request](/docs/routing/middleware#composing-other-middlewares). -To create the corresponding navigation APIs, you can [omit the `locales` argument](/docs/routing/navigation#locales-unknown) from `createSharedPathnamesNavigation` in this case. +To create the corresponding navigation APIs, you can [omit the `locales` argument](/docs/routing/navigation#locales-unknown) from `createNavigation` in this case. @@ -84,10 +84,7 @@ export const routing = defineRouting({ In this case, requests where the locale prefix matches the default locale will be redirected (e.g. `/en/about` to `/about`). This will affect both prefix-based as well as domain-based routing. -**Note that:** - -1. If you use this strategy, you should make sure that your middleware matcher detects [unprefixed pathnames](/docs/routing/middleware#matcher-no-prefix). -2. If you use [the `Link` component](/docs/routing/navigation#link), the initial render will point to the prefixed version but will be patched immediately on the client once the component detects that the default locale has rendered. The prefixed version is still valid, but SEO tools might report a hint that the link points to a redirect. +**Note that:** If you use this strategy, you should make sure that your middleware matcher detects [unprefixed pathnames](/docs/routing/middleware#matcher-no-prefix) for the routing to work as expected. #### Never use a locale prefix [#locale-prefix-never] @@ -95,8 +92,8 @@ If you'd like to provide a locale to `next-intl`, e.g. based on user settings, y However, you can also configure the middleware to never show a locale prefix in the URL, which can be helpful in the following cases: -1. You're using [domain-based routing](#domains) and you support only a single locale per domain -2. You're using a cookie to determine the locale but would like to enable static rendering +1. You want to use [domain-based routing](#domains) and have only one locale per domain +2. You want to use a cookie to determine the locale while enabling static rendering ```tsx filename="routing.ts" {5} import {defineRouting} from 'next-intl/routing'; @@ -153,8 +150,8 @@ function Component() { // Assuming the locale is 'en-US' const locale = useLocale(); - // Returns 'US' - new Intl.Locale(locale).region; + // Extracts the "US" region + const {region} = new Intl.Locale(locale); } ``` @@ -222,13 +219,6 @@ export const routing = defineRouting({ Localized pathnames map to a single internal pathname that is created via the file-system based routing in Next.js. In the example above, `/de/ueber-uns` will be handled by the page at `/[locale]/about/page.tsx`. - - If you're using localized pathnames, you should use - `createLocalizedPathnamesNavigation` instead of - `createSharedPathnamesNavigation` for your [navigation - APIs](/docs/routing/navigation). - -
How can I revalidate localized pathnames? @@ -403,3 +393,72 @@ PORT=3001 npm run dev ```
+ +
+Can I use a different `localePrefix` setting per domain? + +Since such a configuration would require reading the domain at runtime, this would prevent the ability to render pages statically. Due to this, `next-intl` doesn't support this configuration out of the box. + +However, you can still achieve this by building the app for each domain separately, while injecting diverging routing configuration via an environment variable. + +**Example:** + +```tsx filename="routing.ts" +import {defineRouting} from 'next-intl/routing'; + +export const routing = defineRouting({ + locales: ['en', 'fr'], + defaultLocale: 'en', + localePrefix: + process.env.VERCEL_PROJECT_PRODUCTION_URL === 'us.example.com' + ? 'never' + : 'always', + domains: [ + { + domain: 'us.example.com', + defaultLocale: 'en', + locales: ['en'] + }, + { + domain: 'ca.example.com', + defaultLocale: 'en' + } + ] +}); +``` + +
+ +
+Special case: Using `domains` with `localePrefix: 'as-needed'` + +Since domains can have different default locales, this combination requires some tradeoffs that apply to the [navigation APIs](/docs/routing/navigation) in order for `next-intl` to avoid reading the current host on the server side (which would prevent the usage of static rendering). + +1. [``](/docs/routing/navigation#link): This component will always render a locale prefix on the server side, even for the default locale of a given domain. However, during hydration on the client side, the prefix is potentially removed, if the default locale of the current domain is used. Note that the temporarily prefixed pathname will always be valid, however the middleware will potentially clean up a superfluous prefix via a redirect if the user clicks on a link before hydration. +2. [`redirect`](/docs/routing/navigation#redirect): When calling this function, a locale prefix is always added, regardless of the provided locale. However, similar to the handling with ``, the middleware will potentially clean up a superfluous prefix. +3. [`getPathname`](/docs/routing/navigation#getpathname): This function requires that a `domain` is passed as part of the arguments in order to avoid ambiguity. This can either be provided statically (e.g. when used in a sitemap), or read from a header like [`x-forwarded-host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host). + +```tsx +import {getPathname} from '@/i18n/routing'; +import {headers} from 'next/headers'; + +// Case 1: Statically known domain +const domain = 'ca.example.com'; + +// Case 2: Read at runtime (dynamic rendering) +const domain = headers().get('x-forwarded-host'); + +// Assuming the current domain is `ca.example.com`, +// the returned pathname will be `/about` +const pathname = getPathname({ + href: '/about', + locale: 'en', + domain +}); +``` + +A `domain` can optionally also be passed to `redirect` in the same manner to ensure that a prefix is only added when necessary. Alternatively, you can also consider redirecting in the middleware or via [`useRouter`](/docs/routing/navigation#usrouter) on the client side. + +If you need to avoid these tradeoffs, you can consider building the same app for each domain separately, while injecting diverging routing configuration via an [environment variable](#domains-localeprefix-individual). + +
diff --git a/docs/pages/docs/routing/middleware.mdx b/docs/pages/docs/routing/middleware.mdx index 30c5d27cd..f59072fba 100644 --- a/docs/pages/docs/routing/middleware.mdx +++ b/docs/pages/docs/routing/middleware.mdx @@ -14,6 +14,8 @@ The middleware receives a [`routing`](/docs/routing#define-routing) configuratio 2. Applying relevant redirects & rewrites 3. Providing [alternate links](#alternate-links) for search engines +**Example:** + ```tsx filename="middleware.ts" import createMiddleware from 'next-intl/middleware'; import {routing} from './i18n/routing'; diff --git a/docs/pages/docs/routing/navigation.mdx b/docs/pages/docs/routing/navigation.mdx index 34e7f722a..ca2a84758 100644 --- a/docs/pages/docs/routing/navigation.mdx +++ b/docs/pages/docs/routing/navigation.mdx @@ -11,88 +11,54 @@ import Details from 'components/Details'; `next-intl` provides lightweight wrappers around Next.js' navigation APIs like [``](https://nextjs.org/docs/app/api-reference/components/link) and [`useRouter`](https://nextjs.org/docs/app/api-reference/functions/use-router) that automatically handle the user locale and pathnames behind the scenes. -Depending on if you're using the [`pathnames`](/docs/routing#pathnames) setting, you can pick from one of these functions to create the corresponding navigation APIs: - -- `createSharedPathnamesNavigation`: Pathnames are shared across all locales (default) -- `createLocalizedPathnamesNavigation`: Pathnames are provided per locale (use with `pathnames`) - -These functions are typically called in a central module like [`src/i18n/routing.ts`](/docs/getting-started/app-router/with-i18n-routing#i18n-routing) in order to provide easy access to navigation APIs in your components and should receive a [`routing`](/docs/routing) configuration that is shared with the middleware. - - - +To create these APIs, you can call the `createNavigation` function with your `routing` configuration: ```tsx filename="routing.ts" -import {createSharedPathnamesNavigation} from 'next-intl/navigation'; +import {createNavigation} from 'next-intl/navigation'; import {defineRouting} from 'next-intl/routing'; -const routing = defineRouting(/* ... */); +export const routing = defineRouting(/* ... */); export const {Link, redirect, usePathname, useRouter} = - createSharedPathnamesNavigation(routing); + createNavigation(routing); ``` +This function is typically called in a central module like [`src/i18n/routing.ts`](/docs/getting-started/app-router/with-i18n-routing#i18n-routing) in order to provide easy access to navigation APIs in your components. +
What if the locales aren't known at build time? -In case you're building an app where locales can be added and removed at runtime, `createSharedPathnamesNavigation` can be called without the `locales` argument, therefore allowing any string that is encountered at runtime to be a valid locale. - -In this case, you'd not use the `defineRouting` function. +In case you're building an app where locales can be added and removed at runtime, `createNavigation` can be called without the `locales` argument, therefore allowing any string that is encountered at runtime to be a valid locale. In this case, you'd not use the [`defineRouting`](/docs/routing#define-routing) function. ```tsx filename="routing.ts" -import {createSharedPathnamesNavigation} from 'next-intl/navigation'; +import {createNavigation} from 'next-intl/navigation'; -export const {Link, redirect, usePathname, useRouter} = - createSharedPathnamesNavigation({ - // ... potentially other routing - // config, but no `locales` ... - }); +export const {Link, redirect, usePathname, useRouter} = createNavigation({ + // ... potentially other routing + // config, but no `locales` ... +}); ``` -Note however that the `locales` argument for the middleware is mandatory. However, you can provide the routing configuration for the middleware [dynamically per request](/docs/routing/middleware#composing-other-middlewares). +Note however that the `locales` argument for the middleware is still mandatory. If you need to fetch the available locales at runtime, you can provide the routing configuration for the middleware [dynamically per request](/docs/routing/middleware#composing-other-middlewares).
-
- - -```tsx filename="routing.ts" -import {createLocalizedPathnamesNavigation} from 'next-intl/navigation'; -import {defineRouting} from 'next-intl/routing'; - -const routing = defineRouting({ - // ... - pathnames: { - // ... - } -}); - -export const {Link, redirect, usePathname, useRouter, getPathname} = - createLocalizedPathnamesNavigation(routing); -``` +## APIs - - Have a look at the [App Router example](/examples#app-router) to explore a - working implementation of localized pathnames. - +The created navigation APIs are thin wrappers around the equivalents from Next.js and mostly adhere to the same function signatures. Your routing configuration and the user's locale are automatically incorporated. - -
+If you're using the [`pathnames`](/docs/routing#pathnames) setting in your routing configuration, the internal pathnames that are accepted for `href` arguments will be strictly typed and localized to the given locale.
How can I ensure consistent usage of navigation APIs? -To ensure consistent usage in your app, you can consider [linting for usage of these APIs](/docs/workflows/linting#consistent-usage-of-navigation-apis). +To avoid importing APIs like `` directly from Next.js by accident, you can consider [linting](/docs/workflows/linting#consistent-usage-of-navigation-apis) for the consistent usage of internationalized navigation APIs.
-## APIs - ### `Link` -This component wraps [`next/link`](https://nextjs.org/docs/app/api-reference/components/link) and automatically incorporates your routing strategy. - - - +This component wraps [`next/link`](https://nextjs.org/docs/app/api-reference/components/link) and localizes the pathname as necessary. ```tsx import {Link} from '@/i18n/routing'; @@ -100,14 +66,28 @@ import {Link} from '@/i18n/routing'; // When the user is on `/en`, the link will point to `/en/about` About +// Search params can be added via `query` +Users + // You can override the `locale` to switch to another language +// (this will set the `hreflang` attribute on the anchor tag) Switch to German +``` + +Depending on if you're using the [`pathnames`](/docs/routing#pathnames) setting, dynamic params can either be passed as: -// Dynamic params need to be interpolated into the pathname +```tsx +// 1. A final string (when not using `pathnames`) Susan -``` -If you're providing the `locale` prop, the `hreflang` attribute will be set accordingly on the anchor tag. +// 2. An object (when using `pathnames`) + + Susan + +``` - - -When using [localized pathnames](/docs/routing#pathnames), the `href` prop corresponds to an internal pathname, but will be mapped to a locale-specific pathname. - -```tsx -import {Link} from '@/i18n/routing'; - -// When the user is on `/de`, the link will point to `/de/ueber-uns` -About - -// You can override the `locale` to switch to another language -Switch to English - -// Dynamic params can be passed via the object form - - Susan - - -// Catch-all params can be passed as arrays - - T-Shirts - - -// Search params can be added via `query` -Users -``` - - - - - -