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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@sanity/eslint-plugin-i18n

v1.1.0

Published

This ESLint plugin provides rules to enforce specific code standards in internationalization practices, particularly focusing on attribute strings and template literals in JSX.

Downloads

14,466

Readme

@sanity/eslint-plugin-i18n

This ESLint plugin provides rules to enforce specific code standards in internationalization practices, particularly focusing on attribute strings and template literals in JSX. It aims to improve code quality and maintainability in projects with internationalization concerns.

Installation

👋 Note: We highly recommend installing @sanity/eslint-config-i18n instead which comes bundled with other plugins that are useful for localizing your Studio.

Ensure ESLint is installed and initialized then install the following package:

npm install @sanity/eslint-plugin-i18n --save-dev

Then update your eslint configuration:

// .eslintrc.cjs
module.exports = {
  plugins: ['@sanity/i18n'],
  rules: {
    '@sanity/i18n/no-attribute-string-literals': 'error',
    '@sanity/i18n/no-attribute-template-literals': 'error',
    '@sanity/i18n/no-i18next-import': 'error',
  },
};

Rules

no-attribute-string-literals

This rule finds string literals within JSX attributes. These attribute can be nested within ternary expression and logical expressions.

Note: This rule does not report if there is an expression within template literals as this is what no-attribute-template-literals is for.

Examples of incorrect code for this rule:

// 🛑 JSX literal
<MyComponent prop="some string literal">
// 🛑 string literal inside of a JSX expression container
<MyComponent prop={'some string literal'}>
// 🛑 template literal with no expressions in a JSX expression container
<MyComponent prop={`some string literal`}>
// 🛑 string literals nested within a ternary
<MyComponent prop={conditional ? 'nested literal' : 'another literal'}>
// 🛑 string literals nested within a logical expression
<MyComponent prop={nullishValue ?? 'fallback literal'}>

Examples of correct code for this rule:

// ✅ function call
<MyComponent text={t('some.localized.text')}>
// ✅ not string literals
<MyComponent booleanAttr numberAttr={5}>

no-attribute-template-literals

This rule finds template literals with expressions within JSX attributes. These attribute can be nested within ternary expression and logical expressions.

Note: With the default configuration, this rule will only report if there is whitespace within a template element. See usage below to change this behavior.

Examples of incorrect code for this rule:

// 🛑 template literal with an expression and whitespace
<MyComponent prop={`Hello ${name}!`}>
// 🛑 template literals with expressions nested within a ternary
<MyComponent prop={conditional ? `Hello ${name}` : `Goodbye ${name}`}>
// 🛑 template literals with expression nested within a logical expression
<MyComponent prop={nullishValue ?? `Hello ${name}`}>

Examples of correct code for this rule:

// ✅ does not contain an expression (this will break the other rule)
<MyComponent text={`no expression`}>
// ✅ no whitespace in the template element (with the default config)
<MyComponent id={`prefix-${id}`}>
// ✅ not string literals
<MyComponent booleanAttr numberAttr={5}>

no-i18next-import

This rule finds imports of the i18next and react-i18next package. While these packages are (currently) used as dependencies of sanity, this is considered an implementation detail and should not be relied upon. Instead, you should import any i18n utility/helper from the sanity module directly.

Examples of incorrect code for this rule:

// 🛑 importing from `react-i18next``
import { useTranslation } from 'react-i18next';
// 🛑 requiring from `react-i18next``
const { Trans } = require('react-i18next');

Examples of correct code for this rule:

// ✅ requiring from `sanity`
const { Translate } = require('sanity');

Usage

In your ESLint config, add "@sanity/i18n" to the list of plugins and enable the rules:

// .eslintrc.cjs
module.exports = {
  plugins: ['@sanity/i18n'],
  rules: {
    '@sanity/i18n/no-attribute-string-literals': [
      'error',
      // these take in the same options.
      // you may omit this if you don't want to configure any more options.
      {
        ignores: {
          // ...
        },
        only: {
          // ...
        },
        mode: 'extend',
      },
    ],
    '@sanity/i18n/no-attribute-template-literals': [
      'error',
      // these take in the same options.
      // you may omit this if you don't want to configure any more options.
      {
        ignores: {
          // ...
        },
        only: {
          // ...
        },
        mode: 'extend',
      },
    ],
    '@sanity/i18n/no-i18next-import': 'error',
  },
};

Rule options

| Option | Description | Type | Default value | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ---------------------------- | | mode | If the mode is specified as override the default configuration for ignores and only will be overridden by the user configuration. The default behavior is to extend. | 'extend' \| 'override' | 'extend' | | only | If specified, only attributes matching its conditions will be considered. If omitted, all attributes are initially selected. | InputOption | See here | | ignores | This is the primary filter used to specify exceptions or conditions for which attributes should not be reported. Attributes matching its conditions are excluded from being reported. | InputOption | See here |

Note: If the only option is present with ignores, the only option will determine which attributes to consider and then the ignores option is used to ignore any attributes that match.

InputOption

The only and ignores options take in this InputOption type shown below. Note how you can combine logic expressions to consider and ignore different attributes.

type InputOption =
  | { and: InputOption[] }
  | { or: InputOption[] }
  | { not: InputOption }
  | {
      /**
       * List of attributes to match.
       *
       * For instance, to match the attribute `foo` in `<Component foo="text" />`,
       * add "foo" to this list.
       */
      attributes?: string[];

      /**
       * Regex patterns for attribute names. (case insensitive)
       *
       * E.g., to match all `data-` attributes in `<Component data-testid="a" />`,
       * use the pattern "^data-\w+".
       */
      attributePatterns?: string[];

      /**
       * List of string literals or template element values to match.
       *
       * For instance, to match the value `hello ` in `<Component text={`hello ${name}`} />`,
       * add `'hello '` to this list.
       */
      values?: string[];

      /**
       * Regex patterns to match string literals or template element values.
       * (case insensitive)
       *
       * For instance, to match the value `id:` in `<Component text={`id:${name}`} />`,
       * add `'id:'` to this list.
       */
      valuePatterns?: string[];

      /**
       * List of component names to match.
       *
       * For instance, to match the `<Button />` component,
       * add "Button" to this list.
       */
      components?: string[];

      /**
       * Regex patterns for component names. (case insensitive)
       *
       * E.g., to match any component with "Btn" in its name like `<SubmitBtn />` or `<CancelBtn />`,
       * use the pattern ".*btn".
       */
      componentPatterns?: string[];
    };

There are three logical operators to aid in crafting your criteria:

  • OR (or): Any condition within the or block being true makes the entire block true.
  • AND (and): All conditions within the and block must be true for the entire block to be true.
  • NOT (not): Inverts the result of its condition.

When multiple keys are present in the input object, they follow the "OR" logic. If you desire an "AND" relationship, use the and condition explicitly. For example, to enforce that an attribute must be from the Button component AND named 'as', you'd use:

{
  "and": [{ "components": ["Button"] }, { "attributes": ["as"] }]
}

See the defaults for more examples.

Additionally the default options can be imported via @sanity/eslint-plugin-i18n/defaults

const {
  defaultNoAttributeTemplateLiteralsOptions,
  defaultNoAttributeStringLiteralsOptions,
} = require('@sanity/eslint-plugin-i18n/defaults');