Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into v4
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/next-intl/.size-limit.ts
#	packages/next-intl/src/navigation/react-client/createLocalizedPathnamesNavigation.tsx
#	packages/next-intl/src/navigation/react-client/createNavigation.tsx
#	packages/next-intl/src/navigation/react-client/createSharedPathnamesNavigation.tsx
#	packages/next-intl/src/server/react-client/index.test.tsx
  • Loading branch information
amannn committed Nov 28, 2024
2 parents 21b882a + 55a243a commit cb6b998
Show file tree
Hide file tree
Showing 19 changed files with 129 additions and 44 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_SCM_BASE: ${{ github.event_name == 'pull_request' && (github.event.pull_request.base.sha || github.event.before) }}
steps:
# General setup
- uses: actions/checkout@v4
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 3.25.3 (2024-11-26)

### Bug Fixes

* Follow-up for [#1573](https://github.com/amannn/next-intl/issues/1573) to also handle the case when a non-default locale is in use ([#1578](https://github.com/amannn/next-intl/issues/1578)) ([fd71741](https://github.com/amannn/next-intl/commit/fd7174179881a19e3573fceb9c6e903923644761)), closes [#1568](https://github.com/amannn/next-intl/issues/1568) – by @amannn

## 3.25.2 (2024-11-25)

### Bug Fixes

* Handle inconsistency in Next.js when using `usePathname` with custom prefixes, `localePrefix: 'as-needed'` and static rendering ([#1573](https://github.com/amannn/next-intl/issues/1573)) ([20fd0f0](https://github.com/amannn/next-intl/commit/20fd0f0015839357893bcd256ff880a98b01ea1f)) – by @amannn

## 3.25.1 (2024-11-13)

### Bug Fixes
Expand Down
5 changes: 0 additions & 5 deletions docs/src/components/Analytics.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions docs/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Analytics} from '@vercel/analytics/next';
import {SpeedInsights} from '@vercel/speed-insights/next';
import {AppProps} from 'next/app';
import {Inter} from 'next/font/google';
import {ReactNode} from 'react';
import Analytics from '@/components/Analytics';
import 'nextra-theme-docs/style.css';
import '../styles.css';

Expand All @@ -17,8 +17,8 @@ export default function App({Component, pageProps}: Props) {
return (
<div className={inter.className}>
{getLayout(<Component {...pageProps} />)}
<Analytics />
<SpeedInsights />
<Analytics />
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const routing = defineRouting({

// Lightweight wrappers around Next.js' navigation APIs
// that will consider the routing configuration
export const {Link, redirect, usePathname, useRouter} =
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
```

Expand Down
2 changes: 1 addition & 1 deletion docs/src/pages/docs/routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const routing = defineRouting({
});
```

Additionally, you should adapt your middleware matcher to detect [unprefixed pathnames](/docs/routing/middleware#matcher-no-prefix) for this routing strategy to work as expected.
**Important**: For this routing strategy to work as expected, you should additionally adapt your middleware matcher to detect [unprefixed pathnames](/docs/routing/middleware#matcher-no-prefix).

Note that if a superfluous locale prefix like `/en/about` is requested, the middleware will automatically redirect to the unprefixed version `/about`. This can be helpful in case you're redirecting from another locale and you want to update a potential cookie value first (e.g. [`<Link />`](/docs/routing/navigation#link) relies on this mechanism).

Expand Down
3 changes: 2 additions & 1 deletion docs/src/pages/docs/routing/middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ This enables:
<Details id="matcher-avoid-hardcoding">
<summary>Can I avoid hardcoding the locales in the `matcher` config?</summary>

A [Next.js `matcher`](https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher) needs to be statically analyzable, therefore you can't use variables to generate this value dynamically. However, you can implement the matcher dynamically instead:
A [Next.js `matcher`](https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher) needs to be statically analyzable, therefore you can't use variables to generate this value. However, you can alternatively implement a programmatic condition in the middleware:

```tsx filename="middleware.ts"
import {NextRequest} from 'next/server';
Expand All @@ -135,6 +135,7 @@ const handleI18nRouting = createMiddleware(routing);
export default function middleware(request: NextRequest) {
const {pathname} = request.nextUrl;

// Matches '/', as well as all paths that start with a locale like '/en'
const shouldHandle =
pathname === '/' ||
new RegExp(`^/(${locales.join('|')})(/.*)?$`).test(
Expand Down
13 changes: 7 additions & 6 deletions docs/src/pages/docs/routing/navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting(/* ... */);

export const {Link, redirect, usePathname, useRouter} =
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
```

Expand All @@ -32,10 +32,11 @@ In case you're building an app where locales can be added and removed at runtime
```tsx filename="routing.ts"
import {createNavigation} from 'next-intl/navigation';

export const {Link, redirect, usePathname, useRouter} = createNavigation({
// ... potentially other routing
// config, but no `locales` ...
});
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation({
// ... potentially other routing
// config, but no `locales` ...
});
```

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).
Expand Down Expand Up @@ -324,7 +325,7 @@ function UserProfile({userId}: {userId?: string}) {
To work around this limitation, you can add an explicit type annotation to the `redirect` function:

```tsx filename="routing.ts"
const {redirect: _redirect} = createNavigation(routing);
const {/* ..., */ redirect: _redirect} = createNavigation(routing);

// Enable type narrowing after calling `redirect`
export const redirect: typeof _redirect = _redirect;
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json",
"version": "3.25.1",
"version": "3.25.3",
"packages": [
"packages/*"
],
Expand Down
24 changes: 12 additions & 12 deletions packages/next-intl/.size-limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,50 @@ import type {SizeLimitConfig} from 'size-limit';

const config: SizeLimitConfig = [
{
name: "import * from 'next-intl' (react-client, production)",
name: "import * from 'next-intl' (react-client)",
path: 'dist/esm/production/index.react-client.js',
limit: '13.065 KB'
},
{
name: "import {NextIntlClientProvider} from 'next-intl' (react-client, production)",
name: "import {NextIntlClientProvider} from 'next-intl' (react-client)",
import: '{NextIntlClientProvider}',
path: 'dist/esm/production/index.react-client.js',
limit: '1 KB'
},
{
name: "import * from 'next-intl' (react-server, production)",
name: "import * from 'next-intl' (react-server)",
path: 'dist/esm/production/index.react-server.js',
limit: '14.005 KB'
},
{
name: "import {createNavigation} from 'next-intl/navigation' (react-client, production)",
name: "import {createNavigation} from 'next-intl/navigation' (react-client)",
path: 'dist/esm/production/navigation.react-client.js',
import: '{createNavigation}',
limit: '2.445 KB'
limit: '2.475 KB'
},
{
name: "import {createNavigation} from 'next-intl/navigation' (react-server, production)",
name: "import {createNavigation} from 'next-intl/navigation' (react-server)",
path: 'dist/esm/production/navigation.react-server.js',
import: '{createNavigation}',
limit: '3.245 KB'
limit: '3.25 KB'
},
{
name: "import * from 'next-intl/server' (react-client, production)",
name: "import * from 'next-intl/server' (react-client)",
path: 'dist/esm/production/server.react-client.js',
limit: '1 KB'
},
{
name: "import * from 'next-intl/server' (react-server, production)",
name: "import * from 'next-intl/server' (react-server)",
path: 'dist/esm/production/server.react-server.js',
limit: '13.335 KB'
},
{
name: "import * from 'next-intl/middleware' (production)",
name: "import * from 'next-intl/middleware'",
path: 'dist/esm/production/middleware.js',
limit: '9.265 KB'
limit: '9.305 KB'
},
{
name: "import * from 'next-intl/routing' (production)",
name: "import * from 'next-intl/routing'",
path: 'dist/esm/production/routing.js',
limit: '1 KB'
}
Expand Down
12 changes: 12 additions & 0 deletions packages/next-intl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 3.25.3 (2024-11-26)

### Bug Fixes

* Follow-up for [#1573](https://github.com/amannn/next-intl/issues/1573) to also handle the case when a non-default locale is in use ([#1578](https://github.com/amannn/next-intl/issues/1578)) ([fd71741](https://github.com/amannn/next-intl/commit/fd7174179881a19e3573fceb9c6e903923644761)), closes [#1568](https://github.com/amannn/next-intl/issues/1568) – by @amannn

## 3.25.2 (2024-11-25)

### Bug Fixes

* Handle inconsistency in Next.js when using `usePathname` with custom prefixes, `localePrefix: 'as-needed'` and static rendering ([#1573](https://github.com/amannn/next-intl/issues/1573)) ([20fd0f0](https://github.com/amannn/next-intl/commit/20fd0f0015839357893bcd256ff880a98b01ea1f)) – by @amannn

## 3.25.1 (2024-11-13)

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion packages/next-intl/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next-intl",
"version": "3.25.1",
"version": "3.25.3",
"sideEffects": false,
"author": "Jan Amann <[email protected]>",
"funding": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,36 @@ describe("localePrefix: 'as-needed'", () => {
});
});

describe("localePrefix: 'as-needed', custom `prefixes`", () => {
const {usePathname} = createNavigation({
defaultLocale,
locales,
localePrefix: {
mode: 'as-needed',
prefixes: {
en: '/english',
de: '/deutsch'
}
}
});
const renderPathname = getRenderPathname(usePathname);

// https://github.com/vercel/next.js/issues/73085
it('is tolerant when a locale is used in the pathname for the default locale', () => {
mockCurrentLocale('en');
mockLocation({pathname: '/en/about'});
renderPathname();
screen.getByText('/about');
});

it('is tolerant when a locale is used in the pathname for a non-default locale', () => {
mockCurrentLocale('de');
mockLocation({pathname: '/de/about'});
renderPathname();
screen.getByText('/about');
});
});

describe("localePrefix: 'as-needed', with `basePath` and `domains`", () => {
const {useRouter} = createNavigation({
locales,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function createNavigation<
function usePathname(): [AppPathnames] extends [never]
? string
: keyof AppPathnames {
const pathname = useBasePathname(config.localePrefix);
const pathname = useBasePathname(config);
const locale = useLocale();

// @ts-expect-error -- Mirror the behavior from Next.js, where `null` is returned when `usePathname` is used outside of Next, but the types indicate that a string is always returned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ function mockPathname(pathname: string) {
}

function Component() {
const pathname = useBasePathname({
// The mode is not used, only the absence of
// `prefixes` is relevant for this test suite
mode: 'as-needed'
return useBasePathname({
localePrefix: {
// The mode is not used, only the absence of
// `prefixes` is relevant for this test suite
mode: 'as-needed'
}
});
return <>{pathname}</>;
}

describe('unprefixed routing', () => {
Expand Down
28 changes: 22 additions & 6 deletions packages/next-intl/src/navigation/react-client/useBasePathname.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
Locales
} from '../../routing/types.tsx';
import {
getLocaleAsPrefix,
getLocalePrefix,
hasPathnamePrefixed,
unprefixPathname
Expand All @@ -15,7 +16,10 @@ import {
export default function useBasePathname<
AppLocales extends Locales,
AppLocalePrefixMode extends LocalePrefixMode
>(localePrefix: LocalePrefixConfigVerbose<AppLocales, AppLocalePrefixMode>) {
>(config: {
localePrefix: LocalePrefixConfigVerbose<AppLocales, AppLocalePrefixMode>;
defaultLocale?: AppLocales[number];
}) {
// The types aren't entirely correct here. Outside of Next.js
// `useParams` can be called, but the return type is `null`.

Expand All @@ -34,12 +38,24 @@ export default function useBasePathname<
return useMemo(() => {
if (!pathname) return pathname;

const prefix = getLocalePrefix(locale, localePrefix);
let unlocalizedPathname = pathname;

const prefix = getLocalePrefix(locale, config.localePrefix);
const isPathnamePrefixed = hasPathnamePrefixed(prefix, pathname);
const unlocalizedPathname = isPathnamePrefixed
? unprefixPathname(pathname, prefix)
: pathname;

if (isPathnamePrefixed) {
unlocalizedPathname = unprefixPathname(pathname, prefix);
} else if (
config.localePrefix.mode === 'as-needed' &&
config.localePrefix.prefixes
) {
// Workaround for https://github.com/vercel/next.js/issues/73085
const localeAsPrefix = getLocaleAsPrefix(locale);
if (hasPathnamePrefixed(localeAsPrefix, pathname)) {
unlocalizedPathname = unprefixPathname(pathname, localeAsPrefix);
}
}

return unlocalizedPathname;
}, [locale, localePrefix, pathname]);
}, [config.localePrefix, locale, pathname]);
}
6 changes: 5 additions & 1 deletion packages/next-intl/src/shared/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ export function getLocalePrefix<
(localePrefix.mode !== 'never' && localePrefix.prefixes?.[locale]) ||
// We return a prefix even if `mode: 'never'`. It's up to the consumer
// to decide to use it or not.
'/' + locale
getLocaleAsPrefix(locale)
);
}

export function getLocaleAsPrefix(locale: string) {
return '/' + locale;
}

export function templateToRegex(template: string): RegExp {
const regexPattern = template
// Replace optional catchall ('[[...slug]]')
Expand Down
12 changes: 12 additions & 0 deletions packages/use-intl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 3.25.3 (2024-11-26)

### Bug Fixes

* Follow-up for [#1573](https://github.com/amannn/next-intl/issues/1573) to also handle the case when a non-default locale is in use ([#1578](https://github.com/amannn/next-intl/issues/1578)) ([fd71741](https://github.com/amannn/next-intl/commit/fd7174179881a19e3573fceb9c6e903923644761)), closes [#1568](https://github.com/amannn/next-intl/issues/1568) – by @amannn

## 3.25.2 (2024-11-25)

### Bug Fixes

* Handle inconsistency in Next.js when using `usePathname` with custom prefixes, `localePrefix: 'as-needed'` and static rendering ([#1573](https://github.com/amannn/next-intl/issues/1573)) ([20fd0f0](https://github.com/amannn/next-intl/commit/20fd0f0015839357893bcd256ff880a98b01ea1f)) – by @amannn

## 3.25.1 (2024-11-13)

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion packages/use-intl/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "use-intl",
"version": "3.25.1",
"version": "3.25.3",
"sideEffects": false,
"author": "Jan Amann <[email protected]>",
"description": "Internationalization (i18n) for React",
Expand Down

0 comments on commit cb6b998

Please sign in to comment.