fluent-react

The preset for @fluent/react. It reads Fluent .ftl files, <Localized> elements and getString calls.

The fluent-react preset supports @fluent/react, the React binding of Project Fluent.

Detection

The preset applies when the project depends on @fluent/react.

x18n.config.ts
import { defineConfig } from '@x18n/core'

export default defineConfig({
  presets: ['fluent-react'],
})

Locale files

If your config has no resources, the preset uses public/locales/{locale}/*.ftl, locales/{locale}.ftl and src/locales/{locale}.ftl. The ftl format reads and writes these files. All .ftl files of a locale share one set of keys, as in a Fluent bundle.

The ftl format reads a message attribute as a key of its own: .title on the message login is the key login.title. When a message is used, its attributes count as used too, since <Localized id="login" attrs={{ title: true }}> reads them.

Supported syntax

The id prop of <Localized> is a key. These names count as translate calls: l10n.getString, props.getString and this.props.getString. useLocalization() returns l10n, and withLocalization gives the getString prop.

A bare getString('key') call, for example from function Bye({ getString }), is not read: x18n can't tell it apart from other functions named getString. Call it as props.getString, or add getString to usage.calls in your config.

src/App.tsx
function Greeting({ name }) {
  const { l10n } = useLocalization()
  return (
    <Localized id="hello" vars={{ name }}>
      <p title={l10n.getString('hello-title')}>Hello</p>
    </Localized>
  )
}

function Bye(props) {
  return <p>{props.getString('bye')}</p>
}

export default withLocalization(Bye)

Placeholders

Fluent variables: { $name }. For hello = Hello { $name } in en and hello = Hallo in de, the placeholder-mismatch check reports hello.

Extraction templates

KindsTemplateImports
jsx-text<Localized id="{key}" />import { Localized } from '@fluent/react'
jsx-attribute{attr}={l10n.getString('{key}'{args})}
js-string, js-templatel10n.getString('{key}'{args})

<Localized> without children renders the message as text. The l10n templates need const { l10n } = useLocalization() in the component. A string outside a component, such as a module-level constant, has no l10n: move it into the component before you extract it.