Zum Hauptinhalt springen

ESLint-Plugin für FormatJS

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Dieses ESLint-Plugin ermöglicht es Ihnen, bestimmte Regeln für Ihre ICU-Nachrichten durchzusetzen.

Verwendung

npm i -D eslint-plugin-formatjs

Dann in Ihrer ESLint-Konfiguration:

import formatjs from 'eslint-plugin-formatjs'

export default [
// other configs...
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-offset': 'error',
},
},
]

React

Aktuell werden intl.formatMessage, defineMessage, defineMessages und <FormattedMessage> aus react-intl als Hooks zur Nachrichtenvalidierung verwendet. Verwenden Sie daher in Ihrem Code einen der folgenden Mechanismen:

import {defineMessages, defineMessage} from 'react-intl'

const messages = defineMessages({
foo: {
defaultMessage: 'foo',
description: 'bar',
},
})

defineMessage({
defaultMessage: 'single message',
})
import {FormattedMessage} from 'react-intl'
;<FormattedMessage defaultMessage="foo" description="bar" />
function foo() {
intl.formatMessage({
defaultMessage: 'foo',
})
}

Vue

Hiermit werden Aufrufe von intl.formatMessage und $formatMessage sowohl in JS/TS-Dateien als auch in SFC .vue-Dateien überprüft. Beispiel:

<template>
<p>
{{
$formatMessage({
defaultMessage: 'today is {now, date}',
})
}}
</p>
</template>

Gemeinsame Einstellungen

Diese Einstellungen gelten global für alle FormatJS-Regeln, sobald sie festgelegt wurden. Details zur Konfiguration finden Sie unter Gemeinsame Einstellungen.

formatjs.additionalFunctionNames

Ähnlich wie bei babel-plugin-formatjs und @formatjs/ts-transformer können Sie hiermit zusätzliche Funktionsnamen zur Überprüfung neben formatMessage und $formatMessage festlegen.

formatjs.additionalComponentNames

Ähnlich wie bei babel-plugin-formatjs und @formatjs/ts-transformer können Sie hiermit zusätzliche Komponentennamen zur Überprüfung neben FormattedMessage festlegen.

Teilbare Konfigurationen

Das Plugin bietet folgende zwei teilbare Konfigurationen:

  1. recommended

  2. strict

Durch deren Verwendung können Sie Ihre Konfiguration vereinfachen und gleichzeitig einen Regelsatz verwenden, der Ihren Qualitätsstandards entspricht.

Beispiel

import formatjs from 'eslint-plugin-formatjs'

export default [
formatjs.configs.recommended,
// Other configs...
]

Verfügbare Regeln

blocklist-elements

Blockiert die Verwendung bestimmter Elemente in ICU-Nachrichten.

Gründe

  • Bestimmte Übersetzungsdienstleister können Elemente wie selectordinal nicht verarbeiten

Verfügbare Elemente

enum Element {
// literal text, like `defaultMessage: 'some text'`
literal = 'literal',
// placeholder, like `defaultMessage: '{placeholder} var'`
argument = 'argument',
// number, like `defaultMessage: '{placeholder, number} var'`
number = 'number',
// date, like `defaultMessage: '{placeholder, date} var'`
date = 'date',
// time, like `defaultMessage: '{placeholder, time} var'`
time = 'time',
// select, like `defaultMessage: '{var, select, foo{one} bar{two}} var'`
select = 'select',
// selectordinal, like `defaultMessage: '{var, selectordinal, one{one} other{two}} var'`
selectordinal = 'selectordinal',
// plural, like `defaultMessage: '{var, plural, one{one} other{two}} var'`
plural = 'plural',
}

Beispiel

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/blocklist-elements': [2, ['selectordinal']],
},
},
]

enforce-description

Erzwingt die Angabe von description im Nachrichtendeskriptor.

Gründe

  • Beschreibungen liefern wertvolle Kontextinformationen für Übersetzer
import {defineMessages} from 'react-intl'

const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'foo',
description: 'bar',
},
// FAILS
bar: {
defaultMessage: 'bar',
},
})

Optionen

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-description': ['error', 'literal'],
},
},
]

Die Option literal erzwingt, dass description immer ein String-Literal sein muss (keine Funktionsaufrufe oder Variablen). Dies ist hilfreich für Extraktionstools, die description als Literal erwarten.

enforce-default-message

Erzwingt defaultMessage im Nachrichtendeskriptor.

