i18n-sentry
| Install | |
|---|---|
npm install i18n-sentry |
|
| Latest Version: | 0.2.0 |
| License: | MIT |
| Last Updated: | Sep 21, 2026 |
| Links: | npm |
i18n-sentry
A lightweight, framework-aware i18n quality checker for Vue, React, Angular and Svelte projects. Detects missing keys, hardcoded strings, invalid ICU syntax, and more.
What it checks
| Level | Check |
|---|---|
| Error | t('key') used in code but key missing in locale files |
| Error | Key exists in source locale but missing in other locales |
| Error | Key exists in other locales but missing in source locale |
| Error | Key defined in locale files but never used in code |
| Error | Invalid ICU message syntax (unbalanced braces, empty placeholders, unescaped @) |
| Error | Placeholder mismatch between locales ({user} in de vs {name} in en) |
| Error | ICU structure mismatch between locales (plural vs simple) |
| Error | Namespace conflict (key used as both object and string) |
| Error | Hardcoded visible text in templates |
| Error | Hardcoded text in visible props (placeholder, noDataText, alt, etc.) |
| Error | Hardcoded text in <script setup> — object properties and known function calls (Vue only, AST mode) |
| Warning | Hardcoded accessibility text (aria-label, title, etc.) |
Status: v0.1.0 – Vue/Nuxt and React (react-i18next, react-intl) are production-ready. Angular (ngx-translate) and Svelte are supported. Angular Localize (XLIFF) is planned for v0.2.0
Framework support
| Framework | Key extraction | Hardcoded text | AST mode (script blocks) | Status |
|---|---|---|---|---|
| Vue / Nuxt (vue-i18n) | t('key'), $t('key') |
<template> scanning |
✓ <script setup> |
Production-ready |
| React (react-i18next) | t('key'), useTranslation(), i18n.t(), <Trans i18nKey>, tagged templates |
JSX scanning | — | Production-ready |
| React (react-intl) | <FormattedMessage id>, intl.formatMessage(), defineMessages() |
JSX scanning | — | Production-ready |
| Angular (ngx-translate) | | translate, .instant(), .get() |
Template scanning | — | Supported |
| Svelte (svelte-i18n) | $t('key'), t('key') |
Template scanning | — | Supported |
| Svelte (typesafe-i18n) | $LL.key() |
Template scanning | — | Supported |
| Angular Localize (XLIFF) | i18n="@@key" |
XLIFF parsing | — | Planned v0.2 |
Limitation: Keys must be statically analyzable via regex. Dynamically generated keys (e.g.,
t(variable),t(condition ? 'key1' : 'key2')) are not supported yet. UseignoreKeysto exclude patterns until AST-based key extraction is implemented.
Setup
1. Install as npm package
npm install i18n-sentry
2. Run interactive setup
Note: The examples below use
npx, but the CLI also works with other package runners such asbunx,pnpm dlx, andyarn dlx.
npx i18n-sentry setup
The setup script will:
- Auto-detect your framework (
vue,react,angular,svelte) frompackage.json - Auto-detect your locale directory and scan directory
- Auto-detect your locale languages from existing files
- For Vue projects, offer to configure AST mode for
<script setup>scanning - Ask whether to install
tsx(required to run the TypeScript-based CLI) - Ask whether to install the pre-commit hook
- Ask whether to add a
lint:i18nscript topackage.json - Write
i18n-sentry.config.jsonto your project root or to the path of your package.json
Usage
Run manually
npx i18n-sentry
or
npx i18n-sentry scan
Additional commands
npx i18n-sentry sort # sort locale files
npx i18n-sentry install-hook # install git hook
Help & Version
npx i18n-sentry --help # show CLI help
npx i18n-sentry --version # show version
Git hook (optional)
The hook runs on every commit, warns about i18n issues, sorts locale files alphabetically, and never blocks commits.
Config reference
| Key | Type | Default | Description |
|---|---|---|---|
localeDir |
string |
— | Path to locale files or directories (required) |
scanDir |
string |
— | Directory to scan for source files (required) |
locales |
string[] |
["de", "en"] |
Locale identifiers to check |
sourceLocale |
string |
first in locales |
The reference/source locale |
ignoreKeys |
string[] |
[] |
Keys to ignore — supports * wildcard suffix. Use this for dynamic keys that cannot be statically analyzed (e.g., t(variable) or t(condition ? 'a' : 'b')) |
ignoreRawText |
string[] |
[] |
Exact text values to ignore in template checks |
textAttributes |
string[] |
[] |
Additional props to flag as hardcoded text errors |
warnAttributes |
string[] |
[] |
Additional props to flag as hardcoded text warnings |
scriptScanFunctions |
Record<string, "all" | number[]> |
{} |
Vue only. Dot-path function calls to scan for hardcoded strings in <script setup>, e.g. { "toast.success": "all" }. "all" scans every string argument; number[] scans only those 0-based argument indices. See AST mode |
scriptScanProperties |
string[] |
[] |
Vue only. Object property keys whose string value is scanned in <script setup>, e.g. ["label", "title"]. See AST mode |
Locale directory structures
Both flat and nested structures are supported and auto-detected:
# Flat (default)
locales/
├── de.json
└── en.json
# Nested (auto-detected)
locales/
├── de/
│ ├── common.json
│ └── users.json
└── en/
├── common.json
└── users.json
AST mode (Vue only)
The regular template scanner only looks inside <template>. It can't see hardcoded strings living in the <script setup> block — in JS objects, toast(...) calls, h() render functions, or arguments passed to helper functions:
<script setup lang="ts">
const repairStates = [
{ value: "open", label: "Offen" }, // not caught by template scanning
]
toast.success("Status aktualisiert") // not caught by template scanning
h("p", "Dieser Text wird sonst übersehen.") // not caught by template scanning
</script>
AST mode fixes this by parsing <script setup> with @vue/compiler-sfc + the TypeScript compiler API, and looking for string values in two configurable contexts:
- Object properties whose key is listed in
scriptScanProperties— e.g.{ label: "Offen" } - Function call arguments for dot-path calls listed in
scriptScanFunctions, at specific argument indices (or"all")
In both contexts, the value doesn't have to be a plain string literal — AST mode also resolves:
- Template literals with interpolation, e.g.
toast.success(`Status: ${status} aktualisiert`)is reported asStatus: {status} aktualisiert constvariables, e.g.const msg = "..."followed bytoast.error(msg)— the call is reported at the point it's used, with the resolved text
{
"scriptScanFunctions": {
"toast.success": "all",
"toast.error": "all",
"toast.warning": "all",
"h": [1, 2],
"createTextColumn": [2]
},
"scriptScanProperties": ["label", "title", "text", "description"]
}
Notes:
h(tag, children)andh(tag, props, children)are both valid signatures — index0is always the tag name, never text, but the actual content sits at index1in the first form and index2in the second. Scan both ([1, 2], the setup wizard's default) unless you know your codebase only uses one form.- Calls with mixed technical/display arguments (e.g.
createTextColumn(columnHelper, "caseNumber", "Kennung")) need explicit argument indices rather than"all", since earlier args are often field keys, not display text. - Calls to
t(),$t(),i18n.t(),i18n.$t()are always excluded — those are already-translated keys, handled by the regular key extraction, not by AST mode. - The setup wizard (
npx i18n-sentry setup) offers to configure this automatically for Vue projects, withtoast.*andh()as suggested defaults. - AST mode is opt-in: it only runs when
scriptScanFunctionsorscriptScanPropertiesis non-empty in your config. - A single config file wins. i18n-sentry searches your project root first, then subdirectories up to 2 levels deep, and uses the first
i18n-sentry.config.jsonit finds — an older config left behind at the root (e.g. from before AST mode existed) silently takes priority over a newer one further down. If AST mode seems to have no effect despite a correct config, check for a stray config file elsewhere in your project (find . -name "i18n-sentry.config.json" -not -path "*/node_modules/*").
Known limitations
AST mode resolves direct literals, template literals, and const aliases — but a few patterns are intentionally not covered, since handling them reliably would need real data-flow or module-resolution analysis rather than a straightforward AST walk:
- Renamed or destructured imports —
import { toast as notify } from 'vue-sonner'; notify.success("...")isn't matched, sincescriptScanFunctionsis matched by the identifier name written in the call, not by where it was imported from. letvariables — onlyconstdeclarations are resolved as aliases, since aletcould be reassigned between declaration and use, making the "this is its value" assumption unsafe.- Getters or methods returning a string —
{ get label() { return "..." } }is not a plain property assignment and isn't scanned.
If any of these show up often in your codebase, ignoreRawText won't help (it only matches exact strings already found) — for now, the workaround is to rewrite the call site to a plain literal or a const, or wait for AST mode to grow support for that pattern.
Exit codes
| Code | Meaning |
|---|---|
0 |
No errors (warnings may still be present) |
1 |
One or more errors found |
Known limitations & roadmap
v0.2 planned
- Angular Localize / XLIFF support — parse
messages.xlf, extract<trans-unit id>as keys, detect missing<target>translations, scan templates fori18n="@@key"attributes - Nested locale file support — currently merges files using filename as namespace prefix; deeper nesting strategies to be refined
- Multi-framework monorepo support — currently detects one framework per project via
package.json; file-based detection per directory planned - AST mode for React/Angular/Svelte — extend the Vue
<script setup>AST scanner to JSX (React) and component class bodies (Angular/Svelte)
Future roadmap
- AST-based key extraction — replace the regex-based
t('key')extraction with Babel (React), TypeScript Compiler API, or Angular template parser for higher accuracy - Confidence scoring — flag uncertain matches separately from strong matches
- Normalized i18n IR — unified intermediate representation across frameworks
- VS Code extension — inline warnings in editor
- CI reporter formats — JSON, SARIF, GitHub Actions annotations
- Dynamic key resolution — advanced patterns for
t(variable)and conditional key access
Feedback & Contributing
This project was developed by MEGWARE Computer Vertrieb und Service GmbH and actively being improved. We welcome your feedback, bug reports, and contributions!
- Found a bug? Open an issue
- Have a feature request? Start a discussion
- Want to contribute? Pull requests are welcome!
Your input helps make i18n-sentry better for the community.
Related Packages
title: inlang CLI - Localization Automation for CI/CD description: Automate tran...
Localization for Laravel applications, from the terminal: Lyngo wraps user-facin...
Framework-agnostic i18n extraction tool with plugin support for Svelte, Vue, and...
Find missing translations, remove dead keys, rename across all locales at once —...
Git-native translation management with intelligent stale detection. Hash-based s...
Version History
| Version | Released | License |
|---|---|---|
| 0.2.0 | MIT | |
| 0.1.3 | MIT | |
| 0.1.2 | MIT |