Skip to content

Commit 965231a

Browse files
docs: Tutorials section improvements (#2075)
1 parent 9853713 commit 965231a

File tree

12 files changed

+468
-566
lines changed

12 files changed

+468
-566
lines changed

vercel.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
{ "source": "/guides/excluding-build-files", "destination": "/ref/cli#compile", "permanent": true},
1111
{ "source": "/tutorials/explicit-vs-generated-ids", "destination": "/guides/explicit-vs-generated-ids", "permanent": true},
1212
{ "source": "/tutorials/setup-vite", "destination": "/installation#vite", "permanent": true },
13-
{ "source": "/tutorials/setup-react", "destination": "/installation", "permanent": true }
13+
{ "source": "/tutorials/setup-react", "destination": "/installation", "permanent": true },
14+
{ "source": "/tutorials/react-patterns", "destination": "/tutorials/react", "permanent": true }
1415
]
1516
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
title: Lazy Translations
3+
description: Lazy translations allow you to defer translation of a message until it is actually displayed
4+
---
5+
6+
# Lazy Translations
7+
8+
Lazy translation allows you to defer translation of a message until it's rendered, giving you flexibility in how and where you define messages in your code. With lazy translation, you can tag a string with the [`msg`](/docs/ref/macro.mdx#definemessage) macro to create a _message descriptor_ that can be saved, passed around as a variable, and rendered later.
9+
10+
## Usage Example
11+
12+
To render the message descriptor as a string-only translation, pass it to the [`i18n._()`](/docs/ref/core.md#i18n._) method:
13+
14+
```jsx
15+
import { msg } from "@lingui/core/macro";
16+
import { i18n } from "@lingui/core";
17+
18+
const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];
19+
20+
export function getTranslatedColorNames() {
21+
return favoriteColors.map((color) => i18n._(color));
22+
}
23+
```
24+
25+
## Usage in React
26+
27+
To render the message descriptor in a React component, pass its `id` to the [`Trans`](/docs/ref/react.md#trans) component as a value of the `id` prop:
28+
29+
```jsx
30+
import { msg } from "@lingui/core/macro";
31+
import { Trans } from "@lingui/react";
32+
33+
const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];
34+
35+
export default function ColorList() {
36+
return (
37+
<ul>
38+
{favoriteColors.map((color) => (
39+
<li>
40+
<Trans id={color.id} />
41+
</li>
42+
))}
43+
</ul>
44+
);
45+
}
46+
```
47+
48+
:::info Important
49+
Please note that we import the `<Trans>` component from `@lingui/react` to use the runtime version, as the message is already defined and we don't need the compile-time macro here.
50+
:::
51+
52+
### Picking a Message Based on a Variable
53+
54+
Sometimes you need to choose between different messages to display depending on the value of a variable. For example, imagine you have a numeric "status" code that comes from an API, and you need to display a message that represents the current status.
55+
56+
An easy way to do this is to create an object that maps the possible values of "status" to message descriptors (tagged with the [`msg`](/docs/ref/macro.mdx#definemessage) macro) and render them as needed with deferred translation:
57+
58+
```jsx
59+
import { msg } from "@lingui/core/macro";
60+
import { useLingui } from "@lingui/react";
61+
62+
const statusMessages = {
63+
["STATUS_OPEN"]: msg`Open`,
64+
["STATUS_CLOSED"]: msg`Closed`,
65+
["STATUS_CANCELLED"]: msg`Cancelled`,
66+
["STATUS_COMPLETED"]: msg`Completed`,
67+
};
68+
69+
export default function StatusDisplay({ statusCode }) {
70+
const { _ } = useLingui();
71+
return <div>{_(statusMessages[statusCode])}</div>;
72+
}
73+
```
74+
75+
### Memoization Pitfall
76+
77+
In the following contrived example, we document how a welcome message will or will not be updated when locale changes. The documented behavior may not be intuitive at first, but it is expected, because of the way the `useMemo` dependencies work.
78+
79+
To avoid bugs with stale translations, use the `_` function returned from the [`useLingui`](/docs/ref/react.md#uselingui) hook: it is safe to use with memoization because its reference changes whenever the Lingui context updates.
80+
81+
:::tip
82+
You can also use the `t` function from the [`useLingui`](/docs/ref/macro.mdx#uselingui) macro hook, which behaves the same way as `_` from the runtime [`useLingui`](/docs/ref/react.md#uselingui) counterpart.
83+
:::
84+
85+
Keep in mind that `useMemo` is primarily a performance optimization tool in React. Because of this, there might be no need to memoize your translations. Additionally, this issue is not present when using the `Trans` component, which we recommend using whenever possible.
86+
87+
```jsx
88+
import { msg } from "@lingui/core/macro";
89+
import { i18n } from "@lingui/core";
90+
import { useLingui } from "@lingui/react";
91+
92+
const welcomeMessage = msg`Welcome!`;
93+
94+
// ❌ Bad! This code won't work
95+
export function Welcome() {
96+
const buggyWelcome = useMemo(() => {
97+
return i18n._(welcomeMessage);
98+
}, []);
99+
100+
return <div>{buggyWelcome}</div>;
101+
}
102+
103+
// ❌ Bad! This code won't work either because the reference to i18n does not change
104+
export function Welcome() {
105+
const { i18n } = useLingui();
106+
107+
const buggyWelcome = useMemo(() => {
108+
return i18n._(welcomeMessage);
109+
}, [i18n]);
110+
111+
return <div>{buggyWelcome}</div>;
112+
}
113+
114+
// ✅ Good! `useMemo` has i18n context in the dependency
115+
export function Welcome() {
116+
const linguiCtx = useLingui();
117+
118+
const welcome = useMemo(() => {
119+
return linguiCtx.i18n._(welcomeMessage);
120+
}, [linguiCtx]);
121+
122+
return <div>{welcome}</div>;
123+
}
124+
125+
// 🤩 Better! `useMemo` consumes the `_` function from the Lingui context
126+
export function Welcome() {
127+
const { _ } = useLingui();
128+
129+
const welcome = useMemo(() => {
130+
return _(welcomeMessage);
131+
}, [_]);
132+
133+
return <div>{welcome}</div>;
134+
}
135+
```

website/docs/installation.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Learn how to install Lingui in your project, whether you use JavaScript, React (
1818
- Install [Lingui CLI](/docs/ref/cli.md) to manage your translations and catalogs.
1919

2020
:::tip
21-
Don't miss the [Lingui ESLint Plugin](/docs/ref/eslint-plugin.md) which can help you find and prevent common l10n mistakes in your code.
21+
Don't miss the [Lingui ESLint Plugin](/docs/ref/eslint-plugin.md) which can help you find and prevent common i18n mistakes in your code.
2222
:::
2323

2424
## Choosing a Transpiler

website/docs/ref/macro.mdx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ const message = t({
386386

387387
### `defineMessage` / `msg` {#definemessage}
388388

389-
The `defineMessage` (alias: `msg`) macro allows to define a message for later use. It has the same signature as `t` and returns a `MessageDescriptor` that you can pass to `i18n._` to get a translated string at any time later. This is useful for [lazy translations](/tutorials/react-patterns#lazy-translations).
389+
The `defineMessage` (alias: `msg`) macro allows to define a message for later use. It has the same signature as `t` and returns a `MessageDescriptor` that you can pass to `i18n._` to get a translated string at any time later. This is useful for [Lazy Translations](/guides/lazy-translations).
390390

391391
In other words, `t` returns a translated string at the time when it's called, while `msg` returns a `MessageDescriptor` that can produce translated strings later:
392392

@@ -764,6 +764,55 @@ function MyComponent() {
764764
The `useLingui` React macro is available from **Lingui v5**.
765765
:::
766766

767+
## Important Notes
768+
769+
### Using Macros
770+
771+
All Core Macros cannot be used at the module level:
772+
773+
```jsx
774+
import { t } from "@lingui/core/macro";
775+
776+
// ❌ Bad! This won't work because the `t` macro is used at the module level.
777+
// The `t` macro returns a string, and once this string is assigned, it won't react to locale changes.
778+
const colors = [t`Red`, t`Orange`, t`Yellow`, t`Green`];
779+
780+
// ✅ Good! Every time the function is executed, the `t` macro will be re-executed as well,
781+
// and the correctly translated color labels will be returned.
782+
function getColors() {
783+
return [t`Red`, t`Orange`, t`Yellow`, t`Green`];
784+
}
785+
```
786+
787+
:::tip
788+
There is an [ESLint Plugin](/docs/ref/eslint-plugin.md) rule designed to check for this misuse: `t-call-in-function`.
789+
:::
790+
791+
A better option would be to use the [Lazy Translations](/guides/lazy-translations) pattern.
792+
793+
### Global `i18n` Instance
794+
795+
When you use the [`t`](#t) macro (or [`plural`](#plural), [`select`](#select), [`selectOrdinal`](#selectordinal)), it uses a global [`i18n`](/docs/ref/core.md#i18n) instance. While this generally works, there are situations, such as server-side rendering (SSR) applications, where it may not be the best solution.
796+
797+
For better control and flexibility, it's a good idea to avoid the global `i18n` instance and instead use a specific instance tailored to your needs:
798+
799+
```js
800+
import { msg } from "@lingui/core/macro";
801+
import { useLingui } from "@lingui/react/macro";
802+
803+
export function showAlert(i18n) {
804+
alert(i18n._(msg`...`));
805+
}
806+
807+
function MyComponent() {
808+
// Get i18n instance from React Context
809+
const { i18n } = useLingui();
810+
811+
// Pass the instance from outside
812+
showAlert(i18n);
813+
}
814+
```
815+
767816
## More Examples
768817

769818
### Examples of JS macros

website/docs/ref/react.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ The `useLingui` hook provides access to the Lingui context. It returns an object
135135
| `_` | `I18n[_]` | Reference to the [`i18n._`](/ref/core#i18n._) function, explained below |
136136
| `defaultComponent` | `React.ComponentType` | The same `defaultComponent` you passed to `I18nProvider`, if provided |
137137

138-
Components that use `useLingui` hook will re-render when locale and / or catalogs change. However, the reference to the `i18n` object is stable and doesn't change between re-renders. This can lead to unexpected behavior with memoization (see [memoization pitfall](/tutorials/react-patterns#memoization-pitfall)).
138+
Components that use `useLingui` hook will re-render when locale and / or catalogs change. However, the reference to the `i18n` object is stable and doesn't change between re-renders. This can lead to unexpected behavior with memoization (see [memoization pitfall](/guides/lazy-translations#memoization-pitfall)).
139139

140140
To alleviate the issue, `useLingui` provides the `_` function, which is the same as [`i18n._`](/ref/core#i18n._) but _its reference changes_ with each update of the Lingui context. Thanks to that, you can safely use this `_` function as a hook dependency:
141141

website/docs/releases/migration-4.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ You will need to make some changes as this is a misuse of the library that actua
117117

118118
Due to the changes caused by hash-based message ID feature described earlier, this approach will no longer work.
119119

120-
Instead, please use [recommended](/docs/tutorials/react-patterns.md#lazy-translations) pattern for such translations:
120+
Instead, please use [recommended](/guides/lazy-translations) pattern for such translations:
121121

122122
```tsx
123123
import { msg } from "@lingui/macro";

website/docs/tutorials/javascript.md

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,40 @@ description: Learn how to use Lingui's internationalization features in your van
55

66
# JavaScript Apps Internationalization
77

8-
In this tutorial, we'll learn how to use Lingui's internationalization features that don't depend on React. We'll take a minimalist approach and cover the main features of the `@lingui/core` package.
8+
This tutorial will walk you through using Lingui's internationalization (i18n) features in a vanilla JavaScript application. We'll cover the essentials of the `@lingui/core` package, which handles all translation and message catalog management.
9+
10+
:::tip Example
11+
If you're looking for a working solution, check out the [Vanilla JS example](https://github.com/lingui/js-lingui/tree/main/examples/js). This example application shows a complete setup using Lingui and vanilla JavaScript.
12+
:::
913

1014
## Installing Lingui
1115

1216
1. Follow the [Installation and Setup](/docs/installation.mdx) page for initial setup.
13-
2. Install the [`@lingui/core`](/docs/ref/core.md) package, which is responsible for translation and handling of message catalogs.
17+
2. Install the [`@lingui/core`](/docs/ref/core.md) package, which is responsible for translation and message catalog handling.
1418

1519
## Setting up i18n
1620

17-
First we need to setup the i18n singleton, which is pretty simple:
21+
First, we need to set up the i18n singleton, which is pretty simple:
1822

1923
```js
2024
import { i18n } from "@lingui/core";
21-
22-
// messages.js is generated by the cli
2325
import { messages } from "path-to-locale/en/messages.js";
2426

2527
i18n.load("en", messages);
2628
i18n.activate("en");
2729
```
2830

31+
The `messages.js` is generated by the Lingui CLI and contains compiled message catalogs.
32+
33+
:::tip
34+
Alternatively, you can load catalogs dynamically using the [`@lingui/loader`](/docs/ref/loader.md) or [`@lingui/vite-plugin`](/docs/ref/vite-plugin.md) without the need to import compiled messages manually.
35+
:::
36+
2937
## Localizing Your App
3038

31-
Once that is done, we can go ahead and use it! Wrap your text in [`t`](/docs/ref/macro.mdx#t) macro:
39+
To localize your application, you need to wrap your localizable texts in [Macros](/docs/ref/macro.mdx). Lingui provides a set of Core Macros that transform tagged template literals and can be used in any JavaScript context.
40+
41+
Let's wrap our text in the [`t`](/docs/ref/macro.mdx#t) macro:
3242

3343
```js
3444
import { t } from "@lingui/core/macro";
@@ -41,7 +51,7 @@ t`My name is ${name}`;
4151
// becomes "Je m'appelle Fred"
4252
```
4353

44-
Plurals and selections are possible using plural and select methods:
54+
Plurals and selections are possible using [`plural`](/docs/ref/macro.mdx#plural) and [`select`](/docs/ref/macro.mdx#select) macros:
4555

4656
```js
4757
import { plural } from "@lingui/core/macro";
@@ -55,10 +65,10 @@ plural(count, {
5565
// becomes "42 livres"
5666
```
5767

58-
It's also possible to nest message formats. Each message format method in i18n has a standalone companion, which only returns message without performing the translation:
68+
It's also possible to nest message formats. Each message format method in `i18n` has a standalone companion, which only returns message without performing the translation:
5969

6070
```js
61-
import { t, select, plural } from "@lingui/core/macro"
71+
import { t, select, plural } from "@lingui/core/macro";
6272

6373
select(gender, {
6474
offset: 1,
@@ -71,10 +81,12 @@ select(gender, {
7181
}),
7282
male: plural(value, {...}),
7383
other: plural(value, {...}),
74-
})
84+
});
7585
```
7686

77-
## Further Reading
87+
## See Also
7888

79-
- [CLI Reference](/docs/ref/cli.md)
89+
- [Message Extraction Guide](/docs/guides/message-extraction.md)
8090
- [Pluralization Guide](/docs/guides/plurals.md)
91+
- [Dynamic Loading of Message Catalogs](/docs/guides/dynamic-loading-catalogs.md)
92+
- [`@lingui/cli` Reference](/docs/ref/cli.md)

0 commit comments

Comments
 (0)