Gründe

  • Nützlich, wenn Nachrichten aus dem Quellcode für Übersetzungen extrahiert werden sollen. Stellt sicher, dass defaultMessage nicht vergessen wird.
import {defineMessages} from 'react-intl'

const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'This is default message',
description: 'bar',
},
// FAILS
bar: {
description: 'bar',
},
})

Optionen

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-default-message': ['error', 'literal'],
},
},
]

Die Option literal erzwingt, dass defaultMessage immer ein String-Literal sein muss (keine Funktionsaufrufe oder Variablen). Dies ist hilfreich für Extraktionstools, die defaultMessage als Literal erwarten.

enforce-placeholders

Stellt sicher, dass alle Werte übergeben werden, wenn eine Nachricht Platzhalter (number/date/time/plural/select/selectordinal) enthält. Erfordert, dass Werte als Literal-Objekt (keine Variable) übergeben werden.

// WORKS, no error
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={{placeholder: 'dog'}}
/>

// WORKS, no error
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, {placeholder: 'dog'})

// WORKS, error bc no values were provided
<FormattedMessage
defaultMessage="this is a {placeholder}"
/>

// WORKS, error bc no values were provided
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
})

// WORKS, error bc `placeholder` is not passed in
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={{foo: 1}}
/>

// WORKS, error bc `placeholder` is not passed in
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, {foo: 1})

// DOESN'T WORK
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={someVar}
/>

// DOESN'T WORK
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, values)

Optionen

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-placeholders': [
'error',
{
ignoreList: ['foo'],
},
],
},
},
]
  • ignoreList: Liste der zu ignorierenden Platzhalternamen. Funktioniert mit defaultRichTextElements in react-intl, um falsch positive Ergebnisse bei globaler Tag-Formatierung zu vermeiden.

enforce-plural-rules

Erzwingt, dass bestimmte Pluralregeln in einer Nachricht immer angegeben oder verboten sind.

Gründe

  • Es wird empfohlen, other stets als Fallback in der Nachricht anzugeben.

  • Einige Übersetzungsdienste akzeptieren nur bestimmte Regeln.

Verfügbare Regeln

enum LDML {
zero = 'zero',
one = 'one',
two = 'two',
few = 'few',
many = 'many',
other = 'other',
}

Beispiel

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-plural-rules': [
2,
{
one: true,
other: true,
zero: false,
},
],
},
},
]

no-camel-case

Stellt sicher, dass Platzhalter nicht in Camel-Case geschrieben werden.

Gründe

Warum

import {defineMessages} from 'react-intl'

const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'foo {snake_case} {nothing}',
},
// FAILS
bar: {
defaultMessage: 'foo {camelCase}',
},
})

no-missing-icu-plural-one-placeholders

Nachrichten wie {thing, plural, one {1 thing} other {# things}} müssen geändert werden in {thing, plural, one {# thing} other {# things}}.

Gründe

  • one ist eine Kategorie für Zahlen, die sich wie 1 verhalten. In einigen Sprachen wie Ukrainisch, Russisch und Polnisch gilt one → Zahlen, die auf 1 enden (z.B. 1, 21, 151), aber nicht auf 11 (z.B. 11, 111, 10311). Weitere Infos

no-emoji

Verhindert die Verwendung von Emojis (oder oberhalb einer bestimmten Unicode-Version) in Nachrichten.

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-emoji': ['error'],
},
},

// OR
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-emoji': ['error', {versionAbove: '12.0'}],
},
},
]

Gründe

  • Einige Übersetzungsdienste können keine Emojis verarbeiten.

  • Die plattformübergreifende Kodierung von Emojis ist fehleranfällig.

import {defineMessages} from 'react-intl'

const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'Smileys & People',
},
// WORKS with option {versionAbove: '12.0'}
foo_bar: {
defaultMessage: '😃 Smileys & People',
},
// FAILS
bar: {
defaultMessage: '😃 Smileys & People',
},
// FAILS with option {versionAbove: '12.0'}
bar_foo: {
defaultMessage: '🥹 Smileys & People',
},
})

no-literal-string-in-jsx

Verhindert nicht übersetzte Strings in JSX.

Gründe

  • Es wird leicht vergessen, JSX-Text in Übersetzungsfunktionen oder -komponenten zu wrappen.

  • Barrierefreiheitsattribute wie aria-label werden leicht übersehen und nicht übersetzt.

// WORKS
<Button>
<FormattedMessage defaultMessage="Submit" />
</Button>
// WORKS
<Button>
{customTranslateFn("Submit")}
</Button>
// WORKS
<input aria-label={intl.formatMessage({defaultMessage: "Label"})} />
// WORKS
<img
src="/example.png"
alt={intl.formatMessage({defaultMessage: "Image description"})}
/>
// FAILS
<Button>Submit</Button>
// FAILS
<Button>{'Submit'}</Button>
// FAILS
<Button>{`Te` + 's' + t}</Button>
// FAILS
<input aria-label="Untranslated label" />
// FAILS
<img src="/example.png" alt="Image description" />
// FAILS
<input aria-label={`Untranslated label`} />

Dieser Linter meldet Textliterale oder String-Ausdrücke einschließlich String-Verkettungen in JSX-Children. Er prüft auch anpassbare JSX-Attribute.

Beispiel

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-literal-string-in-jsx': [
2,
{
// Include or exclude additional prop checks (merged with the default checks)
props: {
include: [
// picomatch style glob pattern for tag name and prop name.
// check `name` prop of `UI.Button` tag.
['UI.Button', 'name'],
// check `message` of any component.
['*', 'message'],
],
// Exclude will always override include.
exclude: [
// do not check `message` of the `Foo` tag.
['Foo', 'message'],
// do not check aria-label and aria-description of `Bar` tag.
['Bar', 'aria-{label,description}'],
],
},
},
],
},
},
]

Standardmäßig werden folgende Props geprüft:

{
include: [
// check aria attributes that the screen reader announces.
['*', 'aria-{label,description,details,errormessage}'],
// check placeholder and title attribute of all native DOM elements.
['[a-z]*([a-z0-9])', '(placeholder|title)'],
// check alt attribute of the img tag.
['img', 'alt'],
],
exclude: []
}

no-literal-string-in-object

Verhindert nicht übersetzte Strings in ausgewählten Objekteigenschaften.

Gründe

  • Bei Objektfeldern wie {label: "Untranslated label"} wird leicht vergessen, Literale in Übersetzungsfunktionen zu wrappen.
const options = () => [
// FAILS
{value: 'chocolate', label: 'Chocolate'},
// WORKS
{
value: 'strawberry',
label: intl.formatMessage({defaultMessage: 'Strawberry'}),
},
// WORKS, custom translation function
{
value: 'mint',
label: customTranslateFn('Mint'),
},
// FAILS, string concatenation
{
value: 'coconut',
label: 'Coconut' + intl.formatMessage({defaultMessage: 'Ice Cream'}),
},
// FAILS, template literal
{
value: 'mango',
label: `Mango ${intl.formatMessage({defaultMessage: 'Ice Cream'})}`,
},
// FAILS, conditional rendering
{
value: 'recommended',
label: feelLikeSour
? intl.formatMessage({defaultMessage: 'Lime'})
: 'Vanilla',
},
]

const MyComponent = () => <Select options={options()} />

Dieser Linter meldet Textliterale oder String-Ausdrücke einschließlich Verkettungen in anpassbaren Objekteigenschaften.

Beispiel

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-literal-string-in-object': [
'warn',
{
// The object properties to check for untranslated literal strings, default: ['label']
include: ['label'],
},
],
},
},
]

no-multiple-whitespaces

Verhindert mehrere aufeinanderfolgende Leerzeichen in Nachrichten.

Gründe

  • Aufeinanderfolgende Leerzeichen werden in verschiedenen Locales unterschiedlich behandelt.

  • Verhindert \-Zeilenumbrüche in JS-Strings, die zu unerwünschten Leerzeichen führen.

import {defineMessages} from 'react-intl'

const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'Smileys & People',
},
// FAILS
bar: {
defaultMessage: 'Smileys & People',
},
// FAILS
baz: {
defaultMessage:
'this message is too long \
so I wanna line break it.',
},
})

no-multiple-plurals

Verhindert mehrere Pluralformen in einer Nachricht.

Gründe

  • Verschachtelte Pluralformen sind sprachübergreifend schwer zu übersetzen und werden von einigen Diensten nicht unterstützt.
import {defineMessages} from 'react-intl'

const messages = defineMessages({
// WORKS
foo: {
defaultMessage: '{p1, plural, one{one}}',
},
// FAILS
bar: {
defaultMessage: '{p1, plural, one{one}} {p2, plural, one{two}}',
}
// ALSO FAILS
bar2: {
defaultMessage: '{p1, plural, one{{p2, plural, one{two}}}}',
}
})

no-offset

Verhindert die Angabe von Offsets in Pluralregeln.

