Skip to content

fix: Handle race condition when detecting locale changes which could lead to reset of messages with useExtracted#2131

Merged
amannn merged 3 commits intocanaryfrom
fix/wiped-out-messages
Nov 25, 2025
Merged

fix: Handle race condition when detecting locale changes which could lead to reset of messages with useExtracted#2131
amannn merged 3 commits intocanaryfrom
fix/wiped-out-messages

Conversation

@amannn
Copy link
Owner

@amannn amannn commented Nov 25, 2025

Problem doc

wiped out messages

user comments

colin 1

I might have run into another issue, but I’m not fully confident because it’s hard to reproduce.

A few times, all translations in a locale were replaced with empty strings. I first saw it while testing the canary build when pt.po translations suddenly got wiped, but I couldn’t reproduce it and assumed I had deleted that file by mistake and it had to be rebuilt.

However, now it happened twice on my branch using 4.5.2. I’ve probably run the build 20+ times and only saw this happen 3 times, so I don’t have a reliable repro. Just mentioning it in case it helps you spot something. This happened twice with my pt.po file and once with es.po, never with the source en.po file.

colin 2

This just happened again when I was setting up this repo on a new MacBook. It happened when I ran pnpm build from the root directory (which uses Turborepo).

I have three translation files there: en.po, es.po, pt.po. Only es.po got wiped out.

This happened only the first time I ran pnpm build. I couldn't reproduce it again. One thing I realized is that this always happened when I was building multiple apps at the same time (using pnpm build with Turborepo). I don't know if it's just a coincidence since that's how I often run builds, though.

julius 1

I was migrating a project from useTranslations to useExtracted. json files set up for useTranslations, and po files for useExtractions.

My process was following: Migrate one component or page to useExtracted, and wait for the other locale to refresh - once that was done I stopped the dev server (when editing target files with Poedit, something crashes in the dev server, which results in the target files only partially updating, or not at all until the dev server is restarted), and added the translations to the target file.
Every time when clicking "Needs work" for some translation in Poedit and starting the dev server, for a split second the nextjs browser tab returned with "fuzzy comments not suppported" before emptying all values in my "de.po" file.

Next.js: 16.0.3
next-intl: 4.5.5
MacOS Tahoe
Macbook Air M3 | Beefy hackintosh
Locales: "en.po" (source) and "de.po"

next.config.ts

import type {NextConfig} from 'next';
import createNextIntlPlugin from 'next-intl/plugin';

const withNextIntl = createNextIntlPlugin({
  experimental: {
    srcPath: './src',
    extract: {
      sourceLocale: 'en'
    },
    messages: {
      path: './messages',
      format: 'po',
      locales: 'infer'
    }
  }
});

const nextConfig: NextConfig = {
  reactCompiler: true,
  experimental: {
    turbopackFileSystemCacheForDev: true
  }
};

export default withNextIntl(nextConfig);

request.ts

export const locales = ['de', 'en', 'fr'] as const;
export const defaultLocale: Locale = 'de';

export default getRequestConfig(async () => {
  const store = await cookies();
  const candidate = store.get('locale')?.value;
  const locale = hasLocale(locales, candidate) ? candidate : defaultLocale;

  const jsonMessages = await import(`../../messages/${locale}.json`);
  const poMessages = await import(`../../messages/${locale}.po`);

  return {
    locale,
    messages: {
      ...jsonMessages.default,
      ...poMessages.default
    }
  };
});

issue description

Translation files (.po) are intermittently getting wiped out—all translations replaced with empty strings. This affects non-source locale files (pt.po, es.po, de.po) but never the source locale (en.po). It can affect a single locale, while other target locales are not affected.

Observed triggers:

  • Building multiple apps simultaneously with Turborepo
  • Dev server crashes/restarts during file editing

my notes

fuzzy flag is handled in separate pr and shouldnt make difference. colin reports that it happened during builds.

