Skip to content

Commit

Permalink
wip [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
andrii-bodnar committed Oct 29, 2024
1 parent 0a94393 commit 68574ad
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 233 deletions.
67 changes: 67 additions & 0 deletions website/docs/guides/lazy-translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: Lazy Translations
description: Lazy translations allow you to defer translation of a message until it is actually displayed.
---

# Lazy Translations

You don't need to declare messages at the same code location where they are displayed. Tag a string with the [`msg`](/docs/ref/macro.mdx#definemessage) macro, and you've created a "message descriptor", which can then be passed around as a variable, and can be displayed as a translated string by passing its `id` to [`Trans`](/docs/ref/macro.mdx#trans) as its `id` prop:

```jsx
import { msg } from "@lingui/core/macro";
import { Trans } from "@lingui/react";

const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];

export default function ColorList() {
return (
<ul>
{favoriteColors.map((color) => (
<li>
<Trans id={color.id} />
</li>
))}
</ul>
);
}
```

:::note
Note that we import `<Trans>` component from `@lingui/react`, because we want to use the runtime `Trans` component here, not the (compile-time) macro.
:::

To render the message descriptor as a string-only translation, pass it to the [`i18n._()`](/docs/ref/core.md#i18n._) method:

```jsx
import { i18n } from "@lingui/core";
import { msg } from "@lingui/core/macro";

const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];

export function getTranslatedColorNames() {
return favoriteColors.map((color) => i18n._(color));
}
```

### Picking a message based on a variable

Sometimes you need to pick 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 representing the current status.

A simple 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:

```jsx
import { msg } from "@lingui/core/macro";
import { useLingui } from "@lingui/react";

const statusMessages = {
["STATUS_OPEN"]: msg`Open`,
["STATUS_CLOSED"]: msg`Closed`,
["STATUS_CANCELLED"]: msg`Cancelled`,
["STATUS_COMPLETED"]: msg`Completed`,
};

export default function StatusDisplay({ statusCode }) {
const { _ } = useLingui();
return <div>{_(statusMessages[statusCode])}</div>;
}
```
51 changes: 51 additions & 0 deletions website/docs/ref/macro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,57 @@ function MyComponent() {
The `useLingui` React macro is available from **Lingui v5**.
:::

## Important Notes

### Using Macros

All Core Macros cannot be used at the module level:

```jsx
import { t } from "@lingui/core/macro";

// ❌ Bad! This won't work because the `t` macro is used at the module level.
// The `t` macro returns a string, and once this string is assigned, it won't react to locale changes.
const colors = [t`Red`, t`Orange`, t`Yellow`, t`Green`];

// ✅ Good! Every time the function is executed, the `t` macro will be re-executed as well,
// and the correctly translated color labels will be returned.
function getColors() {
return [t`Red`, t`Orange`, t`Yellow`, t`Green`];
}
```

:::tip
There is an [ESLint Plugin](/docs/ref/eslint-plugin.md) rule designed to check for this misuse: `t-call-in-function`.
:::

[//]: # (TODO: link to the article)

A better option would be to use the Lazy Translations pattern.

### Global `i18n` Instance

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.

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:

```js
import { msg } from "@lingui/core/macro";
import { useLingui } from "@lingui/react/macro";

export function showAlert(i18n) {
alert(i18n._(msg`...`));
}

function MyComponent() {
// Get i18n instance from React Context
const { i18n } = useLingui();

// Pass the instance from outside
showAlert(i18n);
}
```

## More Examples

### Examples of JS macros
Expand Down
185 changes: 0 additions & 185 deletions website/docs/tutorials/react-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,191 +5,6 @@ description: Learn about the most common i18n patterns in React and how to use t

# Common i18n Patterns in React

This page describes the most common i18n patterns in React. It's a follow-up to the [tutorial](/docs/tutorials/react.md) with practical examples. See the [API reference](/docs/ref/react.md) for detailed information about all components.

## Macros

Using jsx macros is the most straightforward way to translate your React components.

[`Trans`](/docs/ref/macro.mdx#trans) handles translations of messages including variables and other React components:

```jsx
import { Trans } from "@lingui/react/macro";

function render() {
return (
<>
<h1>
<Trans>LinguiJS example</Trans>
</h1>
<p>
<Trans>
Hello <a href="/profile">{name}</a>.
</Trans>
</p>
</>
);
}
```

You don't need anything special to use [`Trans`](/docs/ref/macro.mdx#trans) inside your app (except of wrapping the root component in [`I18nProvider`](/docs/ref/react.md#i18nprovider)).

## Element attributes and string-only translations

Sometimes you can't use [`Trans`](/docs/ref/macro.mdx#trans) component, for example when translating element attributes:

```html
<img src="..." alt="Image caption" />
```

In such case you need to use the [`useLingui()`](/docs/ref/macro.mdx#uselingui) macro:

```jsx
import { useLingui } from "@lingui/react/macro";

export default function ImageWithCaption() {
const { t } = useLingui();

return <img src="..." alt={t`Image caption`} />;
}
```

If `useLingui` macro is not available in your setup you can use the [`useLingui()`](/docs/ref/react.md#uselingui) runtime hook with the [`msg`](/docs/ref/macro.mdx#definemessage) macro.

```jsx
import { msg } from "@lingui/core/macro";
import { useLingui } from "@lingui/react";

export default function ImageWithCaption() {
const { _ } = useLingui();

return <img src="..." alt={_(msg`Image caption`)} />;
}
```

## Translations outside React components

Sometimes, you may need to access translations outside React components, which is another common pattern. You can use [`t`](/docs/ref/macro.mdx#t) macro outside React context as usual:

```jsx
import { t } from "@lingui/core/macro";

export function showAlert() {
alert(t`...`);
}
```

:::caution
When you use [`t`](/docs/ref/macro.mdx#t) macro (and [`plural`](/docs/ref/macro.mdx#plural), [`select`](/docs/ref/macro.mdx#select), [`selectOrdinal`](/docs/ref/macro.mdx#selectordinal)), it uses a global `i18n` instance. While this generally works, there are situations, like in server-side rendering (SSR) applications, where it may not be the best fit.

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.

```ts
import { msg } from "@lingui/core/macro";
import { useLingui } from "@lingui/react/macro";
import { i18n } from "@lingui/core";

export function showAlert(i18n: I18n) {
alert(i18n._(msg`...`));
}

function MyComponent() {
// get i18n instance from React Context
const { i18n } = useLingui();

// pass instance outside
showAlert(i18n);
}
```

Note that we import `useLingui` from `@lingui/react/macro`. There is also a runtime version of `useLingui` hook exported from `@lingui/react`. In the case above, it doesn't matter what version to choose since we use only `i18n` object which is returned by both.

:::

:::note
All js macros such as [`t`](/docs/ref/macro.mdx#t) [`plural`](/docs/ref/macro.mdx#plural), [`select`](/docs/ref/macro.mdx#select), [`selectOrdinal`](/docs/ref/macro.mdx#selectordinal) cannot be used on the module level.

```jsx
import { t } from "@lingui/core/macro";

// ❌ Bad! This won't work because the `t` macro is used at the module level.
// The `t` macro returns a string, and once this string is assigned, it won't react to locale changes.
const colors = [t`Red`, t`Orange`, t`Yellow`, t`Green`];

// ✅ Good! Every time the function is executed, the `t` macro will be re-executed as well,
// and the correctly translated color labels will be returned.
function getColors() {
return [t`Red`, t`Orange`, t`Yellow`, t`Green`];
}
```

There is an [ESLint Rule](https://github.com/lingui/eslint-plugin#t-call-in-function) designed to check for this misuse.

A better option would be to use the Lazy Translations pattern described in the following paragraph.
:::

## Lazy Translations

You don't need to declare messages at the same code location where they are displayed. Tag a string with the [`msg`](/docs/ref/macro.mdx#definemessage) macro, and you've created a "message descriptor", which can then be passed around as a variable, and can be displayed as a translated string by passing its `id` to [`Trans`](/docs/ref/macro.mdx#trans) as its `id` prop:

```jsx
import { msg } from "@lingui/core/macro";
import { Trans } from "@lingui/react";

const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];

export default function ColorList() {
return (
<ul>
{favoriteColors.map((color) => (
<li>
<Trans id={color.id} />
</li>
))}
</ul>
);
}
```

:::note
Note that we import `<Trans>` component from `@lingui/react`, because we want to use the runtime `Trans` component here, not the (compile-time) macro.
:::

To render the message descriptor as a string-only translation, pass it to the [`i18n._()`](/docs/ref/core.md#i18n._) method:

```jsx
import { i18n } from "@lingui/core";
import { msg } from "@lingui/core/macro";

const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];

export function getTranslatedColorNames() {
return favoriteColors.map((color) => i18n._(color));
}
```

### Picking a message based on a variable

Sometimes you need to pick 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 representing the current status.

A simple 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:

```jsx
import { msg } from "@lingui/core/macro";
import { useLingui } from "@lingui/react";

const statusMessages = {
["STATUS_OPEN"]: msg`Open`,
["STATUS_CLOSED"]: msg`Closed`,
["STATUS_CANCELLED"]: msg`Cancelled`,
["STATUS_COMPLETED"]: msg`Completed`,
};

export default function StatusDisplay({ statusCode }) {
const { _ } = useLingui();
return <div>{_(statusMessages[statusCode])}</div>;
}
```

## Memoization pitfall

Expand Down
2 changes: 1 addition & 1 deletion website/docs/tutorials/react-rsc.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ After configuring the middleware, make sure your page and route files are moved

### Next.js Config

Secondly, add the `swc-plugin` to the `next.config.js`, so that you can use [Lingui macros](https://lingui.dev/ref/macro).
Secondly, add the `swc-plugin` to the `next.config.js`, so that you can use [Lingui Macros](/docs/ref/macro.mdx).

```js title="next.config.js"
/** @type {import('next').NextConfig} */
Expand Down
Loading

0 comments on commit 68574ad

Please sign in to comment.