eslint-plugin-i18next

An ESLint plugin that catches hardcoded, non-localized strings in JSX, Vue templates, and TypeScript before they ship.

Tool
npm
v6.1.5
175 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
39 /100 Needs Attention
Development Activity 12
Maintenance 0
Community 64
Maturity 60
Momentum 20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
71 /100 Good
Architecture 78
Code Quality 72
Innovation 58
Learning Curve 75

eslint-plugin-i18next adds a single, focused ESLint rule — no-literal-string — that flags raw text left directly in JSX, Vue templates, or JavaScript/TypeScript source instead of being routed through an i18next translation call. It’s built for teams that already use i18next (or a similar translation library) and want a lint-time safety net instead of catching missed strings during a translation audit or a bug report from a non-English-speaking user.

The rule ships with pragmatic defaults tuned for React (JSX-only mode, skips className/style/id-like attributes, ignores uppercase constants and object keys) but is fully configurable for Vue templates, TypeScript, and vanilla JavaScript codebases. It plugs into both ESLint 9’s flat config and legacy .eslintrc setups via ready-made flat/recommended and recommended configs.

What You Get

  • A single, well-scoped ESLint rule (no-literal-string) that flags untranslated text in JSX, Vue templates, and plain JS/TS
  • Ready-made flat/recommended and recommended configs for ESLint 9 flat config and legacy .eslintrc setups
  • Configurable allow-lists for callees, JSX components/attributes, object keys, class properties, and word patterns (emoji, punctuation, ALL_CAPS constants)
  • TypeScript-aware detection that skips literals whose contextual type is itself a string-literal type, avoiding false positives on typed unions
  • Framework-specific modes (jsx-only, jsx-text-only, vue-template-only) to scope validation to the parts of a codebase that actually render to users

Common Use Cases

  • Enforcing translation coverage on a React/JSX codebase migrating to i18next
  • Catching accidentally hardcoded English strings introduced in new pull requests via CI lint checks
  • Auditing a Vue application’s templates for text that bypasses the i18n pipeline
  • Gating merges in international SaaS products where every user-facing string must be localizable

Under The Hood

Architecture

The plugin is a single-rule ESLint plugin: lib/index.js uses requireindex to auto-load everything under lib/rules/ and exposes flat/recommended (ESLint 9 flat config) and recommended (legacy .eslintrc) config presets. The actual logic lives in lib/rules/no-literal-string.js, which tracks whether the current AST scope is “valid” (translatable-string-safe) via an indicatorStack — a stack of booleans pushed and popped by ESLint visitor callbacks for node types like JSXElement, JSXAttribute, VElement (Vue), TSEnumMember, PropertyDefinition, and BinaryExpression. Vue support is layered in via parserServices.defineTemplateBodyVisitor, re-dispatching Vue template nodes into the same handlers used for JSX. Configuration matching (callee/attribute/component allow-lists) is factored into lib/helper/ (shouldSkip, matchPatterns, generateFullMatchRegExp), with a WeakMap cache in matchPatterns avoiding recompilation of the same pattern set across many AST nodes in one lint run.

Tech Stack

Written in plain CommonJS JavaScript with no build step — lib/ ships as-is, paired with a hand-authored lib/index.d.ts for TypeScript consumers. It uses requireindex to auto-register rules, and works against ESLint’s own parser abstraction plus optional parsers for each supported syntax: @babel/eslint-parser (JSX), vue-eslint-parser (Vue SFCs), and @typescript-eslint/parser (TypeScript, including type-checker access for contextual-type checks). The repo is a pnpm workspace with an examples/ package used as a live lint fixture, tested with mocha, and enforces commit hygiene via husky, lint-staged, and commitlint. CI runs on GitHub Actions across Node 18.x/20.x, with a separate manual-dispatch release workflow.

Code Quality

Testing is thorough for the plugin’s single rule: mocha drives ESLint’s own RuleTester across more than ten dedicated scenario files (default, callees, jsx-only, jsx-text-only, jsx-attributes, jsx-components, object-properties, words, vue, typescript, should-validate-template), backed by shared fixture files (valid.jsx, invalid.jsx, valid-typescript.ts). The examples/ package additionally self-lints as an integration smoke test in CI. The plugin’s own source is plain JS with no static type-checking of its implementation (only the shipped .d.ts is typed), and there’s no visible ESLint config for the plugin’s own source — style is enforced only via prettier + lint-staged at commit time.

What Makes It Unique

Rather than a simple regex sweep over source text, the rule models scope validity as a stack that visitor callbacks push and pop per AST node — letting it distinguish, node by node, between a literal that’s an enum member, an object key, a JSX className, and one that’s genuinely rendered to a user. Combined with native TypeScript type-checker integration (skipping literals whose contextual type is itself a string-literal or literal-union type), this gives more precise scoping than pattern-only equivalents, though the underlying problem — flagging unlocalized UI strings — is a well-established category across linting ecosystems rather than a novel one.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers