Add i18n to Kibana React components and server code using @kbn/i18n and @kbn/i18n-react. Use when internationalizing UI strings, adding FormattedMessage, calling i18n.translate, naming message IDs, or working with Kibana translation files.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Add i18n to Kibana React components and server code using @kbn/i18n and @kbn/i18n-react. Use when internationalizing UI strings, adding FormattedMessage, calling i18n.translate, naming message IDs, or working with Kibana translation files.
All user-visible text in React components must be internationalized. No raw string literals in JSX output, props like title, placeholder, aria-label, button labels, error messages, tooltips, or any other text rendered to the screen.
API
i18n.translate (non-JSX contexts)
The id is a positional first argument, not inside the options object:
When a translated string contains JSX elements (bold, links, etc.), two patterns are available:
Tag chunking (preferred for wrapping text)
XML-like tags in defaultMessage map to functions in values that receive the inner content as chunks:
<FormattedMessage
id="myPlugin.myFeature.benefitTitle"
defaultMessage="<bold>Focused navigation</bold>. Only the tools you need."
values={{
bold: (chunks) =><strong>{chunks}</strong>,
}}
/>
<FormattedMessageid="myPlugin.myFeature.backgroundSearchLabel"defaultMessage='"{name}" is running. <link>Check progress here.</link>'values={{name,
link: (chunks) =><EuiLinkhref={url}>{chunks}</EuiLink>,
}}
/>
Nested FormattedMessage (for inner strings that need their own translation)
Use when the interpolated value is a standalone translated string, not just a wrapper:
<FormattedMessage
id="myPlugin.myFeature.confirmDescription"
defaultMessage="This will delete {strongCount} and cannot be undone."
values={{
strongCount: (
<strong><FormattedMessageid="myPlugin.myFeature.confirmDescription.strongCountDetail"defaultMessage="{count, plural, one {# item} other {# items}}"values={{count }}
/></strong>
),
}}
/>
Inner IDs follow the pattern: {parentId}.{variableName}{TypeSuffix} — use the same type suffix table (e.g., LinkText for a link, Label for a label). Only use Detail when no specific suffix applies.
Static Analysis Constraints
The extraction tooling parses AST -- these must be static literals:
id -- string literal only (no variables, no template literals)
defaultMessage -- must be a static string (string literal, template literal without expressions, or string concatenation). The content can include ICU syntax, tag chunks like <bold>...</bold>, etc.; the constraint is on the variable form, not the content format.
values keys must match {placeholders} and <tags> in defaultMessage exactly
// WRONG -- dynamic id
i18n.translate(`myPlugin.${key}Label`, { defaultMessage: 'Hello' });
// WRONG -- values/defaultMessage mismatch
i18n.translate('myPlugin.greetingLabel', {
defaultMessage: 'Hello, {name}',
values: { userName }, // key must be `name`, not `userName`
});
Splitting Rules
Never split a single sentence across multiple IDs -- context for translators is lost.
Never separate sentences that reference each other (e.g. pronouns like "close it").
For unavoidable large paragraphs, split at logical boundaries where each chunk is self-contained.
Validation
After adding or modifying i18n strings, run:
node scripts/i18n_check --fix
Legacy Patterns (avoid in new code)
injectI18n HOC -- still exported but discouraged. Use i18n.translate() or <FormattedMessage> instead.
intl.formatMessage() via useIntl / injected props -- use i18n.translate() directly (no provider dependency needed for imperative calls).
FormattedPlural -- exported but unused in the codebase. Use ICU {count, plural, ...} in defaultMessage instead.