@spaced-out/eslint-plugin-i18n
v3.1.1
Published
ESLint plugin for i18n with TypeScript support
Downloads
1,344
Readme
eslint-plugin-i18n
ESLint Rules for Translation and Internationalization:
This repository introduces ESLint rules designed to enforce best practices for internationalization (i18n) in your codebase. These rules ensure that static labels are replaced with translation functions and that all translation keys are defined in the corresponding en.json(source of truth).
Note: This plugin has been migrated to ESLint 9 with full functionality restored using the new scope analysis API and now includes comprehensive TypeScript support.
Features
- ✅ Full TypeScript Support: Works seamlessly with
.tsand.tsxfiles - ✅ Backward Compatibility: Maintains full support for
.jsand.jsxfiles - ✅ ESLint 9 Compatible: Uses modern ESLint 9 scope analysis API
- ✅ Auto-fix Support: Many rules include automatic fixes
- ✅ Type-aware Analysis: Enhanced type checking for better accuracy
Rules overview
1. no-static-labels
Description:
This rule identifies and prevents the use of static labels (hardcoded strings) in JSX code. It enforces wrapping all static labels with a translation function (e.g., t('key', 'defaultValue')).
Key Features:
- Detects static strings in JSX elements, attributes, and variables.
- Identifies template literals, binary expressions, logical expressions, and conditional expressions containing static strings.
- Supports dynamic attribute checks, including menu, options, tooltip, and more.
- Auto-fix functionality to wrap detected static labels in a translation function.
- TypeScript Support: Works with TypeScript interfaces, types, and type annotations.
Why Use It?
Ensures that all user-facing text is localized, improving the scalability and maintainability of your application for internationalization.
2. missing-translation
Description:
This rule checks if the translation keys used in your t function are present in your source translation JSON file (e.g., en.json,). It warns developers about missing keys, ensuring translation files remain consistent with the codebase.
Key Features:
- Verifies translation keys passed to the
tfunction. - Supports t as a standalone function and as a method (e.g.,
this.t()). - Helps maintain completeness of translation files.
- Warns developers if keys are missing from the source of truth JSON file (en.json).
- TypeScript Support: Works with TypeScript function signatures and type definitions.
Why Use It?
Ensures all user-facing text is properly localized and prevents runtime issues due to missing translations.
3. invalid-translation-key-format
Description:
This rule enforces a strict naming convention for translation keys used in the t('key', 'defaultValue') function to ensure consistency and predictability across translation files.
Convention Rules:
- All spaces in the fallback value must be replaced by underscores (
_) in the key. - The key must preserve the exact casing of the fallback string (no forced lowercase).
- No special characters allowed in the key, except a single apostrophe (
').
4. no-react-i18next-import
Description:
This rule disallows the use of the useTranslation hook from react-i18next in your codebase. It enforces a centralized approach to translation using the t('key', 'fallback') pattern, instead of dynamically obtaining t via hooks. (rule specifically for sense repo)
Key Features:
- Detects imports of useTranslation from react-i18next.
- Reports any invocation of useTranslation() in the code.
- TypeScript Support: Works with TypeScript import statements and type declarations.
5. no-dynamic-labels
Description:
This rule enforces wrapping dynamic labels (variables, expressions, and computed values) in translation functions using the makeKey wrapper. It ensures that all dynamic content that could be user-facing is properly localized.
Key Features:
- Detects dynamic expressions in JSX that should be translated
- Enforces the use of
makeKey()function for dynamic translation keys - Supports various dynamic contexts: variables, member expressions, function calls, conditional expressions, logical expressions, and template literals
- Auto-fix functionality to wrap detected dynamic labels in
t(makeKey(dynamicValue), dynamicValue)format - Handles array method calls (map, filter, flatMap, reduce) and their callbacks
- Supports styled-components template expressions
- TypeScript Support: Enhanced type checking for better accuracy in identifying string vs non-string expressions
Why Use It?
Ensures that all dynamic content (variables, computed values, expressions) that could be user-facing is properly internationalized using the standardized makeKey pattern for dynamic translation keys.
6. i18n-enforce-makeKey-wrapper
Description:
This rule enforces that dynamic variables used as translation keys must be wrapped with the makeKey() function from the useLabelTranslation hook. It ensures consistent handling of dynamic translation keys across the codebase.
Key Features:
- Detects dynamic variables used directly as translation keys
- Enforces wrapping with
makeKey()function fromuseLabelTranslationhook - Validates proper hook usage and destructuring
- Auto-fix functionality to wrap dynamic keys with
makeKey() - Supports both direct function calls and method calls
- Configurable translation function names and hook names
- TypeScript Support: Works with TypeScript interfaces, types, and function signatures
Why Use It?
Ensures consistent and safe handling of dynamic translation keys by enforcing the use of the makeKey wrapper function, which provides proper key generation and validation.
TypeScript Support
This plugin now includes comprehensive TypeScript support, making it suitable for both JavaScript and TypeScript projects.
TypeScript Features:
- File Extension Detection: Automatically detects
.tsand.tsxfiles - Type Annotation Support: Understands TypeScript type annotations and interfaces
- Enhanced Type Checking: Better accuracy in identifying string vs non-string expressions
- TypeScript Pragma Support: Recognizes TypeScript-specific pragmas (
@ts-check,@ts-nocheck, etc.) - Interface and Type Support: Works with TypeScript interfaces, types, and type aliases
- Generic Support: Handles TypeScript generics and complex type expressions
TypeScript Configuration:
// .eslintrc.js
module.exports = {
extends: [
// other configurations...
],
plugins: ["@spaced-out"],
overrides: [
{
files: ["*.ts", "*.tsx"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
rules: {
"@spaced-out/i18n/no-static-labels": "error",
"@spaced-out/i18n/missing-translation": "warn",
"@spaced-out/i18n/invalid-translation-key-format": "error",
"@spaced-out/i18n/no-react-i18next-import": "error",
"@spaced-out/i18n/no-dynamic-labels": "error",
"@spaced-out/i18n/i18n-enforce-makeKey-wrapper": "error",
},
},
{
files: ["*.js", "*.jsx"],
rules: {
"@spaced-out/i18n/no-static-labels": "error",
"@spaced-out/i18n/missing-translation": "warn",
"@spaced-out/i18n/invalid-translation-key-format": "error",
"@spaced-out/i18n/no-react-i18next-import": "error",
"@spaced-out/i18n/no-dynamic-labels": "error",
"@spaced-out/i18n/i18n-enforce-makeKey-wrapper": "error",
},
},
],
};TypeScript Usage Examples:
// TypeScript interface example
interface User {
name: string;
email: string;
}
// TypeScript component with proper translations
const UserProfile: React.FC<{ user: User }> = ({ user }) => {
return (
<div>
<h1>{t('user_profile_title', 'User Profile')}</h1>
<p>{t('welcome_message', 'Welcome, {name}!', { name: user.name })}</p>
</div>
);
};
// TypeScript with type annotations
const getMessage = (type: 'success' | 'error'): string => {
return t(`message_${type}`, type === 'success' ? 'Success!' : 'Error occurred');
};ESLint 9 Compatibility
This plugin has been successfully migrated to support ESLint 9 with full functionality restored using the new scope analysis API.
Changes Made:
- Updated scope analysis: Replaced
context.getScope()with ESLint 9'scontext.sourceCode.scopeManagerAPI - Restored all rules: All rules now work with full functionality using the new scope analysis
- Enhanced error handling: Added fallback mechanisms for scope analysis failures
- Updated ESLint dependency: Upgraded from ESLint 8.35.0 to ESLint 9.x
- TypeScript support: Added comprehensive TypeScript parsing and analysis capabilities
Impact:
- All rules are fully functional: No functionality has been lost in the migration
- Enhanced compatibility: Better error handling and fallback mechanisms
- Future-proof: Uses ESLint 9's modern scope analysis API
- TypeScript ready: Full support for TypeScript syntax and type checking
Installation
1. Install ESLint and the plugin:
npm install @spaced-out/eslint-plugin-i18n --save-dev2. For TypeScript projects, install additional dependencies:
npm install @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript --save-dev3. Add the rules to your .eslintrc.js configuration:
For JavaScript projects:
module.exports = {
extends: [
// other configurations...
],
plugins: ["@spaced-out"],
rules: {
"@spaced-out/i18n/no-static-labels": "error",
"@spaced-out/i18n/missing-translation": "warn",
"@spaced-out/i18n/invalid-translation-key-format": "error",
"@spaced-out/i18n/no-react-i18next-import": "error",
"@spaced-out/i18n/no-dynamic-labels": "error",
"@spaced-out/i18n/i18n-enforce-makeKey-wrapper": "error",
},
};For TypeScript projects:
module.exports = {
extends: [
// other configurations...
],
plugins: ["@spaced-out"],
overrides: [
{
files: ["*.ts", "*.tsx"],
parser: "@typescript-eslint/parser",
// ... rest of TypeScript configuration
}
]
};Using the provided configurations:
module.exports = {
extends: [
"plugin:@spaced-out/i18n/recommended", // For JavaScript
// OR
"plugin:@spaced-out/i18n/typescript", // For TypeScript
],
};4. Configure your translation files:
Place your translation files (e.g., en.json, fr.json) in the translations folder inside your i18n directory. The rules will automatically pick up these files to validate translation keys.
How It Works
Rule: no-static-labels
The rule inspects your code for hardcoded static strings. For example:
Input:
<div>Hello, World!</div>;
const message = "Welcome!";
<h1>{message}</h1>;Output: The rule suggests wrapping the strings in a translation function:
<div>{t("Hello_World", "Hello, World!")}</div>;
const message = t("Welcome", "Welcome!");
<h1>{message}</h1>;Supported Contexts:
- JSX Text (
<div>Static Text</div>) - JSX Attributes (
<button label="Click me" />) - JavaScript Variables (
const label = "Static text";) - Template Literals, Binary Expressions, Logical Expressions, Conditional Expressions, and Object Properties.
- TypeScript: Interfaces, types, and type annotations
Rule: missing-translation
The rule checks translation keys used in the t function. If a key is missing in the JSON files, a warning is displayed:
Input:
const message = t("NonExistentKey", "Default Message");Warning Message:
Translation key "NonExistentKey" is missing in the config translations JSON file.Supported Function Usages:
t('key', 'defaultValue')- TypeScript: Type-safe function calls with proper type checking
Rule: invalid-translation-key-format
This rule checks that the key passed to t() matches the fallback string (with underscores for spaces, identical casing, and only apostrophes allowed as special characters).
Input:
t("youre cute", "You're cute");Output: The rule suggests fixing the key of wrapped translation function:
Translation key "youre_cute" is invalid. It should be "You're_cute".Supported Contexts:
- JSX Text (
<div>Static Text</div>) - JSX Attributes (
<button label="Click me" />) - JavaScript Variables (
const label = "Static text";) - Template Literals, Binary Expressions, Logical Expressions, Conditional Expressions, and Object Properties.
- TypeScript: Type-safe key validation
Rule: no-dynamic-labels
This rule detects dynamic expressions that should be wrapped in translation functions using the makeKey pattern.
Input:
const userName = "John";
const items = [{ name: "Item 1" }, { name: "Item 2" }];
<div>Hello, {userName}!</div>
<div>{items.map(item => item.name)}</div>
<div>{getLabel()}</div>Output: The rule suggests wrapping dynamic expressions in translation functions:
const userName = "John";
const items = [{ name: "Item 1" }, { name: "Item 2" }];
<div>Hello, {t(makeKey(userName), userName)}!</div>
<div>{items.map(item => t(makeKey(item.name), item.name))}</div>
<div>{t(makeKey(getLabel()), getLabel())}</div>Supported Contexts:
- Dynamic variables (
{userName}) - Member expressions (
{item.name}) - Function calls (
{getLabel()}) - Logical expressions (
{value || "default"}) - Template literals with expressions
- Array method callbacks (
items.map(item => item.name)) - Styled-components template expressions
- TypeScript: Enhanced type checking for better accuracy
Rule: i18n-enforce-makeKey-wrapper
This rule ensures that dynamic variables used as translation keys are properly wrapped with the makeKey() function.
Input:
const label = getLabel();
const message = t(makeKey(label), label);Output:
The rule enforces wrapping dynamic keys with makeKey():
Required Setup:
// Must import and use the hook (specific to Sensehq(ui-server) usecase)
import { useLabelTranslation } from 'path/to/hook';
function Component() {
const { t, makeKey } = useLabelTranslation();
const label = getLabel();
return <div>{t(makeKey(label), label)}</div>;
}Error Messages:
- Missing
makeKeywrapper: "Dynamic Variable translation key must be wrapped with makeKey() from useLabelTranslation" - Missing hook call: "useLabelTranslation hook must be called in this component to use makeKey"
- Missing destructuring: "makeKey must be destructured from useLabelTranslation hook call"
Fix Suggestions and Workflow
Auto-Fix for rules that support it:
- Rules that supports auto-fix functionality. Run ESLint with the
--fixflag to automatically wrap static labels in thetfunction.
npx eslint . --fixHandling Missing Translations
- Run the linter to identify missing keys:
npx eslint .- Use a script to generate the missing keys in your translation files. For example:
yarn generate-translationsThis script should:
- Parse all translation keys from the codebase.
- Compare the keys with existing translation JSON files.
- Append missing keys with default values.
Migration Guide
From JavaScript to TypeScript
If you're migrating your project from JavaScript to TypeScript:
- Install TypeScript dependencies:
npm install @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript --save-dev- Update your ESLint configuration:
// .eslintrc.js
module.exports = {
overrides: [
{
files: ["*.ts", "*.tsx"],
parser: "@typescript-eslint/parser",
// ... rest of TypeScript configuration
}
]
};- Add TypeScript pragmas to your files:
// @ts-check
import React from 'react';- Run ESLint to check for any issues:
npx eslint . --ext .ts,.tsxBackward Compatibility
The plugin maintains full backward compatibility with JavaScript files. All existing configurations will continue to work without any changes.
Contributing
When contributing to this plugin, please ensure:
- TypeScript Support: All new features should work with both JavaScript and TypeScript
- Backward Compatibility: Changes should not break existing JavaScript functionality
- Type Safety: Use TypeScript for new development when possible
- Testing: Test with both
.js/.jsxand.ts/.tsxfiles
License
ISC
