- Lingui Best Practices
- Lingui is a powerful internationalization (i18n) framework for JavaScript. This skill covers best practices for implementing i18n in React and vanilla JavaScript applications.
- Quick Start Workflow
- The standard Lingui workflow consists of these steps:
- Wrap your app in
- I18nProvider
- Mark messages for translation using macros (
- Trans
- ,
- t
- , etc.)
- Extract messages:
- lingui extract
- Translate the catalogs
- Compile catalogs:
- lingui compile
- Load and activate locale in your app
- Core Packages
- Import from these packages:
- // React macros (recommended)
- import
- {
- Trans
- ,
- Plural
- ,
- Select
- ,
- useLingui
- }
- from
- "@lingui/react/macro"
- ;
- // Core macros for vanilla JS
- import
- {
- t
- ,
- msg
- ,
- plural
- ,
- select
- }
- from
- "@lingui/core/macro"
- ;
- // Runtime (rarely used directly)
- import
- {
- I18nProvider
- }
- from
- "@lingui/react"
- ;
- import
- {
- i18n
- }
- from
- "@lingui/core"
- ;
- Setup I18nProvider
- Wrap your application with
- I18nProvider
- :
- import
- {
- I18nProvider
- }
- from
- "@lingui/react"
- ;
- import
- {
- i18n
- }
- from
- "@lingui/core"
- ;
- import
- {
- messages
- }
- from
- "./locales/en/messages"
- ;
- i18n
- .
- load
- (
- "en"
- ,
- messages
- )
- ;
- i18n
- .
- activate
- (
- "en"
- )
- ;
- function
- App
- (
- )
- {
- return
- (
- <
- I18nProvider
- i18n
- =
- {
- i18n
- }
- >
- {
- / Your app /
- }
- </
- I18nProvider
- >
- )
- ;
- }
- Translating UI Text
- Use Trans for JSX Content
- The
- Trans
- macro is the primary way to translate JSX:
- import
- {
- Trans
- }
- from
- "@lingui/react/macro"
- ;
- // Simple text
- <
- Trans
- >
- Hello World
- </
- Trans
- >
- // With variables
- <
- Trans
- >
- Hello
- {
- userName
- }
- </
- Trans
- >
- // With components (rich text)
- <
- Trans
- >
- Read the
- <
- a
- href
- =
- "
- /docs
- "
- >
- documentation
- </
- a
- >
- for more info.
- </
- Trans
- >
- // Extracted as: "Read the <0>documentation</0> for more info."
- When to use
-
- For any translatable text in JSX elements.
- Use useLingui for Non-JSX
- For strings outside JSX (attributes, alerts, function calls):
- import
- {
- useLingui
- }
- from
- "@lingui/react/macro"
- ;
- function
- MyComponent
- (
- )
- {
- const
- {
- t
- }
- =
- useLingui
- (
- )
- ;
- const
- handleClick
- =
- (
- )
- =>
- {
- alert
- (
- t
- `
- Action completed!
- `
- )
- ;
- }
- ;
- return
- (
- <
- div
- >
- <
- img
- src
- =
- "
- ...
- "
- alt
- =
- {
- t
- `
- Image description
- `
- }
- />
- <
- button
- onClick
- =
- {
- handleClick
- }
- >
- {
- t
- `
- Click me
- `
- }
- </
- button
- >
- </
- div
- >
- )
- ;
- }
- When to use
-
- Element attributes, alerts, function parameters, any non-JSX string.
- Use msg for Lazy Translations
- When you need to define messages at module level or in arrays/objects:
- import
- {
- msg
- }
- from
- "@lingui/core/macro"
- ;
- import
- {
- useLingui
- }
- from
- "@lingui/react"
- ;
- // Module-level constants
- const
- STATUSES
- =
- {
- active
- :
- msg
- `
- Active
- `
- ,
- inactive
- :
- msg
- `
- Inactive
- `
- ,
- pending
- :
- msg
- `
- Pending
- `
- ,
- }
- ;
- function
- StatusList
- (
- )
- {
- const
- {
- _
- }
- =
- useLingui
- (
- )
- ;
- return
- Object
- .
- entries
- (
- STATUSES
- )
- .
- map
- (
- (
- [
- key
- ,
- message
- ]
- )
- =>
- (
- <
- div
- key
- =
- {
- key
- }
- >
- {
- _
- (
- message
- )
- }
- </
- div
- >
- )
- )
- ;
- }
- When to use
- Module-level constants, arrays of messages, conditional message selection. Pluralization Use the Plural macro for quantity-dependent messages: import { Plural } from "@lingui/react/macro" ; < Plural value = { messageCount } one = " You have # message " other = " You have # messages " /> The
placeholder is replaced with the actual value. Exact Matches Use _N syntax for exact number matches (takes precedence over plural forms): < Plural value = { count } _0 = " No messages " one = " One message " other = "
messages
"
/>
With Variables and Components
Combine with
Trans
for complex messages:
<
Plural
value
=
{
count
}
one
=
{
You have # message,
${
userName
}
}
other
=
{
<
Trans
You have < strong
- </
- strong
- >
- messages,
- {
- userName
- }
- </
- Trans
- >
- }
- />
- Formatting Dates and Numbers
- Use
- i18n.date()
- and
- i18n.number()
- for locale-aware formatting:
- import
- {
- useLingui
- }
- from
- "@lingui/react/macro"
- ;
- function
- MyComponent
- (
- )
- {
- const
- {
- i18n
- }
- =
- useLingui
- (
- )
- ;
- const
- lastLogin
- =
- new
- Date
- (
- )
- ;
- return
- (
- <
- Trans
- >
- Last login:
- {
- i18n
- .
- date
- (
- lastLogin
- )
- }
- </
- Trans
- >
- )
- ;
- }
- These use the browser's
- Intl
- API for proper locale formatting.
- Message IDs and Context
- Explicit IDs
- Provide a custom ID for stable message keys:
- <
- Trans
- id
- =
- "
- header.welcome
- "
- >
- Welcome to our app
- </
- Trans
- >
- Context for Disambiguation
- When the same text has different meanings, use
- context
- :
- <
- Trans
- context
- =
- "
- direction
- "
- >
- right
- </
- Trans
- >
- <
- Trans
- context
- =
- "
- correctness
- "
- >
- right
- </
- Trans
- >
- These create separate catalog entries.
- Comments for Translators
- Add context for translators:
- <
- Trans
- comment
- =
- "
- Greeting shown on homepage
- "
- >
- Hello World
- </
- Trans
- >
- Configuration
- Basic
- lingui.config.js
- :
- import
- {
- defineConfig
- }
- from
- "@lingui/cli"
- ;
- export
- default
- defineConfig
- (
- {
- sourceLocale
- :
- "en"
- ,
- locales
- :
- [
- "en"
- ,
- "es"
- ,
- "fr"
- ,
- "de"
- ]
- ,
- catalogs
- :
- [
- {
- path
- :
- "
/src/locales/{locale}/messages" - ,
- include
- :
- [
- "src"
- ]
- ,
- exclude
- :
- [
- "/node_modules/"
- ]
- ,
- }
- ,
- ]
- ,
- }
- )
- ;
- For detailed configuration patterns, see
- configuration.md
- .
- Best Practices
- Always Use Macros
- Prefer macros over runtime components. Macros are compiled at build time, reducing bundle size:
- // ✅ Good - uses macro
- import
- {
- Trans
- }
- from
- "@lingui/react/macro"
- ;
- // ❌ Avoid - runtime only
- import
- {
- Trans
- }
- from
- "@lingui/react"
- ;
- Keep Messages Simple
- Avoid complex expressions in messages - they'll be replaced with placeholders:
- // ❌ Bad - loses context
- <
- Trans
- >
- Hello
- {
- user
- .
- name
- .
- toUpperCase
- (
- )
- }
- </
- Trans
- >
- // Extracted as: "Hello {0}"
- // ✅ Good - clear variable name
- const
- userName
- =
- user
- .
- name
- .
- toUpperCase
- (
- )
- ;
- <
- Trans
- >
- Hello
- {
- userName
- }
- </
- Trans
- >
- // Extracted as: "Hello {userName}"
- Use Trans for JSX, t for Strings
- Choose the right tool:
- // ✅ For JSX content
- <
- h1
- >
- <
- Trans
- >
- Welcome
- </
- Trans
- >
- </
- h1
- >
- // ✅ For string values
- const
- {
- t
- }
- =
- useLingui
- (
- )
- ;
- <
- img
- alt
- =
- {
- t
- `
- Profile picture
- `
- }
- />
- Don't Use Macros at Module Level
- Macros need component context - use
- msg
- instead:
- // ❌ Bad - won't work
- import
- {
- t
- }
- from
- "@lingui/core/macro"
- ;
- const
- LABELS
- =
- [
- t
- `
- Red
- `
- ,
- t
- `
- Green
- `
- ,
- t
- `
- Blue
- `
- ]
- ;
- // ✅ Good - use msg for lazy translation
- import
- {
- msg
- }
- from
- "@lingui/core/macro"
- ;
- const
- LABELS
- =
- [
- msg
- `
- Red
- `
- ,
- msg
- `
- Green
- `
- ,
- msg
- `
- Blue
- `
- ]
- ;
- Use the ESLint Plugin
- Install and configure
- eslint-plugin-lingui
- to catch common mistakes automatically:
- npm
- install
- --save-dev eslint-plugin-lingui
- // eslint.config.js
- import
- pluginLingui
- from
- "eslint-plugin-lingui"
- ;
- export
- default
- [
- pluginLingui
- .
- configs
- [
- "flat/recommended"
- ]
- ,
- ]
- ;
- Common Patterns
- Dynamic Locale Switching
- import
- {
- i18n
- }
- from
- "@lingui/core"
- ;
- async
- function
- changeLocale
- (
- locale
- )
- {
- const
- {
- messages
- }
- =
- await
- import
- (
- `
- ./locales/
- ${
- locale
- }
- /messages
- `
- )
- ;
- i18n
- .
- load
- (
- locale
- ,
- messages
- )
- ;
- i18n
- .
- activate
- (
- locale
- )
- ;
- }
- Loading Catalogs Dynamically
- import
- {
- useEffect
- }
- from
- "react"
- ;
- import
- {
- i18n
- }
- from
- "@lingui/core"
- ;
- function
- loadCatalog
- (
- locale
- )
- {
- return
- import
- (
- `
- ./locales/
- ${
- locale
- }
- /messages
- `
- )
- ;
- }
- function
- App
- (
- )
- {
- useEffect
- (
- (
- )
- =>
- {
- loadCatalog
- (
- "en"
- )
- .
- then
- (
- catalog
- =>
- {
- i18n
- .
- load
- (
- "en"
- ,
- catalog
- .
- messages
- )
- ;
- i18n
- .
- activate
- (
- "en"
- )
- ;
- }
- )
- ;
- }
- ,
- [
- ]
- )
- ;
- return
- <
- I18nProvider
- i18n
- =
- {
- i18n
- }
- >
- {
- / ... /
- }
- </
- I18nProvider
- >
- ;
- }
- Memoization with useLingui
- When using memoization, use the
- t
- function from the macro version:
- import
- {
- useLingui
- }
- from
- "@lingui/react/macro"
- ;
- import
- {
- msg
- }
- from
- "@lingui/core/macro"
- ;
- import
- {
- useMemo
- }
- from
- "react"
- ;
- const
- welcomeMessage
- =
- msg
- `
- Welcome!
- `
- ;
- function
- MyComponent
- (
- )
- {
- const
- {
- t
- }
- =
- useLingui
- (
- )
- ;
- // Macro version - reference changes with locale
- // ✅ Safe - t reference updates with locale
- const
- message
- =
- useMemo
- (
- (
- )
- =>
- t
- (
- welcomeMessage
- )
- ,
- [
- t
- ]
- )
- ;
- return
- <
- div
- >
- {
- message
- }
- </
- div
- >
- ;
- }
- Troubleshooting
- If you encounter issues:
- Messages not extracted
-
- Check
- include
- patterns in
- lingui.config.js
- Translations not applied
-
- Ensure catalogs are compiled with
- lingui compile
- Runtime errors
-
- Verify
- I18nProvider
- wraps your app
- Type errors
- Run lingui compile --typescript for TypeScript projects For detailed common mistakes and pitfalls, see common-mistakes.md .