Skip to content

Conversation

@amannn
Copy link
Owner

@amannn amannn commented Nov 13, 2025

Prerelease:

next-intl@0.0.0-canary-8f1d542

#2091 was a first step, but more work was necessary to really fix this.

Use message ID as a tie-breaker in getSortedMessages in case reference paths match for a given message.

The previous tie-breaker, a.message.localeCompare(b.message), was ineffective for untranslated messages (where message is an empty string), causing getSortedMessages to return 0 and rely on non-deterministic insertion order from parallel file extraction. Another issue is that message can be different across locales, therefore the order could vary across catalogs.

This fix ensures consistent ordering for messages, particularly those used in multiple files, by using a stable message.id for tie-breaking. This now also ensures that the order is the same across all catalogs.

# Conflicts:
#	packages/next-intl/.size-limit.ts
#	packages/next-intl/__mocks__/react.tsx
#	packages/next-intl/src/navigation/shared/createSharedNavigationFns.tsx
#	packages/next-intl/src/react-server/index.test.tsx
#	packages/next-intl/src/server/react-server/RequestLocale.tsx
#	packages/next-intl/src/server/react-server/getConfig.tsx
In Next.js 15.3, [Turbopack config has become
stable](https://nextjs.org/blog/next-15-3#turbopack-configuration-in-nextconfigts-stable).
With this fix, the new option is used in order to avoid a deprecation
warning.
# Conflicts:
#	packages/next-intl/src/plugin/getNextConfig.tsx
…on APIs (#1922)

With #959, the middleware
already handled decoding of non-ASCII characters.

This allows you to define localized
[`pathnames`](https://next-intl.dev/docs/routing#pathnames) like so:

```tsx
import {defineRouting} from 'next-intl/routing';
 
export const routing = defineRouting({
  locales: ['en', 'ja'],
  defaultLocale: 'en',
  pathnames: {
    '/about': {
      'de': '/über-uns'
  }
}
```

Since Next.js automatically encodes incoming pathnames, this supports
incoming requests both for decoded pathnames (e.g. `/de/über-uns`), as
well as encoded ones (e.g. `/de/%C3%BCber-uns`).

One piece has been missing though: Pathnames returned from [navigation
APIs](https://next-intl.dev/docs/routing/navigation) should be turned
into an encoded form.

Now, `next-intl` handles this as well:

```tsx
import {Link, getPathname} from '@/i18n/navigation';

// href="/de/%C3%BCber-uns"
<Link href="/about" locale="de" />

// pathname = "/de/%C3%BCber-uns"
const pathname = getPathname({href: '/about', locale: 'de'});
```

This change brings the navigation APIs in line with [Google's
recommendation to encode non-ASCII
pathnames](https://developers.google.com/search/docs/crawling-indexing/url-structure).
amannn and others added 20 commits November 5, 2025 18:05
# Conflicts:
#	docs/src/pages/blog/use-extracted.mdx
#	docs/src/pages/docs/usage/extraction.mdx
Sorts message references alphabetically to ensure deterministic PO file
output.

Previously, when multiple files referenced the same message, the order
of these references in the PO file was nondeterministic. This caused PO
files to change unnecessarily between builds, even when no actual
translations or code changed, leading to "noisy" diffs for users. This
fix ensures that shared message references are always sorted
alphabetically, providing stable and predictable PO file generation.

---
<a
href="https://cursor.com/background-agent?bcId=bc-92413a35-439b-4252-baea-8217fe88d4dc"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/open-in-cursor-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/open-in-cursor-light.svg"><img alt="Open in
Cursor"
src="https://cursor.com/open-in-cursor.svg"></picture></a>&nbsp;<a
href="https://cursor.com/agents?id=bc-92413a35-439b-4252-baea-8217fe88d4dc"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/open-in-web-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/open-in-web-light.svg"><img alt="Open in Web"
src="https://cursor.com/open-in-web.svg"></picture></a>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…xplicitly requested (#2093)

Ignore `node_modules`, `.next` and `.git` by default, allowing consumers
to use `srcPath: './'` in case they're not using a `src` directory. In a
future iteration, we might infer these directories from `.gitignore`.

Note that you can still use:

```
srcPath: ['./src', './node_modules/@acme/components'],
```

… in case you want to extract from source files inside of such a
directory.

This PR also contains a fix that allows using trailing slashes in
`srcPath` (e.g. `./src/app/`).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Use message ID as a tie-breaker in `getSortedMessages` in case reference
paths match for a given message.

The previous tie-breaker, `a.message.localeCompare(b.message)`, was
ineffective for untranslated messages (where `message` is an empty
string), causing `getSortedMessages` to return `0` and rely on
non-deterministic insertion order from parallel file extraction. Another
issue is that `message` can be different across locales, therefore the
order could vary across catalogs.

This fix ensures consistent ordering for messages, particularly those
used in multiple files, by using a stable `message.id` for tie-breaking.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@vercel
Copy link

vercel bot commented Nov 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
next-intl-docs Ready Ready Preview Comment Nov 13, 2025 10:00am
next-intl-example-app-router Ready Ready Preview Comment Nov 13, 2025 10:00am
next-intl-example-app-router-without-i18n-routing Ready Ready Preview Comment Nov 13, 2025 10:00am

@amannn amannn changed the title fix(useExtracted): Fix inconsistent translation message ordering fix: Fix inconsistent ordering of messages for useExtracted Nov 13, 2025
@amannn
Copy link
Owner Author

amannn commented Nov 13, 2025

@ceolinwill and @johanneskares: You two reported that ordering of messages wasn't deterministic yet, thanks again for the helpful reports and supplemental context! 🙏

I found a bug and I think now everything should work as expected. Can you try next-intl@0.0.0-canary-8f1d542 in your apps and report back here if everything works fine now?

Note that you might see an initial change after installing this version, but all subsequent extractions should match the previous one now. If this looks good to you, I'll release this to stable.

@ceolinwill
Copy link
Contributor

@amannn Thanks for looking into this. It's working as expected now.

@amannn
Copy link
Owner Author

amannn commented Nov 13, 2025

@ceolinwill Thanks, that's great to hear! @johanneskares gave me some details about a case where there is apparently still a bug happening. I'll see if I can reproduce this before merging this fix.

@amannn
Copy link
Owner Author

amannn commented Nov 13, 2025

Ok, false alarm—also @johanneskares is getting deterministic results now. Will publish to stable, thanks everyone!

@amannn amannn changed the title fix: Fix inconsistent ordering of messages for useExtracted fix: Fix inconsistent ordering of messages for useExtracted pt. 2 Nov 13, 2025
@amannn amannn merged commit 5cbd5da into main Nov 13, 2025
13 checks passed
@johanneskares
Copy link

amazing, thanks @amannn 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants