@intl-party/eslint-plugin
v1.0.2
Published
ESLint plugin for IntlParty - detect hardcoded strings and enforce i18n best practices
Maintainers
Readme
@intl-party/eslint-plugin
ESLint plugin for IntlParty - enforce best practices and catch common i18n issues in your code.
Features
- 🚫 No hardcoded strings - Detect untranslated user-facing text
- 🔍 Missing translation keys - Catch references to non-existent translation keys
- ⚛️ React hooks enforcement - Prefer translation hooks over direct i18n usage
- 📝 Consistent patterns - Enforce consistent translation patterns across your codebase
- ⚙️ Configurable rules - Customize rules to fit your project needs
Installation
npm install --save-dev @intl-party/eslint-plugin
# or
pnpm add -D @intl-party/eslint-plugin
# or
yarn add --dev @intl-party/eslint-pluginConfiguration
Basic Setup
Add the plugin to your ESLint configuration:
// .eslintrc.js
module.exports = {
plugins: ["@intl-party"],
extends: ["@intl-party/recommended"],
};Manual Configuration
// .eslintrc.js
module.exports = {
plugins: ["@intl-party"],
rules: {
"@intl-party/no-hardcoded-strings": "error",
"@intl-party/no-missing-keys": "error",
"@intl-party/prefer-translation-hooks": "warn",
},
};TypeScript Configuration
// .eslintrc.js
module.exports = {
extends: ["@intl-party/recommended", "@intl-party/typescript"],
parserOptions: {
project: "./tsconfig.json",
},
};Rules
@intl-party/no-hardcoded-strings
Prevents hardcoded user-facing strings that should be translated.
❌ Incorrect
function Welcome() {
return <h1>Welcome to our app!</h1>; // Hardcoded string
}
function Button() {
return <button>Click here</button>; // Hardcoded string
}
const message = "Hello world"; // Hardcoded string✅ Correct
function Welcome() {
const t = useTranslations("common");
return <h1>{t("welcome")}</h1>;
}
function Button() {
const t = useTranslations("common");
return <button>{t("clickHere")}</button>;
}
const message = t("hello");Configuration
{
"@intl-party/no-hardcoded-strings": ["error", {
"ignorePatterns": [
"^\\d+$", // Numbers
"^[A-Z_]+$", // Constants
"^https?://", // URLs
"className", // CSS classes
"data-*" // Data attributes
],
"ignoreElements": ["script", "style"],
"ignoreAttributes": ["className", "id", "data-testid"]
}]
}@intl-party/no-missing-keys
Catches references to translation keys that don't exist in your translation files.
❌ Incorrect
function Component() {
const t = useTranslations("common");
return <h1>{t("nonExistentKey")}</h1>; // Key doesn't exist
}✅ Correct
function Component() {
const t = useTranslations("common");
return <h1>{t("welcome")}</h1>; // Key exists in common namespace
}Configuration
{
"@intl-party/no-missing-keys": ["error", {
"translationsPath": "./messages",
"defaultLocale": "en",
"namespaces": ["common", "navigation"],
"checkDynamicKeys": false
}]
}@intl-party/prefer-translation-hooks
Encourages using translation hooks instead of direct i18n instance usage in React components.
❌ Incorrect
function Component() {
const { i18n } = useI18nContext();
return <h1>{i18n.t("welcome")}</h1>; // Direct i18n usage
}✅ Correct
function Component() {
const t = useTranslations("common");
return <h1>{t("welcome")}</h1>; // Using translation hook
}Configuration
{
"@intl-party/prefer-translation-hooks": ["warn", {
"allowedMethods": ["formatDate", "formatNumber"],
"ignoreServerComponents": true
}]
}Configuration Presets
Recommended Preset
// Balanced rules for most projects
{
"extends": ["@intl-party/recommended"]
}Includes:
@intl-party/no-hardcoded-strings:error@intl-party/no-missing-keys:error@intl-party/prefer-translation-hooks:warn
Strict Preset
// Stricter rules for high-quality i18n
{
"extends": ["@intl-party/strict"]
}Includes all recommended rules plus:
- Stricter hardcoded string detection
- Enforcement of namespace consistency
- Required translation comments
TypeScript Preset
// Additional rules for TypeScript projects
{
"extends": [
"@intl-party/recommended",
"@intl-party/typescript"
]
}Includes type-aware rules and TypeScript-specific checks.
Advanced Configuration
Project-Specific Settings
// .eslintrc.js
module.exports = {
plugins: ["@intl-party"],
settings: {
"intl-party": {
// Path to translation files
translationsPath: "./src/locales",
// Default locale for key validation
defaultLocale: "en",
// Available namespaces
namespaces: ["common", "navigation", "forms"],
// Translation file pattern
filePattern: "{locale}/{namespace}.json",
// Ignore patterns for hardcoded strings
ignorePatterns: [
"^[A-Z_]+$", // Constants
"^\\d+$", // Numbers
"^https?://", // URLs
"^mailto:", // Email links
"^tel:", // Phone links
],
// Elements to ignore for hardcoded strings
ignoreElements: ["script", "style", "code", "pre"],
// Attributes to ignore
ignoreAttributes: ["className", "id", "data-*", "aria-*"],
},
},
rules: {
"@intl-party/no-hardcoded-strings": "error",
"@intl-party/no-missing-keys": "error",
"@intl-party/prefer-translation-hooks": "warn",
},
};Framework-Specific Configuration
Next.js
// .eslintrc.js
module.exports = {
extends: ["next/core-web-vitals", "@intl-party/recommended"],
settings: {
"intl-party": {
translationsPath: "./messages",
framework: "nextjs",
},
},
};React
// .eslintrc.js
module.exports = {
extends: ["react-app", "@intl-party/recommended"],
settings: {
"intl-party": {
translationsPath: "./src/translations",
framework: "react",
},
},
};Integration with Build Tools
CI/CD Integration
# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run lint
- run: npm run lint:i18n # Custom script for i18n-specific lintingPre-commit Hooks
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"eslint --fix",
"eslint --ext .js,.jsx,.ts,.tsx --config .eslintrc.i18n.js"
]
}
}Custom Scripts
// package.json
{
"scripts": {
"lint": "eslint src/",
"lint:i18n": "eslint src/ --config .eslintrc.i18n.js",
"lint:fix": "eslint src/ --fix"
}
}Troubleshooting
Common Issues
False positives for hardcoded strings
- Add patterns to
ignorePatternsin rule configuration - Use
eslint-disable-next-linecomments for specific cases
- Add patterns to
Missing key errors for dynamic keys
- Set
checkDynamicKeys: falsein rule configuration - Use template strings for predictable patterns
- Set
Performance issues with large translation files
- Use
translationsPathsetting to optimize file loading - Consider splitting large translation files
- Use
Debug Mode
Enable debug logging to troubleshoot rule issues:
DEBUG=eslint-plugin-intl-party eslint src/Custom Rule Configuration
// For specific files or patterns
{
"overrides": [
{
"files": ["**/*.test.{js,jsx,ts,tsx}"],
"rules": {
"@intl-party/no-hardcoded-strings": "off"
}
},
{
"files": ["**/admin/**"],
"rules": {
"@intl-party/no-missing-keys": "warn"
}
}
]
}Examples
Real-world Configuration
// .eslintrc.js for a Next.js project
module.exports = {
extends: ["next/core-web-vitals", "@intl-party/recommended"],
settings: {
"intl-party": {
translationsPath: "./messages",
defaultLocale: "en",
namespaces: ["common", "navigation", "forms", "errors"],
ignorePatterns: [
"^[A-Z_]+$",
"^\\d+(\\.\\d+)?$",
"^#[0-9a-fA-F]{3,6}$",
"^rgb\\(",
"^https?://",
"^mailto:",
"^\\+\\d",
],
},
},
overrides: [
{
files: ["**/*.test.{js,jsx,ts,tsx}", "**/*.stories.{js,jsx,ts,tsx}"],
rules: {
"@intl-party/no-hardcoded-strings": "off",
},
},
{
files: ["**/admin/**", "**/cms/**"],
rules: {
"@intl-party/no-hardcoded-strings": "warn",
},
},
],
};License
MIT © IntlParty
