intl-watcher
v1.3.2
Published
Automated translation key extraction and dictionary management plugin for Next.js
Maintainers
Readme

Automatically scans your Next.js project's source files to manage internationalization (i18n) translation keys. It keeps your translation dictionaries up-to-date by extracting new keys, removing unused ones, and optionally partitioning keys into separate client and server dictionaries.
[!NOTE] Primarily designed for use with
next-intl, this plugin can technically be adapted for other i18n libraries or frameworks that share a similar dictionary structure and access pattern in code.
Features
- Automatic Extraction: Scans your project's source code for i18n keys.
- Namespace Support: Handles nested i18n keys organized into namespaces for structured translations.
- Dictionary Syncing: Automatically updates JSON dictionaries with new translation keys.
- Unused Keys Handling: Warns or removes unused keys.
- Client/Server Partitioning: Separates translation keys into client-side and server-side bundles.
- Debounced Scanning: Efficiently handles rapid file changes.
- Suppression Escape Hatch: Silence warnings for specific unsupported expression kinds while native support is pending.
Installation
Install the package via npm, yarn or pnpm:
npm install intl-watcher
# or
yarn add intl-watcher
# or
pnpm add intl-watcherUsage
Wrap your Next.js configuration with the provided createIntlWatcher function:
// next.config.ts
import { createIntlWatcher } from 'intl-watcher'
const withIntlWatcher = createIntlWatcher({
dictionaryPaths: ['./i18n/en.json', './i18n/de.json'],
})
export default withIntlWatcher({
reactStrictMode: true,
// other Next.js config options...
})Single-Run Mode
For use cases where you need to perform a one-time sync without file watching (e.g., in CI/CD pipelines, custom build scripts, or pre-commit hooks), use the syncTranslationKeys function:
// scripts/sync-translations.ts
import { syncTranslationKeys } from 'intl-watcher'
syncTranslationKeys({
dictionaryPaths: ['./i18n/en.json', './i18n/de.json'],
// ... other options
})Run this script manually or as part of your build process:
# Using ts-node
npx ts-node scripts/sync-translations.ts
# Using tsx
npx tsx scripts/sync-translations.ts
# In package.json scripts
{
"scripts": {
"sync-translations": "tsx scripts/sync-translations.ts"
}
}Use Cases
- CI/CD Validation: Ensure translation keys are synchronized before deployment
- Pre-commit Hooks: Automatically update dictionaries when committing changes
- Custom Build Steps: Integrate into existing build pipelines
- Testing: Programmatically verify translation coverage
Configuration Options
The following options apply to both createIntlWatcher and syncTranslationKeys:
Required Options
dictionaryPaths
- Type:
string[] - Description: Paths to JSON dictionary files for each language. These files will be managed and kept in sync with your source.
Shared Optional Options
defaultValue
- Type:
(key: string) => string - Default:
(key) => `[NYT: ${key}]` - Description: Function that generates a default translation for new keys.
removeUnusedKeys
- Type:
boolean - Default:
false - Description: When true, removes keys no longer found in source files; otherwise emits warnings.
scanDelay
- Type:
number - Default:
500 - Description: Delay in milliseconds before re‑scanning after file changes.
tsConfigFilePath
- Type:
string - Default:
tsconfig.json - Description: Path to
tsconfig.jsonfor project file resolution.
watchPaths
- Type:
string[] - Default:
['./src'] - Description: Paths that the plugin watches to trigger rescans.
This does not change which files belong to your TypeScript project; that is controlled by
tsconfig.jsonviatsConfigFilePath.
useTabs
- Type:
boolean - Default:
true - Description: Use tab characters for indentation. When
false, spaces will be used instead (seetabWidth).
tabWidth
- Type:
number - Default:
4 - Description: Number of spaces per indentation level. Only applies when
useTabsisfalse.
suppressExpressionWarnings
- Type:
string[] - Default:
[] - Description: Expression kind names for which the unsupported-expression diagnostic is silenced.
Each entry must match a kind name as reported in the warning — for example
'ConditionalExpression'or'CallExpression'. Any entry that does not suppress at least one warning during a scan will itself emit a warning, prompting you to remove the stale entry.
[!NOTE] This option is intended as a temporary escape hatch while native support for a given expression kind is pending. Before adding a kind here, consider opening a feature request so that support can be added properly.
Non-Partitioning Mode (default)
applyPartitioning
- Type:
false - Default:
false - Description: Default mode where keys are not split.
translationFunctions
- Type:
string[] - Default:
['useTranslations', 'getTranslations'] - Description: Translation function name(s) to scan for keys.
Partitioning Mode
applyPartitioning
- Type:
true - Description: Enables splitting translation keys into separate client and server bundles.
partitioningOptions
- Type:
{ clientFunction?: string; serverFunction?: string } - Default:
{ clientFunction: 'useTranslations', serverFunction: 'getTranslations' } - Description: Function names to use for extracting client vs server translation keys.
Dictionary Partitioning
When applyPartitioning is enabled, the plugin generates separate dictionaries for client and server bundles.
For example:
locales/
├── en.json
├── en.client.json
└── en.server.jsonThis enables optimized bundle sizes and clearer separation of translations by environment.
Development
See .github/CONTRIBUTING.md, then .github/DEVELOPMENT.md.
Thanks! 💖
