npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@intl-party/eslint-plugin

v1.0.2

Published

ESLint plugin for IntlParty - detect hardcoded strings and enforce i18n best practices

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-plugin

Configuration

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 linting

Pre-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

  1. False positives for hardcoded strings

    • Add patterns to ignorePatterns in rule configuration
    • Use eslint-disable-next-line comments for specific cases
  2. Missing key errors for dynamic keys

    • Set checkDynamicKeys: false in rule configuration
    • Use template strings for predictable patterns
  3. Performance issues with large translation files

    • Use translationsPath setting to optimize file loading
    • Consider splitting large translation files

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