Gründe

  • Offsets haben komplexe logische Implikationen und werden von einigen Übersetzungsdiensten nicht unterstützt.
import {defineMessages} from 'react-intl'

const messages = defineMessages({
// PASS
foo: {
defaultMessage: '{var, plural, one{one} other{other}}',
},
// FAILS
bar: {
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
})

enforce-id

Erzwingt eine generierte ID im MessageDescriptor.

Gründe

Pipelines können die automatische/manuelle ID-Generierung auf Linter-Ebene erzwingen (Autofix zum Einfügen automatisch generierter IDs), wodurch dies gewährleistet wird.

import {defineMessages} from 'react-intl';

const messages = defineMessages({
// PASS
foo: {
id: '19shaf'
defaultMessage: '{var, plural, one{one} other{other}}',
},
// FAILS
bar: {
id: 'something',
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
// FAILS
bar: {
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
});

Optionen

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-id': [
'error',
{
idInterpolationPattern: '[sha512:contenthash:base64:6]',
},
],
},
},
]
  • idInterpolationPattern: Muster, gegen das die ID geprüft wird

  • idWhitelist: Ein Array von Strings mit regulären Ausdrücken. Dieses Array erlaubt benutzerdefinierte IDs für Nachrichten in einer Allowlist. Beispielsweise erlaubt '\\.' IDs mit Punkten; '^payment_.*' erlaubt benutzerdefinierte IDs mit dem Präfix payment_. Beachten Sie: Jeder Backslash \ in Strings muss mit einem zusätzlichen Backslash maskiert werden.

no-invalid-icu

Verbietet syntaktisch ungültige Strings innerhalb von defaultMessage.

Gründe

Es ist leicht, Strings zu übersehen, die für Entwickler korrekt aussehen, aber tatsächlich syntaktisch ungültige ICU-Strings sind. Beispielsweise würde Folgendes einen ESLint-Fehler verursachen:

formatMessage(
{
defaultMessage: '{count, plural one {#} other {# more}}', //Missing a comma!
},
{
count: 1,
}
)

no-id

Dies verbietet explizite IDs in MessageDescriptor.

Gründe

Wir empfehlen generell automatische ID-Generierung aus diesen Gründen. Diese Regel stellt sicher, dass keine expliziten IDs gesetzt werden.

no-complex-selectors

Stellt sicher, dass ein Satz nicht zu komplex ist. Die Komplexität wird durch die Anzahl der erzeugten Strings bestimmt, wenn wir den Satz basierend auf seinen Selektoren vereinfachen. Beispiel:

I have {count, plural, one{a dog} other{many dogs}}

hat eine Komplexität von 2, da die Vereinfachung des Plural-Selektors 2 Sätze ergibt: I have a dog & I have many dogs. Die Standard-Komplexitätsgrenze liegt bei 20 (basierend auf Smartling als Referenz)

Optionen

import formatjs from 'eslint-plugin-formatjs'

export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-complex-selectors': [
'error',
{
limit: 3,
},
],
},
},
]

no-useless-message

Dies verbietet Nachrichten, die keine Übersetzung erfordern.

Gründe

Nachrichten wie {test} sind für Übersetzer nicht handhabbar. Der Code sollte stattdessen direkt auf test verweisen.

prefer-formatted-message

Verwenden Sie <FormattedMessage> statt des imperativen intl.formatMessage(...), falls anwendbar.

// Bad
<p>
{intl.formatMessage({defaultMessage: 'hello'})}
</p>

// Good
<p>
<FormattedMessage defaultMessage="hello" />
</p>

Gründe

Einheitlicher Codierstil in JSX und weniger Syntaxüberladenung.

prefer-pound-in-plural

Verwenden Sie # im Plural-Argument, um auf die Anzahl zu verweisen, statt das Argument zu wiederholen.

// Bad
I have {count} {
count, plural,
one {apple}
other {apples}
}
}
// Good
I have {
count, plural,
one {# apple}
other {# apples}
}
}

// Bad
I have {
count, plural,
one {{count} apple}
other {{count} apples}
}
}
// Good
I have {
count, plural,
one {# apple}
other {# apples}
}
}

// Bad
I won the {ranking}{
count, selectordinal,
one {st}
two {nd}
few {rd}
other {th}
} place.
// Good
I won the {ranking}{
count, selectordinal,
one {#st}
two {#nd}
few {#rd}
other {#th}
} place.

Gründe

  1. Prägnantere Nachricht.

  2. Stellt sicher, dass Anzahlen korrekt als Zahlen formatiert werden.