ideas

  • Could it be that we reset target translations since all the ones from the source are considered changed? Maybe the save date of their source file randomly changes
  • Check how often we read a catalog during build? Could there be a concurrency issue somewhere? Or is there a chance that we write before init is done?
  • Multiple compiler instances in parallel builds: Turborepo creates separate processes, each with its own singleton compiler instance. Multiple CatalogManager instances writing to the same files simultaneously without coordination
  • Race condition in saveLocale: translations.get(message.id) || '' (line 358) writes empty strings if translationsByTargetLocale hasn't loaded yet or is empty
  • Race condition in performInitialScan: Calls loadMessages() then save(), but if loadTargetMessages() hasn't completed, saveLocale could write empty translations
  • Empty Map initialization window: In loadTargetMessages (line 170), an empty Map is set before loading messages. If save() runs between these steps, it writes empty strings
  • No file locking: Multiple processes can write simultaneously, last write wins, potentially overwriting valid translations with empty ones
  • Timestamp check race: lastWriteByLocale check (line 341-353) can fail with concurrent writes - process A reads file, process B writes, process A writes over it
  • onLocalesChange callback race: File watcher could trigger while save is in progress, causing loadLocaleMessages to run concurrently with saveLocale

todo

  • "when editing target files with Poedit, something crashes in the dev server, which results in the target files only partially updating, or not at all until the dev server is restarted" > test this
  • investigate if turbopackFileSystemCacheForDev makes a difference
  • add debug logging, ask users to use it, report back when it happens
  • check how often file updates happen (during build?)

@vercel
Copy link

vercel bot commented Nov 25, 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 25, 2025 0:28am
next-intl-example-app-router Ready Ready Preview Comment Nov 25, 2025 0:28am
next-intl-example-app-router-without-i18n-routing Ready Ready Preview Comment Nov 25, 2025 0:28am

@amannn amannn changed the title fix: Handle race condition when detecting locale changes which could lead to wiped out messages fix: Handle race condition when detecting locale changes which could lead to wiped out messages with useExtracted Nov 25, 2025
@amannn amannn changed the title fix: Handle race condition when detecting locale changes which could lead to wiped out messages with useExtracted fix: Handle race condition when detecting locale changes which could lead to reset of messages with useExtracted Nov 25, 2025
@amannn amannn merged commit 8d81357 into canary Nov 25, 2025
8 checks passed
amannn added a commit that referenced this pull request Nov 27, 2025
- #2131
- #2134
- #2128

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Improves the extractor’s catalog loading/saving and message merging to
preserve .po metadata/flags and avoid race conditions, with
deterministic sorting and a po-parser bump.
> 
> - **Extractor**:
>   - **CatalogManager**:
> - Load catalogs before scanning (`loadCatalogsPromise`); add
`reloadLocaleCatalog` and poll-at-save merging to avoid race conditions.
> - Store target translations as `ExtractedMessage` maps; merge unknown
props (e.g., flags) from disk; do not compare references for change
detection.
> - Parallelize saves and ensure writes wait for catalogs to load;
refactor `save/saveImpl` to return `void`.
> - **Deterministic sorting**: use `localeCompare` helper for paths/ids
and reference ordering.
> - **Formatters**:
> - JSON/PO use shared `setNestedProperty`; PO retains and writes back
metadata, with stable ordering.
> - **Types/Utils**:
> - `ExtractedMessage` allows extra properties; add `localeCompare`
helper.
> - **Tests**:
> - Add coverage for race conditions, reference updates across catalogs,
flags/metadata preservation and removal, and `srcPath` filtering; refine
async test utilities.
> - **Dependencies**:
>   - Bump `po-parser` to `^1.0.2`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
94d7fae. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: DongYun Kang <kdy.1997.dev@gmail.com>
@amannn amannn deleted the fix/wiped-out-messages branch January 30, 2026 10:27
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.

1 participant