diff --git a/website/README.md b/website/README.md
new file mode 100644
index 000000000..9a3bd56ac
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1,58 @@
+# Lingui Documentation Website
+
+This website is built using [Docusaurus](https://docusaurus.io/) and includes versioned documentation.
+
+## Development
+
+```bash
+# Install dependencies
+yarn install
+
+# Start development server
+yarn start
+
+# Build for production
+yarn build
+
+# Serve production build
+yarn serve
+```
+
+## Versioning
+
+```bash
+# Create version from current docs/
+yarn docusaurus docs:version VERSION_NUMBER
+```
+
+## Structure
+
+```text
+website/
+├── docs/ # Current version documentation
+│ ├── assets/ # Current version images
+│ └── ... # Other documentation files
+├── versioned_docs/
+│ └── version-x.x/ # Legacy documentation for x.x (unmaintained)
+│ └── ... # Same structure as docs/
+├── versioned_sidebars/
+│ └── version-x.x-sidebars.json # Legacy navigation for x.x
+├── blog/ # Blog posts
+│ ├── ...
+│ └── authors.yml # Blog authors
+├── src/
+│ ├── pages/ # Non-versioned static pages
+│ │ ├── examples.md # Examples page
+│ │ ├── community.md # Community page
+│ │ └── index.tsx # Homepage
+│ ├── components/ # React components
+│ └── css/ # Custom styles
+├── static/
+│ ├── img/
+│ │ ├── features/ # Feature section images
+│ │ └── ... # Global assets (logos, favicons)
+│ └── ...
+├── docusaurus.config.ts # Main configuration
+├── sidebars.ts # Current documentation navigation
+└── versions.json # Available versions list
+```
diff --git a/website/static/img/docs/extractor-deps-scheme.svg b/website/blog/2023-04-26-announcing-lingui-4.0/extractor-deps-scheme.svg
similarity index 100%
rename from website/static/img/docs/extractor-deps-scheme.svg
rename to website/blog/2023-04-26-announcing-lingui-4.0/extractor-deps-scheme.svg
diff --git a/website/blog/2023-04-26-announcing-lingui-4.0/index.md b/website/blog/2023-04-26-announcing-lingui-4.0/index.md
index 66ffb3ca3..867b3c12d 100644
--- a/website/blog/2023-04-26-announcing-lingui-4.0/index.md
+++ b/website/blog/2023-04-26-announcing-lingui-4.0/index.md
@@ -92,7 +92,7 @@ By doing so, it creates a more optimized catalog that only contains the messages
The catalogs would still contain duplicate messages for common components, but it would be much better than the current approach.
-
+
:::tip
See the [Message Extraction](https://lingui.dev/guides/message-extraction) guide to learn more about how message extraction works.
diff --git a/website/static/img/docs/Crowdin__js-lingui-vcs.png b/website/docs/assets/Crowdin__js-lingui-vcs.png
similarity index 100%
rename from website/static/img/docs/Crowdin__js-lingui-vcs.png
rename to website/docs/assets/Crowdin__js-lingui-vcs.png
diff --git a/website/static/img/docs/dynamic-loading-catalogs-1.png b/website/docs/assets/dynamic-loading-catalogs-1.png
similarity index 100%
rename from website/static/img/docs/dynamic-loading-catalogs-1.png
rename to website/docs/assets/dynamic-loading-catalogs-1.png
diff --git a/website/static/img/docs/dynamic-loading-catalogs-2.png b/website/docs/assets/dynamic-loading-catalogs-2.png
similarity index 100%
rename from website/static/img/docs/dynamic-loading-catalogs-2.png
rename to website/docs/assets/dynamic-loading-catalogs-2.png
diff --git a/website/docs/assets/extractor-deps-scheme.svg b/website/docs/assets/extractor-deps-scheme.svg
new file mode 100644
index 000000000..169a9ca33
--- /dev/null
+++ b/website/docs/assets/extractor-deps-scheme.svg
@@ -0,0 +1,157 @@
+
diff --git a/website/static/img/docs/extractor-glob-scheme.svg b/website/docs/assets/extractor-glob-scheme.svg
similarity index 100%
rename from website/static/img/docs/extractor-glob-scheme.svg
rename to website/docs/assets/extractor-glob-scheme.svg
diff --git a/website/static/img/docs/lingui-workflow.svg b/website/docs/assets/lingui-workflow.svg
similarity index 100%
rename from website/static/img/docs/lingui-workflow.svg
rename to website/docs/assets/lingui-workflow.svg
diff --git a/website/static/img/docs/rn-component-nesting.png b/website/docs/assets/rn-component-nesting.png
similarity index 100%
rename from website/static/img/docs/rn-component-nesting.png
rename to website/docs/assets/rn-component-nesting.png
diff --git a/website/static/img/docs/translation-lingui-plural-forms.png b/website/docs/assets/translation-lingui-plural-forms.png
similarity index 100%
rename from website/static/img/docs/translation-lingui-plural-forms.png
rename to website/docs/assets/translation-lingui-plural-forms.png
diff --git a/website/static/img/docs/with-collaboration-tool.svg b/website/docs/assets/with-collaboration-tool.svg
similarity index 100%
rename from website/static/img/docs/with-collaboration-tool.svg
rename to website/docs/assets/with-collaboration-tool.svg
diff --git a/website/static/img/docs/without-collaboration-tool.svg b/website/docs/assets/without-collaboration-tool.svg
similarity index 100%
rename from website/static/img/docs/without-collaboration-tool.svg
rename to website/docs/assets/without-collaboration-tool.svg
diff --git a/website/docs/guides/custom-formatter.md b/website/docs/guides/custom-formatter.md
index 9853fe986..82a25a868 100644
--- a/website/docs/guides/custom-formatter.md
+++ b/website/docs/guides/custom-formatter.md
@@ -5,7 +5,7 @@ description: Learn how to write a custom localization message formatter for your
# Custom Formatter
-If your project requires a message catalog format that Lingui doesn't [natively support](/ref/catalog-formats), you can create a custom formatter to handle it. A custom formatter allows you to define how extracted strings are formatted into a custom catalog format, providing flexibility for specialized workflows and integration with unique file structures.
+If your project requires a message catalog format that Lingui doesn't [natively support](../ref/catalog-formats.md), you can create a custom formatter to handle it. A custom formatter allows you to define how extracted strings are formatted into a custom catalog format, providing flexibility for specialized workflows and integration with unique file structures.
## Overview
diff --git a/website/docs/guides/dynamic-loading-catalogs.md b/website/docs/guides/dynamic-loading-catalogs.md
index 1458af664..c5b760d3c 100644
--- a/website/docs/guides/dynamic-loading-catalogs.md
+++ b/website/docs/guides/dynamic-loading-catalogs.md
@@ -5,9 +5,9 @@ description: Learn how to set up dynamic loading of message catalogs in Lingui t
# Dynamic Loading of Message Catalogs
-Internationalization in modern applications requires careful handling of localized messages to avoid bloating the initial bundle size. By default, Lingui makes it easy to load all strings for a single active locale. For even greater efficiency, developers can selectively load only the messages needed on demand using [`i18n.load`](/ref/core#i18n.load), ensuring minimal resource usage.
+Internationalization in modern applications requires careful handling of localized messages to avoid bloating the initial bundle size. By default, Lingui makes it easy to load all strings for a single active locale. For even greater efficiency, developers can selectively load only the messages needed on demand using [`i18n.load`](../ref/core.md#i18n.load), ensuring minimal resource usage.
-The [`I18nProvider`](/ref/react#i18nprovider) component doesn't make assumptions about your app's structure, giving you the freedom to load only the necessary messages for the currently selected language.
+The [`I18nProvider`](../ref/react.md#i18nprovider) component doesn't make assumptions about your app's structure, giving you the freedom to load only the necessary messages for the currently selected language.
This guide shows how to set up dynamic loading of message catalogs, ensuring only the needed catalogs are loaded, which reduces bundle size and improves performance.
@@ -79,11 +79,11 @@ main.ab4626ef.js
When the page is first loaded, only the main bundle and the bundle for the first language are loaded:
-
+
After changing the language in the UI, the second language bundle is loaded:
-
+
## Dependency Tree Extractor (experimental)
@@ -91,4 +91,4 @@ The Dependency Tree Extractor is an experimental feature designed to improve mes
This allows for a more granular extraction process, resulting in smaller and more relevant message catalogs.
-For detailed guidance on message extraction, refer to the [Message Extraction](/guides/message-extraction) guide.
+For detailed guidance on message extraction, refer to the [Message Extraction](./message-extraction.md) guide.
diff --git a/website/docs/guides/explicit-vs-generated-ids.md b/website/docs/guides/explicit-vs-generated-ids.md
index 70390c954..768b9557a 100644
--- a/website/docs/guides/explicit-vs-generated-ids.md
+++ b/website/docs/guides/explicit-vs-generated-ids.md
@@ -148,7 +148,7 @@ function render() {
}
```
-In the example code above, the content of [`Trans`](/ref/macro#trans) is transformed into a message in MessageFormat syntax. By default, this message is used for generating the ID. Considering the example above, the catalog would contain these entries:
+In the example code above, the content of [`Trans`](../ref/macro.mdx#trans) is transformed into a message in MessageFormat syntax. By default, this message is used for generating the ID. Considering the example above, the catalog would contain these entries:
```js
const catalog = [
@@ -202,7 +202,7 @@ The messages with IDs `msg.header` and `msg.hello` will be extracted with their
### With Core Macro
-To use custom IDs in non-JSX macros, call the [`msg`](/ref/macro#definemessage) function with a message descriptor object, passing the ID using the `id` property:
+To use custom IDs in non-JSX macros, call the [`msg`](../ref/macro.mdx#definemessage) function with a message descriptor object, passing the ID using the `id` property:
```jsx
import { msg } from "@lingui/core/macro";
@@ -212,7 +212,7 @@ msg({ id: "msg.greeting", message: `Hello World` });
Message `msg.greeting` will be extracted with default value `Hello World`.
-For all other js macros ([`plural`](/ref/macro#plural), [`select`](/ref/macro#select), [`selectOrdinal`](/ref/macro#selectordinal), use them inside [`msg`](/ref/macro#definemessage) macro to pass ID (in this case, `'msg.caption'`).
+For all other js macros ([`plural`](../ref/macro.mdx#plural), [`select`](../ref/macro.mdx#select), [`selectOrdinal`](../ref/macro.mdx#selectordinal), use them inside [`msg`](../ref/macro.mdx#definemessage) macro to pass ID (in this case, `'msg.caption'`).
```jsx
import { msg, plural } from "@lingui/core/macro";
@@ -228,5 +228,5 @@ msg({
## See Also
-- [Message Extraction](/guides/message-extraction)
-- [Macros Reference](/ref/macro)
+- [Message Extraction](./message-extraction.md)
+- [Macros Reference](../ref/macro.mdx)
diff --git a/website/docs/guides/lazy-translations.md b/website/docs/guides/lazy-translations.md
index 4d27d944a..dbc329a9f 100644
--- a/website/docs/guides/lazy-translations.md
+++ b/website/docs/guides/lazy-translations.md
@@ -5,11 +5,11 @@ description: Lazy translations allow you to defer translation of a message until
# Lazy Translations
-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`](/ref/macro#definemessage) macro to create a _message descriptor_ that can be saved, passed around as a variable, and rendered later.
+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`](../ref/macro.mdx#definemessage) macro to create a _message descriptor_ that can be saved, passed around as a variable, and rendered later.
## Usage Example
-To render the message descriptor as a string-only translation, pass it to the [`i18n._()`](/ref/core#i18n._) method:
+To render the message descriptor as a string-only translation, pass it to the [`i18n._()`](../ref/core.md#i18n._) method:
```jsx
import { msg } from "@lingui/core/macro";
@@ -24,7 +24,7 @@ export function getTranslatedColorNames() {
## Usage in React
-To render the message descriptor in a React component, pass its `id` to the [`Trans`](/ref/react#trans) component as a value of the `id` prop:
+To render the message descriptor in a React component, pass its `id` to the [`Trans`](../ref/react.md#trans) component as a value of the `id` prop:
```jsx
import { msg } from "@lingui/core/macro";
@@ -53,7 +53,7 @@ Please note that we import the `` component from `@lingui/react` to use t
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.
-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`](/ref/macro#definemessage) macro) and render them as needed with deferred translation:
+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`](../ref/macro.mdx#definemessage) macro) and render them as needed with deferred translation:
```jsx
import { msg } from "@lingui/core/macro";
@@ -76,7 +76,7 @@ export default function StatusDisplay({ statusCode }) {
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.
-To avoid bugs with stale translations, use the `t` function returned from the [`useLingui`](/ref/macro#uselingui) macro: it is safe to use with memoization because its reference changes whenever the Lingui context updates.
+To avoid bugs with stale translations, use the `t` function returned from the [`useLingui`](../ref/macro.mdx#uselingui) macro: it is safe to use with memoization because its reference changes whenever the Lingui context updates.
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.
diff --git a/website/docs/guides/message-extraction.md b/website/docs/guides/message-extraction.md
index c0a934d87..e8306d976 100644
--- a/website/docs/guides/message-extraction.md
+++ b/website/docs/guides/message-extraction.md
@@ -9,7 +9,7 @@ Message extraction is a key part of the internationalization process. It involve
In practice, developers define messages directly in the source code, and the extraction tool automatically collects these messages and stores them in a message catalog for translation.
-Read more about the [`lingui extract`](/ref/cli#extract) command.
+Read more about the [`lingui extract`](../ref/cli.md#extract) command.
## Supported Patterns
@@ -19,7 +19,7 @@ The extractor operates at a static level, meaning that it analyzes the source co
> Macros are JavaScript transformers that run at build time. The value returned by a macro is inlined into the bundle instead of the original function call.
-The Lingui Macro provides powerful macros to transform JavaScript objects and JSX elements into [ICU MessageFormat](/guides/message-format) messages at compile time.
+The Lingui Macro provides powerful macros to transform JavaScript objects and JSX elements into [ICU MessageFormat](./message-format.md) messages at compile time.
Extractor supports all macro usage, such as the following examples:
@@ -39,7 +39,7 @@ const jsx = Hi, my name is {name};
The extractor matches the `t` and `Trans` macro calls and extracts the messages from them.
-For more usage examples, see to the [Macros](/ref/macro) reference.
+For more usage examples, see to the [Macros](../ref/macro.mdx) reference.
### Non-Macros Usage
@@ -108,9 +108,9 @@ The extractor can locate source files in two ways: by specifying a glob pattern
By default, `lingui extract` uses a glob pattern to search for source files containing messages.
-The pattern is defined in the [`catalogs`](/ref/conf#catalogs) property of the Lingui configuration file in your project's root directory.
+The pattern is defined in the [`catalogs`](../ref/conf.md#catalogs) property of the Lingui configuration file in your project's root directory.
-
+
### Dependency Tree Crawling
@@ -130,7 +130,7 @@ By doing so, it creates a more optimized catalog that only contains the messages
The catalogs would still contain duplicate messages for common components, but it would be much better than the current approach.
-
+
To start using `experimental-extractor`, add the following section to your Lingui configuration:
@@ -206,10 +206,10 @@ The extractor supports TypeScript, Flow, and JavaScript (Stage 3) out of the box
If you are using some experimental features (Stage 0 - Stage 2) or frameworks with custom syntax such as Vue.js or Svelte, you may want to implement your own custom extractor.
-Visit [Custom Extractor](/guides/custom-extractor) to learn how to create a custom extractor.
+Visit [Custom Extractor](./custom-extractor.md) to learn how to create a custom extractor.
## See Also
-- [Lingui CLI Reference](/ref/cli)
-- [Macros Reference](/ref/macro)
-- [Catalog Formats](/ref/catalog-formats)
+- [Lingui CLI Reference](../ref/cli.md)
+- [Macros Reference](../ref/macro.mdx)
+- [Catalog Formats](../ref/catalog-formats.md)
diff --git a/website/docs/guides/message-format.md b/website/docs/guides/message-format.md
index de18d632b..4a6d542d3 100644
--- a/website/docs/guides/message-format.md
+++ b/website/docs/guides/message-format.md
@@ -57,5 +57,5 @@ Example: `Attachment {name} saved`
## See Also
-- [Pluralization](/guides/plurals)
+- [Pluralization](./plurals.md)
- [ICU Playground](https://format-message.github.io/icu-message-format-for-translators/editor.html)
diff --git a/website/docs/guides/plurals.md b/website/docs/guides/plurals.md
index b85573d61..40d2371a1 100644
--- a/website/docs/guides/plurals.md
+++ b/website/docs/guides/plurals.md
@@ -39,7 +39,7 @@ plural(numBooks, {
When `numBooks == 1`, this will render as _1 book_ and for `numBook == 2` it will be _2 books_. Interestingly, for `numBooks == -1`, it will be _-1 book_. This is because the "one" plural form also applies to -1. It is therefore important to remember that the plural forms (such as "one" or "two") do not represent the numbers themselves, but rather _categories_ of numbers.
-Under the hood, the [`plural`](/ref/macro#plural) macro is replaced with a low-level [`i18n._`](/ref/core#i18n._) call. In production, the example will look like this:
+Under the hood, the [`plural`](../ref/macro.mdx#plural) macro is replaced with a low-level [`i18n._`](../ref/core.md#i18n._) call. In production, the example will look like this:
```js
i18n._({
@@ -50,7 +50,7 @@ i18n._({
});
```
-When we extract messages from the source code using the [Lingui CLI](/ref/cli), we get:
+When we extract messages from the source code using the [Lingui CLI](../ref/cli.md), we get:
```icu-message-format
{numBooks, plural, one {# book} other {# books}}
diff --git a/website/docs/guides/pseudolocalization.md b/website/docs/guides/pseudolocalization.md
index 6c1527ada..abe55be0b 100644
--- a/website/docs/guides/pseudolocalization.md
+++ b/website/docs/guides/pseudolocalization.md
@@ -13,7 +13,7 @@ It also makes it easy to identify hard-coded strings and improperly concatenated
## Configuration
-To configure pseudolocalization, add the [`pseudoLocale`](/ref/conf#pseudolocale) property to your Lingui configuration file:
+To configure pseudolocalization, add the [`pseudoLocale`](../ref/conf.md#pseudolocale) property to your Lingui configuration file:
```ts title="lingui.config.{ts,js}"
import { defineConfig } from "@lingui/cli";
@@ -27,15 +27,15 @@ export default defineConfig({
});
```
-The `pseudoLocale` option must be set to any string that matches a value in the [`locales`](/ref/conf#locales) configuration. If this is not set correctly, no folder or pseudolocalization will be created.
+The `pseudoLocale` option must be set to any string that matches a value in the [`locales`](../ref/conf.md#locales) configuration. If this is not set correctly, no folder or pseudolocalization will be created.
If the `fallbackLocales` is configured, the pseudolocalization will be generated from the translated fallback locale instead.
## Generate Pseudolocalization
-After running the [`extract`](/ref/cli#extract) command, verify that the folder for the pseudolocale has been created.
+After running the [`extract`](../ref/cli.md#extract) command, verify that the folder for the pseudolocale has been created.
-Pseudolocalization is automatically generated during the [`compile`](/ref/cli#compile) process, using the messages.
+Pseudolocalization is automatically generated during the [`compile`](../ref/cli.md#compile) process, using the messages.
## Switch Browser Into Specified Pseudolocale
diff --git a/website/docs/guides/testing.md b/website/docs/guides/testing.md
index 921c1dd77..f1f9c5f70 100644
--- a/website/docs/guides/testing.md
+++ b/website/docs/guides/testing.md
@@ -1,6 +1,6 @@
# Testing
-In a React application, components that use [`Trans`](/ref/react#trans) or [`useLingui`](/ref/react#uselingui) need access to the context provided by [`I18nProvider`](/ref/react#i18nprovider). How you wrap your component with the I18nProvider depends on the testing library you're using.
+In a React application, components that use [`Trans`](../ref/react.md#trans) or [`useLingui`](../ref/react.md#uselingui) need access to the context provided by [`I18nProvider`](../ref/react.md#i18nprovider). How you wrap your component with the I18nProvider depends on the testing library you're using.
Below is an example using [react-testing-library](https://testing-library.com/docs/react-testing-library/intro/) and its [wrapper-property](https://testing-library.com/docs/react-testing-library/api#wrapper):
diff --git a/website/docs/installation.mdx b/website/docs/installation.mdx
index 523632205..f5da1f7c8 100644
--- a/website/docs/installation.mdx
+++ b/website/docs/installation.mdx
@@ -15,17 +15,17 @@ Learn how to install Lingui in your project, whether you use JavaScript, React (
## Prerequisites
- Make sure you have [Node.js](https://nodejs.org/) installed (v20 or higher).
-- Install [Lingui CLI](/ref/cli) to manage your translations and catalogs.
+- Install [Lingui CLI](./ref/cli.md) to manage your translations and catalogs.
:::tip
-Don't miss the [Lingui ESLint Plugin](/ref/eslint-plugin) which can help you find and prevent common i18n mistakes in your code.
+Don't miss the [Lingui ESLint Plugin](./ref/eslint-plugin.md) which can help you find and prevent common i18n mistakes in your code.
:::
## Choosing a Transpiler
> A transpiler converts code within a language, transforming newer features into older equivalents for compatibility, or expanding concise syntax into more verbose implementations.
-Lingui needs a transpiler to work. It's responsible for transforming Lingui's JS/JSX components into [ICU MessageFormat](/guides/message-format) and extracting message IDs. Both Babel and SWC transpilers are supported. Follow the Babel or SWC setup depending on what transpiler your project already uses.
+Lingui needs a transpiler to work. It's responsible for transforming Lingui's JS/JSX components into [ICU MessageFormat](./guides/message-format.md) and extracting message IDs. Both Babel and SWC transpilers are supported. Follow the Babel or SWC setup depending on what transpiler your project already uses.
@@ -61,7 +61,7 @@ When using a preset, first check if it includes the `macros` plugin. If so, then
> SWC is an extensible Rust-based platform for the next generation of fast developer tools.
-Lingui supports SWC with a dedicated plugin [`@lingui/swc-plugin`](/ref/swc-plugin). SWC is significantly faster than Babel and is a good choice for large projects.
+Lingui supports SWC with a dedicated plugin [`@lingui/swc-plugin`](./ref/swc-plugin.md). SWC is significantly faster than Babel and is a good choice for large projects.
Follow these steps to set up Lingui with SWC:
@@ -71,10 +71,10 @@ Follow these steps to set up Lingui with SWC:
npm install --save-dev @lingui/swc-plugin
```
-2. [Add necessary configurations](/ref/swc-plugin#usage).
+2. [Add necessary configurations](./ref/swc-plugin.md#usage).
:::caution
-SWC Plugin support is still experimental. Semver backwards compatibility between different `@swc/core` versions is not guaranteed. See the [SWC compatibility](/ref/swc-plugin#swc-compatibility) for more information.
+SWC Plugin support is still experimental. Semver backwards compatibility between different `@swc/core` versions is not guaranteed. See the [SWC compatibility](./ref/swc-plugin.md#swc-compatibility) for more information.
:::
@@ -103,13 +103,13 @@ export default defineConfig({
The configuration above specifies the source locale as English and the target locales as Czech and English.
-According to this configuration, Lingui will extract messages from source files in the `src` directory and write them to message catalogs in `src/locales` (the English catalog would be in `src/locales/en/messages.po`, for example). See [Configuration](/ref/conf) for a complete reference.
+According to this configuration, Lingui will extract messages from source files in the `src` directory and write them to message catalogs in `src/locales` (the English catalog would be in `src/locales/en/messages.po`, for example). See [Configuration](./ref/conf.md) for a complete reference.
:::note
Replace `src` with the name of the directory where you have the source files.
:::
-The PO format is the default and recommended format for message catalogs. See the [Catalog Formats](/ref/catalog-formats) for other available formats.
+The PO format is the default and recommended format for message catalogs. See the [Catalog Formats](./ref/catalog-formats.md) for other available formats.
## Build Tools
@@ -117,7 +117,7 @@ The PO format is the default and recommended format for message catalogs. See th
> Vite is a blazing fast frontend build tool powering the next generation of web applications.
-Lingui supports Vite with a dedicated plugin [`@lingui/vite-plugin`](/ref/vite-plugin). This plugin is responsible for extracting messages from your source code and compiling message catalogs.
+Lingui supports Vite with a dedicated plugin [`@lingui/vite-plugin`](./ref/vite-plugin.md). This plugin is responsible for extracting messages from your source code and compiling message catalogs.
There are two ways to set up Lingui with Vite by using the [`@vitejs/plugin-react`](https://www.npmjs.com/package/@vitejs/plugin-react) or [`@vitejs/plugin-react-swc`](https://www.npmjs.com/package/@vitejs/plugin-react-swc). You need to choose the one that fits your project setup.
@@ -194,7 +194,7 @@ The `@vitejs/plugin-react-swc` plugin uses SWC to transform your code, which is
## See Also
-- [React i18n Tutorial](/tutorials/react)
-- [React Server Components Tutorial](/tutorials/react-rsc)
-- [React Native i18n Tutorial](/tutorials/react-native)
-- [JavaScript i18n Tutorial](/tutorials/javascript)
+- [React i18n Tutorial](./tutorials/react.md)
+- [React Server Components Tutorial](./tutorials/react-rsc.md)
+- [React Native i18n Tutorial](./tutorials/react-native.md)
+- [JavaScript i18n Tutorial](./tutorials/javascript.md)
diff --git a/website/docs/introduction.md b/website/docs/introduction.md
index d52b0c6cf..839489e79 100644
--- a/website/docs/introduction.md
+++ b/website/docs/introduction.md
@@ -23,7 +23,7 @@ Keep your code clean and readable, while the library uses battle-tested and powe
### Universal
-Use it everywhere. [`@lingui/core`](/ref/core) provides the essential intl functionality which works in any JavaScript project, while [`@lingui/react`](/ref/react) offers components for leveraging React rendering, including React Server Components (RSC) support.
+Use it everywhere. [`@lingui/core`](./ref/core.md) provides the essential intl functionality which works in any JavaScript project, while [`@lingui/react`](./ref/react.md) offers components for leveraging React rendering, including React Server Components (RSC) support.
### Full Rich-text Support
@@ -31,7 +31,7 @@ Seamlessly use React components within localized messages, without any restricti
### Powerful Tooling
-Manage your intl workflow with the Lingui [CLI](/ref/cli), [Vite Plugin](/ref/vite-plugin), and [ESLint Plugin](/ref/eslint-plugin). The CLI extracts, compiles and validates messages, while the Vite plugin compiles catalogs on the fly, and the ESLint plugin helps catch common usage errors.
+Manage your intl workflow with the Lingui [CLI](./ref/cli.md), [Vite Plugin](./ref/vite-plugin.md), and [ESLint Plugin](./ref/eslint-plugin.md). The CLI extracts, compiles and validates messages, while the Vite plugin compiles catalogs on the fly, and the ESLint plugin helps catch common usage errors.
### Unopinionated
@@ -53,7 +53,7 @@ Join the growing [community of developers](/community) who are using Lingui to b
Using Lingui for internationalization is a straightforward process. Here's a high-level overview of the workflow.
-
+
### Define Messages
@@ -132,4 +132,4 @@ export default function Lingui({ numUsers, name = "You" }) {
## See Also
-- [Installation and Setup](/installation)
+- [Installation and Setup](./installation.mdx)
diff --git a/website/docs/misc/i18next.md b/website/docs/misc/i18next.md
index 6b0bc917c..eaaa4e510 100644
--- a/website/docs/misc/i18next.md
+++ b/website/docs/misc/i18next.md
@@ -58,7 +58,7 @@ document.getElementById("output").innerHTML = t`Hello world`;
```
:::tip
-This example uses a macro for the translation. Macros are a powerful feature of Lingui that allows you to write messages directly in your code. Read more about [Macros](/ref/macro).
+This example uses a macro for the translation. Macros are a powerful feature of Lingui that allows you to write messages directly in your code. Read more about [Macros](../ref/macro.mdx).
:::
If you prefer to define explicit IDs for your messages, you can follow this approach:
@@ -69,7 +69,7 @@ import { t } from "@lingui/core/macro";
document.getElementById("output").innerHTML = t({ id: "msg.greeting", message: `Hello World` });
```
-Read more about [Explicit vs Generated Message IDs](/guides/explicit-vs-generated-ids).
+Read more about [Explicit vs Generated Message IDs](../guides/explicit-vs-generated-ids.md).
## Interpolation
@@ -181,7 +181,7 @@ i18n.date(d, { dateStyle: "medium", timeStyle: "medium" });
## Plurals
-Lingui uses the [ICU MessageFormat](/guides/message-format) syntax to handle plurals. It provides a simple and translator-friendly approach to plurals localization.
+Lingui uses the [ICU MessageFormat](../guides/message-format.md) syntax to handle plurals. It provides a simple and translator-friendly approach to plurals localization.
For example:
@@ -192,7 +192,7 @@ plural(numBooks, {
});
```
-Under the hood, the [`plural`](/ref/macro#plural) macro is replaced with a low-level [`i18n._`](/ref/core#i18n._) call. In production, the example will look like this:
+Under the hood, the [`plural`](../ref/macro.mdx#plural) macro is replaced with a low-level [`i18n._`](../ref/core.md#i18n._) call. In production, the example will look like this:
```js
i18n._({
@@ -203,7 +203,7 @@ i18n._({
});
```
-When we extract messages from the source code using the [Lingui CLI](/ref/cli), we get:
+When we extract messages from the source code using the [Lingui CLI](../ref/cli.md), we get:
```icu-message-format
{numBooks, plural, one {# book} other {# books}}
@@ -264,7 +264,7 @@ i18next can't do this from its plain JSON files.
## React Integration
-Both libraries provide React components for handling translations in React applications. Lingui provides a set of [React Macros](/ref/macro#react-macros) that simplify writing messages directly in your code. i18next provides a `Trans` component to handle translations in JSX.
+Both libraries provide React components for handling translations in React applications. Lingui provides a set of [React Macros](../ref/macro.mdx#react-macros) that simplify writing messages directly in your code. i18next provides a `Trans` component to handle translations in JSX.
i18next sample:
@@ -295,7 +295,7 @@ This is a rather brief comparison. Both libraries have quite different concepts,
- Supports rich-text messages.
- Provides macros to simplify writing messages directly in your code.
- Provides a CLI tool for extracting and compiling messages.
-- Supports a number of [Catalog Formats](/ref/catalog-formats), including [Custom Formatters](/guides/custom-formatter).
+- Supports a number of [Catalog Formats](../ref/catalog-formats.md), including [Custom Formatters](../guides/custom-formatter.md).
- Is small, fast and flexible. It also has a small bundle footprint by stripping unused messages and loading only necessary catalogs.
- Works with vanilla JS, React (including RSC), Next.js, Node.js, Vue.js etc.
- Is actively maintained.
diff --git a/website/docs/misc/react-intl.md b/website/docs/misc/react-intl.md
index 91e070cde..c354fe77b 100644
--- a/website/docs/misc/react-intl.md
+++ b/website/docs/misc/react-intl.md
@@ -65,7 +65,7 @@ The translator gets the message in one piece: `Read the documentationRead the documentation.
```
-All we need to do is to wrap the message in a [`Trans`](/ref/macro#trans) macro:
+All we need to do is to wrap the message in a [`Trans`](../ref/macro.mdx#trans) macro:
```html
@@ -81,9 +81,9 @@ All we need to do is to wrap the message in a [`Trans`](/ref/macro#trans) macro:
```
-The macro will then parse the [`Trans`](/ref/macro#trans) macro children and automatically generate `message` and `components` props in the form described in the previous section.
+The macro will then parse the [`Trans`](../ref/macro.mdx#trans) macro children and automatically generate `message` and `components` props in the form described in the previous section.
-This is very useful when adding i18n to an existing project. All we need to do is wrap all messages in the [`Trans`](/ref/macro#trans) macro.
+This is very useful when adding i18n to an existing project. All we need to do is wrap all messages in the [`Trans`](../ref/macro.mdx#trans) macro.
Let's compare it to the react-intl solution to see the difference:
@@ -110,7 +110,7 @@ It's also worth mentioning that the message IDs are completely optional. Lingui
The message ID is `Read the <0>documentation0>.` instead of `msg.docs`. Both solutions have pros and cons and the library lets you choose the one which works best for you.
-Read more about [Explicit vs Generated Message IDs](/guides/explicit-vs-generated-ids).
+Read more about [Explicit vs Generated Message IDs](../guides/explicit-vs-generated-ids.md).
:::
## Plurals
@@ -130,7 +130,7 @@ Let's take a look at the original example from react-intl docs:
/>
```
-Using Lingui macros, we could combine [`Trans`](/ref/macro#trans), [`Plural`](/ref/macro#plural-1) components and [`i18n.number`](/ref/core#i18n.number) macro:
+Using Lingui macros, we could combine [`Trans`](../ref/macro.mdx#trans), [`Plural`](../ref/macro.mdx#plural-1) components and [`i18n.number`](../ref/core.md#i18n.number) macro:
```jsx
@@ -195,7 +195,7 @@ Custom IDs are supported as well:
```
:::note
-To inject `i18n` object into props, you need to use [`useLingui`](/ref/react#uselingui) hook. It's very similar to `useIntl` from [react-intl](https://formatjs.io/docs/react-intl/api/#useintl-hook).
+To inject `i18n` object into props, you need to use [`useLingui`](../ref/react.md#uselingui) hook. It's very similar to `useIntl` from [react-intl](https://formatjs.io/docs/react-intl/api/#useintl-hook).
:::
## External Message Catalog
@@ -204,7 +204,7 @@ Let's say our application has been internationalized and we want to send the mes
react-intl comes with the Babel plugin which extracts messages from individual files, but it's up to you to merge them into one file which you can send to translators.
-Lingui provides a handy [`CLI`](/ref/cli) that extracts messages and merges them with any existing translations. It supports both the Babel and SWC ecosystems for extracting messages.
+Lingui provides a handy [`CLI`](../ref/cli.md) that extracts messages and merges them with any existing translations. It supports both the Babel and SWC ecosystems for extracting messages.
## Compiling Messages
diff --git a/website/docs/ref/catalog-formats.md b/website/docs/ref/catalog-formats.md
index 2f2c5fd57..3088b53fb 100644
--- a/website/docs/ref/catalog-formats.md
+++ b/website/docs/ref/catalog-formats.md
@@ -5,7 +5,7 @@ description: Learn about the different catalog formats supported by Lingui
# Catalog Formats
-Catalog format (configured by the [`format`](/ref/conf#format) option) refers to the offline catalog file format. This format is never used in production, because the catalog is compiled into a JS module.
+Catalog format (configured by the [`format`](./conf.md#format) option) refers to the offline catalog file format. This format is never used in production, because the catalog is compiled into a JS module.
The reason for this build step is that the choice of catalog format depends on the individual internationalization workflow. On the other hand, the runtime catalog should be as simple as possible so that it can be parsed quickly without additional overhead.
@@ -78,7 +78,7 @@ msgid "msg.inbox"
msgstr "Message Inbox"
```
-Messages with plurals are exported in [ICU MessageFormat](/guides/message-format):
+Messages with plurals are exported in [ICU MessageFormat](../guides/message-format.md):
```po
msgid "{count, plural, one {Message} other {Messages}}"
@@ -182,8 +182,8 @@ With this format, plural messages are exported in the following ways, depending
This format comes with several caveats and should only be used when using ICU plurals in PO files is not an option:
-- Nested/multiple plurals in a message as shown in [`plural`](/ref/macro#plural) are not supported because they cannot be expressed with gettext plurals. Messages containing nested/multiple formats will not be output correctly.
-- The [`select`](/ref/macro#select) and [`selectOrdinal`](/ref/macro#selectordinal) cannot be expressed with gettext plurals, but the original ICU format is still stored in the `msgid`/`msgstr` properties. To disable the warning that this may not be the expected behavior, add `{ disableSelectWarning: true }` to the [`format`](/ref/conf#format) options.
+- Nested/multiple plurals in a message as shown in [`plural`](./macro.mdx#plural) are not supported because they cannot be expressed with gettext plurals. Messages containing nested/multiple formats will not be output correctly.
+- The [`select`](./macro.mdx#select) and [`selectOrdinal`](./macro.mdx#selectordinal) cannot be expressed with gettext plurals, but the original ICU format is still stored in the `msgid`/`msgstr` properties. To disable the warning that this may not be the expected behavior, add `{ disableSelectWarning: true }` to the [`format`](./conf.md#format) options.
- Source/development languages with more than two plurals could experience difficulties when no custom IDs are used, as gettext cannot have more than two plurals cases identifying an item (`msgid` and `msgid_plural`).
- Gettext doesn't support plurals for negative and fractional numbers even though some languages have special rules for these cases.
@@ -293,7 +293,7 @@ msg.common,String for translation
## See Also
-- [Custom Formatter](/guides/custom-formatter)
+- [Custom Formatter](../guides/custom-formatter.md)
[package-po]: https://www.npmjs.com/package/@lingui/format-po
[package-po-gettext]: https://www.npmjs.com/package/@lingui/format-po-gettext
diff --git a/website/docs/ref/cli.md b/website/docs/ref/cli.md
index 84a291921..a784ff84d 100644
--- a/website/docs/ref/cli.md
+++ b/website/docs/ref/cli.md
@@ -43,7 +43,7 @@ If you use TypeScript, you can add the `--typescript` flag to the `compile` scri
### `--config `
-Path to the configuration file. If not set, the default file is loaded as described in the [Configuration](/ref/conf) reference.
+Path to the configuration file. If not set, the default file is loaded as described in the [Configuration](./conf.md) reference.
## Commands
@@ -64,13 +64,13 @@ The `extract` command scans source files to locate and extract messages, generat
This process involves:
-1. Extracting messages from files based on the `include` and `exclude` settings in the [`catalogs`](/ref/conf#catalogs) section of the configuration file.
+1. Extracting messages from files based on the `include` and `exclude` settings in the [`catalogs`](./conf.md#catalogs) section of the configuration file.
2. Merging the newly extracted messages with any existing message catalogs.
3. Updating and saving the message catalogs.
4. Printing extraction statistics for each language, including the total number of messages and any missing translations.
:::tip
-Refer to the [Message Extraction](/guides/message-extraction) guide to learn more about this process and the options available.
+Refer to the [Message Extraction](../guides/message-extraction.md) guide to learn more about this process and the options available.
:::
#### `files` {#extract-files}
@@ -103,11 +103,11 @@ However, over time, some messages may be removed from the source code. You can u
#### `--overwrite` {#extract-overwrite}
-Update translations for [`sourceLocale`](/ref/conf#sourcelocale) from source.
+Update translations for [`sourceLocale`](./conf.md#sourcelocale) from source.
#### `--format ` {#extract-format}
-Extract message catalogs to the specified file format (see the [`format`](/ref/conf#format) option for more details).
+Extract message catalogs to the specified file format (see the [`format`](./conf.md#format) option for more details).
#### `--locale ` {#extract-locale}
@@ -115,7 +115,7 @@ Extract data for the specified locales only.
#### `--convert-from ` {#extract-convert-from}
-Convert message catalogs from the previous format (see the [`format`](/ref/conf#format) option for more details).
+Convert message catalogs from the previous format (see the [`format`](./conf.md#format) option for more details).
#### `--verbose` {#extract-verbose}
@@ -155,7 +155,7 @@ lingui compile
[--watch [--debounce ]]
```
-Once you have all the catalogs ready and translated, you can use this command to compile all the catalogs into minified JS/TS files. It compiles message catalogs located in the [`path`](/ref/conf#catalogs) directory and generates minified JavaScript files. The resulting file is a string that is parsed into a plain JS object using `JSON.parse`.
+Once you have all the catalogs ready and translated, you can use this command to compile all the catalogs into minified JS/TS files. It compiles message catalogs located in the [`path`](./conf.md#catalogs) directory and generates minified JavaScript files. The resulting file is a string that is parsed into a plain JS object using `JSON.parse`.
The output looks like this:
@@ -172,7 +172,7 @@ Messages added to the compiled file are collected in a specific order:
3. Translated message from default fallback locale.
4. Message key.
-It is also possible to merge the translated catalogs into a single file per locale by specifying [`catalogsMergePath`](/ref/conf#catalogsmergepath) in the configuration.
+It is also possible to merge the translated catalogs into a single file per locale by specifying [`catalogsMergePath`](./conf.md#catalogsmergepath) in the configuration.
:::tip
The compiled files can be safely ignored by your version control system, since these files must be created each time you deploy to production. We recommend you to create the compiled catalogs in CI as part of your deployment process. Always remember to **use compiled catalogs** in deployments.
@@ -193,7 +193,7 @@ Fail if a catalog has missing translations.
#### `--format ` {#compile-format}
-Format of message catalogs (see the [`format`](/ref/conf#format) option for more details).
+Format of message catalogs (see the [`format`](./conf.md#format) option for more details).
#### `--verbose` {#compile-verbose}
@@ -201,11 +201,11 @@ Print additional information.
#### `--namespace` {#compile-namespace}
-Specify the namespace for compiled message catalogs (see also [`compileNamespace`](/ref/conf#compilenamespace) for global configuration).
+Specify the namespace for compiled message catalogs (see also [`compileNamespace`](./conf.md#compilenamespace) for global configuration).
#### `--typescript` {#compile-typescript}
-Is the same as using [`compileNamespace`](/ref/conf#compilenamespace) with the value "ts". Generates a `{compiledFile}.ts` file and the exported object is typed using TS.
+Is the same as using [`compileNamespace`](./conf.md#compilenamespace) with the value "ts". Generates a `{compiledFile}.ts` file and the exported object is typed using TS.
#### `--watch` {#compile-watch}
@@ -217,17 +217,17 @@ Delays compilation by `` milliseconds to avoid multiple compilations for
## Configuring the Source Locale
-One limitation of checking for missing translations is that the English message catalog typically does not require translations since our source code is in English. This issue can be resolved by configuring the [`sourceLocale`](/ref/conf#sourcelocale) in the configuration file.
+One limitation of checking for missing translations is that the English message catalog typically does not require translations since our source code is in English. This issue can be resolved by configuring the [`sourceLocale`](./conf.md#sourcelocale) in the configuration file.
## Compiling Catalogs in CI {#compiling-catalogs-in-ci}
-If you're using CI, it's a good idea to add the `compile` command to your build process. Alternatively, you can also use a [Webpack loader](/ref/loader), [Vite plugin](/ref/vite-plugin) or [Metro transformer](/ref/metro-transformer).
+If you're using CI, it's a good idea to add the `compile` command to your build process. Alternatively, you can also use a [Webpack loader](./loader.md), [Vite plugin](./vite-plugin.md) or [Metro transformer](./metro-transformer.mdx).
-Depending on your localization setup, you might also want to run the `extract` command in CI and upload the extracted messages to a [translation service](/tools/introduction).
+Depending on your localization setup, you might also want to run the `extract` command in CI and upload the extracted messages to a [translation service](../tools/introduction.md).
## See Also
-- [Lingui Configuration](/ref/conf)
-- [Message Extraction](/guides/message-extraction)
-- [Catalog Formats](/ref/catalog-formats)
-- [Custom Extractor](/guides/custom-extractor)
+- [Lingui Configuration](./conf.md)
+- [Message Extraction](../guides/message-extraction.md)
+- [Catalog Formats](./catalog-formats.md)
+- [Custom Extractor](../guides/custom-extractor.md)
diff --git a/website/docs/ref/conf.md b/website/docs/ref/conf.md
index d55ea8b03..98707ea23 100644
--- a/website/docs/ref/conf.md
+++ b/website/docs/ref/conf.md
@@ -5,7 +5,7 @@ description: Learn how to configure Lingui for your project
# Configuration
-The following reference covers all supported configuration options in Lingui. To learn more about configuring Lingui, read the [Installation and Setup](/installation) guide.
+The following reference covers all supported configuration options in Lingui. To learn more about configuring Lingui, read the [Installation and Setup](../installation.mdx) guide.
By default, Lingui looks for the configuration in the following locations:
@@ -31,7 +31,7 @@ Default value:
];
```
-The `catalogs` configuration defines the location of message catalogs and specifies which files are included when the [`extract`](/ref/cli#extract) command scans for messages.
+The `catalogs` configuration defines the location of message catalogs and specifies which files are included when the [`extract`](./cli.md#extract) command scans for messages.
- `path`: the directory where the message catalogs are located. It should not end with a slash and must not include a file extension, which depends on the [`format`](#format) configuration. The `{locale}` token will be replaced by the catalog's locale.
- `include` and `exclude`: these patterns specify which files to include or exclude during the extraction process. They are passed to [minimatch](https://github.com/isaacs/minimatch) for pattern matching.
@@ -158,7 +158,7 @@ components/
Default value: `[]`
-The locale tags used in the project. The [`extract`](/ref/cli#extract) and [`compile`](/ref/cli#compile) commands write a catalog for each locale specified. Each locale should be a valid [BCP-47 code](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html):
+The locale tags used in the project. The [`extract`](./cli.md#extract) and [`compile`](./cli.md#compile) commands write a catalog for each locale specified. Each locale should be a valid [BCP-47 code](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html):
```json
{
@@ -217,13 +217,13 @@ Default value: `""`
Locale used for pseudolocalization. For example, when you set `pseudoLocale: "en"`, all messages in the `en` catalog will be pseudo-localized. The locale must be included in the `locales` config.
-Read more about [Pseudolocalization](/guides/pseudolocalization).
+Read more about [Pseudolocalization](../guides/pseudolocalization.md).
## catalogsMergePath
Default value: `""`
-Define the path where translated catalogs are merged into a single file per locale during the [`compile`](/ref/cli#compile) process.
+Define the path where translated catalogs are merged into a single file per locale during the [`compile`](./cli.md#compile) process.
#### Example
@@ -272,7 +272,7 @@ Using the `catalogsMergePath`, separate catalogs can be merged into a single fil
Default value: `cjs`
-Specify namespace for exporting compiled messages. See [`compile`](/ref/cli#compile) command.
+Specify namespace for exporting compiled messages. See [`compile`](./cli.md#compile) command.
#### cjs
@@ -390,7 +390,7 @@ export default defineConfig({
});
```
-Read more about available formatters in [Catalog Formats](/ref/catalog-formats) or create your own [Custom Formatter](/guides/custom-formatter).
+Read more about available formatters in [Catalog Formats](./catalog-formats.md) or create your own [Custom Formatter](../guides/custom-formatter.md).
## orderBy
@@ -422,9 +422,9 @@ Note that using `` as a string token in any other path-based config set
Default value: `["@lingui/core", "i18n"]`
-This setting specifies the module path for the exported `i18n` object. The first value in the array is the module path, and the second is the name of the import. This configuration is essential for [macros](/ref/macro) that need to reference the global `i18n` object.
+This setting specifies the module path for the exported `i18n` object. The first value in the array is the module path, and the second is the name of the import. This configuration is essential for [macros](./macro.mdx) that need to reference the global `i18n` object.
-You only need to set this value if you use custom object created using [`setupI18n`](/ref/core#setupi18n):
+You only need to set this value if you use custom object created using [`setupI18n`](./core.md#setupi18n):
For example, if you have a custom module that exports the `i18n` object:
@@ -450,7 +450,7 @@ import { myI18n } from "./custom-i18n-config";
}
```
-In more advanced scenarios, you may need to change the module from which the [`Trans`](/ref/macro#trans) or [`useLingui`](/ref/macro#uselingui) macros are imported:
+In more advanced scenarios, you may need to change the module from which the [`Trans`](./macro.mdx#trans) or [`useLingui`](./macro.mdx#uselingui) macros are imported:
```jsx
import { Trans, useLingui } from "./custom-config";
@@ -479,7 +479,7 @@ Extractors it's the way to customize which extractor you want for your codebase.
}
```
-See the [Custom Extractor](/guides/custom-extractor) guide for instructions on creating your own extractor.
+See the [Custom Extractor](../guides/custom-extractor.md) guide for instructions on creating your own extractor.
## macro.corePackage
diff --git a/website/docs/ref/core.md b/website/docs/ref/core.md
index fc18380f2..7c5022eec 100644
--- a/website/docs/ref/core.md
+++ b/website/docs/ref/core.md
@@ -37,7 +37,7 @@ const translation = i18n._("Hello World");
```
:::info Advanced
-If you prefer not to use the global `i18n` instance and want to set up your own, you can utilize the [`setupI18n`](#setupi18n) method. Additionally, you'll need to configure the [`runtimeConfigModule`](/ref/conf#runtimeconfigmodule) to ensure that macros work properly.
+If you prefer not to use the global `i18n` instance and want to set up your own, you can utilize the [`setupI18n`](#setupi18n) method. Additionally, you'll need to configure the [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) to ensure that macros work properly.
:::
## Class `i18n()` {#i18n}
@@ -99,9 +99,9 @@ i18n.load({
```
:::tip
-Don't write catalogs manually. The code above is an example of message catalogs. In real applications, messages are loaded from external message catalogs generated by the [`compile`](/ref/cli#compile) command or by using tools such as [Vite Plugin](/ref/vite-plugin), [Webpack Loader](/ref/loader), or [Metro Transformer](/ref/metro-transformer).
+Don't write catalogs manually. The code above is an example of message catalogs. In real applications, messages are loaded from external message catalogs generated by the [`compile`](./cli.md#compile) command or by using tools such as [Vite Plugin](./vite-plugin.md), [Webpack Loader](./loader.md), or [Metro Transformer](./metro-transformer.mdx).
-Formatting of messages as strings (e.g: `"My name is {name}"`) works in development only, when messages are parsed on the fly. In production, however, messages must be compiled using the [`compile`](/ref/cli#compile) command.
+Formatting of messages as strings (e.g: `"My name is {name}"`) works in development only, when messages are parsed on the fly. In production, however, messages must be compiled using the [`compile`](./cli.md#compile) command.
Here's how the same example would look in a real application:
@@ -208,7 +208,7 @@ i18n._({
});
```
-Read more about [Message Descriptor](/ref/macro#core-macros).
+Read more about [Message Descriptor](./macro.mdx#core-macros).
### `i18n.t(...)` {#i18n.t}
@@ -279,7 +279,7 @@ Initialize and return a new `I18n` instance. Typically, you should call this fun
:::info
You typically don't need to set up your own `i18n` instance. In most cases, you can use the global `i18n` object exported from `@lingui/core` directly.
-However, if you need to do this, make sure to also configure the [`runtimeConfigModule`](/ref/conf#runtimeconfigmodule) to ensure macros work properly.
+However, if you need to do this, make sure to also configure the [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) to ensure macros work properly.
:::
```ts
@@ -410,7 +410,7 @@ The `change` event is triggered **after** changing the locale or loading a new m
### `missing`
-The `missing` event is triggered when a translation is requested using [`i18n._`](/ref/core#i18n._) that does not exist in the messages of the active locale.The event provides information about the locale and the missing message ID.
+The `missing` event is triggered when a translation is requested using [`i18n._`](./core.md#i18n._) that does not exist in the messages of the active locale.The event provides information about the locale and the missing message ID.
```ts
i18n.on("missing", (event) => {
diff --git a/website/docs/ref/extractor-vue.md b/website/docs/ref/extractor-vue.md
index fd3d6e9c1..b050498a7 100644
--- a/website/docs/ref/extractor-vue.md
+++ b/website/docs/ref/extractor-vue.md
@@ -32,5 +32,5 @@ export default defineConfig({
## See Also
-- [Message Extraction](/guides/message-extraction)
-- [Custom Extractor](/guides/custom-extractor)
+- [Message Extraction](../guides/message-extraction.md)
+- [Custom Extractor](../guides/custom-extractor.md)
diff --git a/website/docs/ref/loader.md b/website/docs/ref/loader.md
index a610b9c70..f881be252 100644
--- a/website/docs/ref/loader.md
+++ b/website/docs/ref/loader.md
@@ -1,6 +1,6 @@
# Webpack Loader
-The `@lingui/loader` is a Webpack loader for Lingui message catalogs. It offers an alternative to the [`lingui compile`](/ref/cli#compile) and compiles catalogs on the fly.
+The `@lingui/loader` is a Webpack loader for Lingui message catalogs. It offers an alternative to the [`lingui compile`](./cli.md#compile) and compiles catalogs on the fly.
It enables you to `import` `.po` files directly, instead of running `lingui compile` and `import`ing the resulting JavaScript (or TypeScript) files.
@@ -39,5 +39,5 @@ const { messages } = (await import(`@lingui/loader!./locales/${locale}/messages.
## See Also
-- [Dynamic Loading of Message Catalogs](/guides/dynamic-loading-catalogs)
-- [Catalog Formats](/ref/catalog-formats)
+- [Dynamic Loading of Message Catalogs](../guides/dynamic-loading-catalogs.md)
+- [Catalog Formats](./catalog-formats.md)
diff --git a/website/docs/ref/macro.mdx b/website/docs/ref/macro.mdx
index 25f46fe37..d01b42652 100644
--- a/website/docs/ref/macro.mdx
+++ b/website/docs/ref/macro.mdx
@@ -5,7 +5,7 @@ description: Transform JavaScript objects and JSX elements into ICU MessageForma
# Macros
-Lingui Macro provides powerful macros to transform JavaScript objects and JSX elements into [ICU MessageFormat](/guides/message-format) messages at compile time. It provides a more efficient and developer-friendly way to handle internationalization in your project.
+Lingui Macro provides powerful macros to transform JavaScript objects and JSX elements into [ICU MessageFormat](../guides/message-format.md) messages at compile time. It provides a more efficient and developer-friendly way to handle internationalization in your project.
The benefits of using macros:
@@ -19,7 +19,7 @@ There are two types of macros: [Core Macros](#core-macros) (JS) and [React Macro
## Core Macros
-Core (JS) Macros can be used in any JavaScript context (e.g. outside JSX). All JS macros are transformed into a _Message Descriptor_ wrapped inside of [`i18n._`](/ref/core#i18n._) call:
+Core (JS) Macros can be used in any JavaScript context (e.g. outside JSX). All JS macros are transformed into a _Message Descriptor_ wrapped inside of [`i18n._`](./core.md#i18n._) call:
```js
import { t } from "@lingui/core/macro";
@@ -54,7 +54,7 @@ type MessageDescriptor = {
The `id` is the message ID and is the only parameter required. The `id` and `message` are extracted into the message catalog. Only `id` and `values` are used at runtime, all other attributes are removed from the production code for size optimization.
-You don't need to specify the ID manually. By default, Macro will automatically create a short ID from your message. However, you can explicitly specify a custom ID. Read more about [Explicit vs Generated IDs](/guides/explicit-vs-generated-ids).
+You don't need to specify the ID manually. By default, Macro will automatically create a short ID from your message. However, you can explicitly specify a custom ID. Read more about [Explicit vs Generated IDs](../guides/explicit-vs-generated-ids.md).
### `t`
@@ -113,7 +113,7 @@ const message = i18n._(
```
:::info
-By default the `i18n` object is imported from [`@lingui/core`](/ref/core). If you are using a custom instance of the `i18n` object, you need to set [`runtimeConfigModule`](/ref/conf#runtimeconfigmodule) or pass a custom instance to [`t`](/ref/macro#t).
+By default the `i18n` object is imported from [`@lingui/core`](./core.md). If you are using a custom instance of the `i18n` object, you need to set [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) or pass a custom instance to [`t`](./macro.mdx#t).
:::
It's also possible to pass custom `id` and `comment` for translators by calling `t` macro with a message descriptor:
@@ -193,7 +193,7 @@ const message = i18n._(
Choose the plural forms used in your source code based on the pluralization rules of your source locale.
:::
-If you need to add variables in plural form, you can use template string literals. This time you don't need the [`t`](/ref/macro#t) macro, because template strings are transformed automatically:
+If you need to add variables in plural form, you can use template string literals. This time you don't need the [`t`](./macro.mdx#t) macro, because template strings are transformed automatically:
```js
import { plural } from "@lingui/core/macro";
@@ -368,7 +368,7 @@ const message = t({
### `defineMessage` / `msg` {#definemessage}
-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).
+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.md).
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:
@@ -420,7 +420,7 @@ type MacroMessageDescriptor = {
};
```
-Either the `id` or `message` property is required. `id` is a custom message ID. If it isn't set, the `message` (and `context` if present) will be used to generate an ID. Read more about [Explicit vs Generated IDs](/guides/explicit-vs-generated-ids).
+Either the `id` or `message` property is required. `id` is a custom message ID. If it isn't set, the `message` (and `context` if present) will be used to generate an ID. Read more about [Explicit vs Generated IDs](../guides/explicit-vs-generated-ids.md).
```js
import { defineMessage } from "@lingui/core/macro";
@@ -501,7 +501,7 @@ const message = /*i18n*/ {
## React Macros
-React (JSX) Macros are used in JSX elements and are transformed into the [`Trans`](/ref/react#trans) component imported from the [`@lingui/react`](/ref/react) package.
+React (JSX) Macros are used in JSX elements and are transformed into the [`Trans`](./react.md#trans) component imported from the [`@lingui/react`](./react.md) package.
### `Trans`
@@ -528,7 +528,7 @@ Available Props:
#### `id`
-Each message in the catalog is identified by a message ID. While macro uses message (and `context` property if provided) to generate the ID, it's possible to provide custom ID. Read more about [Explicit vs Generated IDs](/guides/explicit-vs-generated-ids).
+Each message in the catalog is identified by a message ID. While macro uses message (and `context` property if provided) to generate the ID, it's possible to provide custom ID. Read more about [Explicit vs Generated IDs](../guides/explicit-vs-generated-ids.md).
```jsx
import { Trans } from "@lingui/react/macro";
@@ -542,11 +542,11 @@ import { Trans } from "@lingui/react";
#### `comment`
-Comment for translators to give them additional information about the message. It will be visible in the [TMS](/tools/introduction) if it is supported, and in the [catalog format](/ref/catalog-formats). It will be removed from production code.
+Comment for translators to give them additional information about the message. It will be visible in the [TMS](../tools/introduction.md) if it is supported, and in the [catalog format](./catalog-formats.md). It will be removed from production code.
#### `context`
-Allows to extract the same messages with different IDs. It is useful when the same message has different meanings in different contexts. See [Context](/guides/explicit-vs-generated-ids#context) for more details.
+Allows to extract the same messages with different IDs. It is useful when the same message has different meanings in different contexts. See [Context](../guides/explicit-vs-generated-ids.md#context) for more details.
Similarly to [`comment`](#comment), it will be added to the message catalog, visible in TMS and will be removed from the production code:
@@ -585,7 +585,7 @@ Components and HTML tags are replaced by dummy indexed tags (`<0>0>`) which ha
#### `render`
-Custom render callback to render translation. This prop is passed directly to the [`Trans`](/ref/react#trans) component from the [`@lingui/react`](/ref/react) package.
+Custom render callback to render translation. This prop is passed directly to the [`Trans`](./react.md#trans) component from the [`@lingui/react`](./react.md) package.
### `Plural`
@@ -706,7 +706,7 @@ The select cases except `other` should be prefixed with underscore: `_male` or `
### `useLingui`
-The `useLingui` React macro gives access to a [`t`](/ref/macro#t) macro that is bound to the local `i18n` object passed from the React context.
+The `useLingui` React macro gives access to a [`t`](./macro.mdx#t) macro that is bound to the local `i18n` object passed from the React context.
It returns an object with the following content:
@@ -767,14 +767,14 @@ function getColors() {
```
:::tip
-There is an [ESLint Plugin](/ref/eslint-plugin) rule designed to check for this misuse: [`t-call-in-function`](https://github.com/lingui/eslint-plugin/blob/main/docs/rules/t-call-in-function.md).
+There is an [ESLint Plugin](./eslint-plugin.md) rule designed to check for this misuse: [`t-call-in-function`](https://github.com/lingui/eslint-plugin/blob/main/docs/rules/t-call-in-function.md).
:::
-A better option would be to use the [Lazy Translations](/guides/lazy-translations) pattern.
+A better option would be to use the [Lazy Translations](../guides/lazy-translations.md) pattern.
### Global `i18n` Instance
-When you use the [`t`](#t) macro (or [`plural`](#plural), [`select`](#select), [`selectOrdinal`](#selectordinal)), it uses a global [`i18n`](/ref/core#i18n) instance. While this generally works, there are situations, such as server-side rendering (SSR) applications, where it may not be the best solution.
+When you use the [`t`](#t) macro (or [`plural`](#plural), [`select`](#select), [`selectOrdinal`](#selectordinal)), it uses a global [`i18n`](./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:
diff --git a/website/docs/ref/metro-transformer.mdx b/website/docs/ref/metro-transformer.mdx
index 1ba30f49f..e6933d687 100644
--- a/website/docs/ref/metro-transformer.mdx
+++ b/website/docs/ref/metro-transformer.mdx
@@ -5,7 +5,7 @@ description: Use Lingui with React Native and compile your message catalogs on t
# Metro Transformer
-[Metro bundler](https://metrobundler.dev/) is a JavaScript bundler used in React Native apps. The `@lingui/metro-transformer` offers an alternative to the [`lingui compile`](/ref/cli#compile) command: a transformer that enables Metro to compile `.po` files on the fly.
+[Metro bundler](https://metrobundler.dev/) is a JavaScript bundler used in React Native apps. The `@lingui/metro-transformer` offers an alternative to the [`lingui compile`](./cli.md#compile) command: a transformer that enables Metro to compile `.po` files on the fly.
The transformer enables you to `import` `.po` files directly, instead of running `lingui compile` and `import`ing the resulting JavaScript (or TypeScript) files.
@@ -111,5 +111,5 @@ Whenever you make a change to the Lingui config file (this should not happen oft
## See Also
-- [React Native i18n Tutorial](/tutorials/react-native)
+- [React Native i18n Tutorial](../tutorials/react-native.md)
- [Lingui React Native Example App](https://github.com/lingui/js-lingui/tree/main/examples/react-native)
diff --git a/website/docs/ref/react.md b/website/docs/ref/react.md
index 1d03bf3f0..7a331fd86 100644
--- a/website/docs/ref/react.md
+++ b/website/docs/ref/react.md
@@ -132,12 +132,12 @@ The `useLingui` hook provides access to the Lingui context. It returns an object
| Key | Type | Description |
| ------------------ | --------------------- | ----------------------------------------------------------------------- |
| `i18n` | `I18n` | The `I18n` object instance that you passed to `I18nProvider` |
-| `_` | `I18n[_]` | Reference to the [`i18n._`](/ref/core#i18n._) function, explained below |
+| `_` | `I18n[_]` | Reference to the [`i18n._`](./core.md#i18n._) function, explained below |
| `defaultComponent` | `React.ComponentType` | The same `defaultComponent` you passed to `I18nProvider`, if provided |
-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)).
+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.md#memoization-pitfall)).
-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:
+To alleviate the issue, `useLingui` provides the `_` function, which is the same as [`i18n._`](./core.md#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:
```jsx
import React from "react";
@@ -156,7 +156,7 @@ const CurrentLocale = () => {
```
:::tip
-There is a [macro version](/ref/macro#uselingui) of the `useLingui` hook which supports all features of the [`t` macro](/ref/macro#t) and uses the runtime `useLingui` hook (from `@lingui/react`) under the hood:
+There is a [macro version](./macro.mdx#uselingui) of the `useLingui` hook which supports all features of the [`t` macro](./macro.mdx#t) and uses the runtime `useLingui` hook (from `@lingui/react`) under the hood:
```jsx
import { useLingui } from "@lingui/react/macro";
@@ -177,7 +177,7 @@ You also can safely use the returned `t` function in a dependency array of React
The `@lingui/react` package provides the `Trans` component for rendering translations in your application. It is a low-level component that allows you to render translations with dynamic values and components.
:::caution
-While this component is available, you will likely find [Macros](/ref/macro) to be more convenient and developer-friendly. Macros simplify the translation process and reduce boilerplate code.
+While this component is available, you will likely find [Macros](./macro.mdx) to be more convenient and developer-friendly. Macros simplify the translation process and reduce boilerplate code.
:::
This section serves as a reference for those who prefer to use the components directly.
@@ -193,7 +193,7 @@ This section serves as a reference for those who prefer to use the components di
The `values` and `components` props allow to pass dynamic values and components used for formatting the translation. In addition, the `comment` prop provides context to translators, helping them to understand the intent behind the message.
:::tip
-Import the [`Trans`](/ref/macro#trans) macro instead if you use macros. It will be transformed into the runtime `Trans` component automatically:
+Import the [`Trans`](./macro.mdx#trans) macro instead if you use macros. It will be transformed into the runtime `Trans` component automatically:
```jsx
import { Trans } from "@lingui/react/macro";
@@ -240,7 +240,7 @@ const MyComponent = () => {
#### Plurals
-If for some reason you cannot use [Macros](/ref/macro), you can render plurals using the simple `Trans` component by passing the [ICU MessageFormat](/guides/message-format) string as the `message` prop:
+If for some reason you cannot use [Macros](./macro.mdx), you can render plurals using the simple `Trans` component by passing the [ICU MessageFormat](../guides/message-format.md) string as the `message` prop:
```jsx
import React from "react";
diff --git a/website/docs/ref/swc-plugin.md b/website/docs/ref/swc-plugin.md
index 6626a07f1..604112b87 100644
--- a/website/docs/ref/swc-plugin.md
+++ b/website/docs/ref/swc-plugin.md
@@ -7,7 +7,7 @@ description: Use Lingui Macros in your SWC project
[SWC](https://swc.rs/) is an extensible Rust-based platform for the next generation of fast developer tools.
-If you're using SWC in your project, you can opt for the `@lingui/swc-plugin`. This plugin, designed for SWC, is a Rust version of [Lingui Macros](/ref/macro).
+If you're using SWC in your project, you can opt for the `@lingui/swc-plugin`. This plugin, designed for SWC, is a Rust version of [Lingui Macros](./macro.mdx).
[](https://www.npmjs.com/package/@lingui/swc-plugin)
[](https://www.npmjs.com/package/@lingui/swc-plugin)
@@ -105,7 +105,7 @@ You can configure the plugin by passing the `runtimeModules` option. This option
]
```
-For more details, refer to the [Runtime Configuration](/ref/conf#runtimeconfigmodule) section of the documentation.
+For more details, refer to the [Runtime Configuration](./conf.md#runtimeconfigmodule) section of the documentation.
### Strip Non-Essential Fields
diff --git a/website/docs/ref/vite-plugin.md b/website/docs/ref/vite-plugin.md
index e806e5bfb..9bf616e86 100644
--- a/website/docs/ref/vite-plugin.md
+++ b/website/docs/ref/vite-plugin.md
@@ -20,7 +20,7 @@ Install `@lingui/vite-plugin` as a development dependency:
npm install --save-dev @lingui/vite-plugin
```
-For a complete installation guide, see [Installation and Setup](/installation#vite).
+For a complete installation guide, see [Installation and Setup](../installation.mdx#vite).
## Usage
@@ -59,5 +59,5 @@ const { messages } = await import(`./locales/${language}.json?lingui`);
## See Also
-- [Dynamic Loading](/guides/dynamic-loading-catalogs)
+- [Dynamic Loading](../guides/dynamic-loading-catalogs.md)
- [Dynamic Import in Vite](https://vitejs.dev/guide/features.html#dynamic-import)
diff --git a/website/docs/releases/migration-3.md b/website/docs/releases/migration-3.md
index f55c42a55..d0fbf563a 100644
--- a/website/docs/releases/migration-3.md
+++ b/website/docs/releases/migration-3.md
@@ -14,24 +14,24 @@ Minimal required versions are:
### `@lingui/react`
-- `` render-prop component was removed in favor of [`useLingui`](/ref/react#uselingui) hook.
-- In [`I18nProvider`](/ref/react#i18nprovider), `defaultRender` prop was renamed to `defaultComponent`, and now only accepts Custom Components
-- In [`Trans`](/ref/react#trans), `defaults` prop was renamed to `message` and `description` to `comment`.
-- In [`Trans`](/ref/react#trans), `render` prop only accepts render-prop function which is used to render translation.
-- In [`Trans`](/ref/react#trans), new prop `component` accepts React component which is used to render translation.
-- In [`Trans`](/ref/react#trans), `components` is now an object, not an array. When using the low level API, it allows to name the component placeholders:
+- `` render-prop component was removed in favor of [`useLingui`](../ref/react.md#uselingui) hook.
+- In [`I18nProvider`](../ref/react.md#i18nprovider), `defaultRender` prop was renamed to `defaultComponent`, and now only accepts Custom Components
+- In [`Trans`](../ref/react.md#trans), `defaults` prop was renamed to `message` and `description` to `comment`.
+- In [`Trans`](../ref/react.md#trans), `render` prop only accepts render-prop function which is used to render translation.
+- In [`Trans`](../ref/react.md#trans), new prop `component` accepts React component which is used to render translation.
+- In [`Trans`](../ref/react.md#trans), `components` is now an object, not an array. When using the low level API, it allows to name the component placeholders:
```jsx
}} />
```
-- `NumberFormat` and `DateFormat` components were removed. Import `i18n` from `@lingui/core` package and use [`i18n.date()`](/ref/core#i18n.date) and [`i18n.number()`](/ref/core#i18n.number) instead.
+- `NumberFormat` and `DateFormat` components were removed. Import `i18n` from `@lingui/core` package and use [`i18n.date()`](../ref/core.md#i18n.date) and [`i18n.number()`](../ref/core.md#i18n.number) instead.
#### Removed `I18nProvider` declarative API
-LinguiJS started as a React library. After `@lingui/core` package was introduced, there were two ways how to switch active locales and manage catalogs in React: either using [`I18nProvider`](/ref/react#i18nprovider) declarative API or using `setupI18n` imperative API.
+LinguiJS started as a React library. After `@lingui/core` package was introduced, there were two ways how to switch active locales and manage catalogs in React: either using [`I18nProvider`](../ref/react.md#i18nprovider) declarative API or using `setupI18n` imperative API.
-In the same spirit as `@apollo/react` and `react-redux`, the [`I18nProvider`](/ref/react#i18nprovider) is simplified and accepts `i18n` manager, which must be created manually:
+In the same spirit as `@apollo/react` and `react-redux`, the [`I18nProvider`](../ref/react.md#i18nprovider) is simplified and accepts `i18n` manager, which must be created manually:
```diff
import { I18nProvider } from '@lingui/react'
@@ -66,21 +66,21 @@ In the same spirit as `@apollo/react` and `react-redux`, the [`I18nProvider`](/r
```
:::caution Note
- If you decide to use custom `i18n` instance, you also need to set [`runtimeConfigModule`](/ref/conf#runtimeconfigmodule). Macros automatically import `i18n` instance and must be aware of correct import path.
+ If you decide to use custom `i18n` instance, you also need to set [`runtimeConfigModule`](../ref/conf.md#runtimeconfigmodule). Macros automatically import `i18n` instance and must be aware of correct import path.
:::
- `i18n.t`, `i18n.plural`, `i18n.select` and `i18n.selectOrdinal` methods were removed in favor of macros.
- `i18n.use` was removed. Using two locales at the same time isn't common use-case and can be solved in user land by having two instances of `i18n` object.
-- Signature of [`i18n._`](/ref/core#i18n._) method has changed. The third parameter now accepts default message in `message` prop, instead of `defaults`:
+- Signature of [`i18n._`](../ref/core.md#i18n._) method has changed. The third parameter now accepts default message in `message` prop, instead of `defaults`:
```diff
- i18n._('Welcome / Greetings', { name: 'Joe' }, { defaults: "Hello {name}" })
+ i18n._('Welcome / Greetings', { name: 'Joe' }, { message: "Hello {name}" })
```
-- [`i18n._`](/ref/core#i18n._) also accepts a message descriptor as a first parameter:
+- [`i18n._`](../ref/core.md#i18n._) also accepts a message descriptor as a first parameter:
```diff
i18n._({
@@ -90,9 +90,9 @@ In the same spirit as `@apollo/react` and `react-redux`, the [`I18nProvider`](/r
})
```
-#### [`i18n.load`](/ref/core#i18n.load) loads a catalog for a single locale
+#### [`i18n.load`](../ref/core.md#i18n.load) loads a catalog for a single locale
-`i18n` manager is the single source of truth and there's no need to keep all catalogs loaded outside this object. To make loading easier, [`i18n.load`](/ref/core#i18n.load) now accepts catalog for a single locale or multiple catalogs at once.
+`i18n` manager is the single source of truth and there's no need to keep all catalogs loaded outside this object. To make loading easier, [`i18n.load`](../ref/core.md#i18n.load) now accepts catalog for a single locale or multiple catalogs at once.
```diff
import { i18n } from "@lingui/core"
@@ -103,7 +103,7 @@ In the same spirit as `@apollo/react` and `react-redux`, the [`I18nProvider`](/r
```
:::caution Note
-You can still use [`i18n.load`](/ref/core#i18n.load) to load all catalogs at once:
+You can still use [`i18n.load`](../ref/core.md#i18n.load) to load all catalogs at once:
```jsx
// i18n.js
@@ -121,7 +121,7 @@ i18n.load({
### `@lingui/macro`
-- [`plural`](/ref/macro#plural), [`select`](/ref/macro#select) and [`selectOrdinal`](/ref/macro#selectordinal) accepts value as a first parameter:
+- [`plural`](../ref/macro.mdx#plural), [`select`](../ref/macro.mdx#select) and [`selectOrdinal`](../ref/macro.mdx#selectordinal) accepts value as a first parameter:
```diff
- plural({ value, one: "# book", other: "# books" })
@@ -135,7 +135,7 @@ i18n.load({
### Whitespace
-Whitespace handling in plugins had few bugs. By fixing them, there might be few backward incompatible changes. It's advised to run [`extract`](/ref/cli#extract) and inspect changes in catalogs (if any).
+Whitespace handling in plugins had few bugs. By fixing them, there might be few backward incompatible changes. It's advised to run [`extract`](../ref/cli.md#extract) and inspect changes in catalogs (if any).
1. Spaces before `{variables}` in JSX aren't preserved. This is how React handles whitespaces in JSX. Leading whitespace is always removed:
@@ -182,7 +182,7 @@ Plugins are replaced with macros. Presets are removed completely because they ar
}
```
-2. Import [`Trans`](/ref/macro#trans), [`Plural`](/ref/macro#plural-1), [`Select`](/ref/macro#select-1) and [`SelectOrdinal`](/ref/macro#selectordinal-1) from `@lingui/macro`:
+2. Import [`Trans`](../ref/macro.mdx#trans), [`Plural`](../ref/macro.mdx#plural-1), [`Select`](../ref/macro.mdx#select-1) and [`SelectOrdinal`](../ref/macro.mdx#selectordinal-1) from `@lingui/macro`:
```diff
- import { Trans } from "@lingui/react"
@@ -190,7 +190,7 @@ Plugins are replaced with macros. Presets are removed completely because they ar
```
:::caution Note
- If you used [`Trans`](/ref/macro#trans) component without children, then keep the import from `@lingui/react`:
+ If you used [`Trans`](../ref/macro.mdx#trans) component without children, then keep the import from `@lingui/react`:
```jsx
import { Trans } from "@lingui/react";
@@ -215,9 +215,9 @@ Plugins are replaced with macros. Presets are removed completely because they ar
## New features
-### [`i18n.load`](/ref/core#i18n.load)
+### [`i18n.load`](../ref/core.md#i18n.load)
-[`i18n.load`](/ref/core#i18n.load) can now accept one catalog for specific locale. Useful for incremental loading of catalogs.
+[`i18n.load`](../ref/core.md#i18n.load) can now accept one catalog for specific locale. Useful for incremental loading of catalogs.
```jsx
import { i18n } from "@lingui/core";
@@ -235,7 +235,7 @@ i18n.load("cs", require("./locale/cs/messages"));
### `i18n.on('change', callback)`
-Event [`change`](/ref/core#change) is fired anytime new catalogs are loaded or when locale is activated.
+Event [`change`](../ref/core.md#change) is fired anytime new catalogs are loaded or when locale is activated.
### Native TypeScript support
diff --git a/website/docs/releases/migration-4.md b/website/docs/releases/migration-4.md
index 84549105f..7a6fc5a45 100644
--- a/website/docs/releases/migration-4.md
+++ b/website/docs/releases/migration-4.md
@@ -37,7 +37,7 @@ This includes components provided by Lingui, such as `Trans` or `Plural` and als
If the changes to the `I18nProvider` pose a problem to you, please open an issue and explain what the problem is. Additionally, you can keep using the [v3 implementation](https://github.com/lingui/js-lingui/blob/31dcab5a9a8f88bfa8b3a2c7cd12aa2d908a1d80/packages/react/src/I18nProvider.tsx#L58) by copying it into your code base and using that instead.
-No migration steps are necessary for components provided by Lingui, such as `Trans` or `Plural`. However, if you rendered translations in React components using the `t` macro, you need to be sure that the `useLingui` hook is called in the component, as seen [here](/ref/react#uselingui).
+No migration steps are necessary for components provided by Lingui, such as `Trans` or `Plural`. However, if you rendered translations in React components using the `t` macro, you need to be sure that the `useLingui` hook is called in the component, as seen [here](../ref/react.md#uselingui).
### Hash-based message ID generation and Context feature
@@ -87,7 +87,7 @@ Enabling this mode will swap the logic, and the formatter will treat all message
You can read more about the motivation behind this change in the [original RFC](https://github.com/lingui/js-lingui/issues/1360)
-Also, we've added a possibility to provide a context for the message. For more details, see the [Providing a context for a message](/guides/explicit-vs-generated-ids#context).
+Also, we've added a possibility to provide a context for the message. For more details, see the [Providing a context for a message](../guides/explicit-vs-generated-ids.md#context).
The context feature affects the message ID generation and adds the `msgctxt` parameter in case of the PO catalog format extraction.
@@ -117,7 +117,7 @@ You will need to make some changes as this is a misuse of the library that actua
Due to the changes caused by hash-based message ID feature described earlier, this approach will no longer work.
-Instead, please use [recommended](/guides/lazy-translations) pattern for such translations:
+Instead, please use [recommended](../guides/lazy-translations.md) pattern for such translations:
```tsx
import { msg } from "@lingui/macro";
@@ -202,7 +202,7 @@ declare type ExtractorType = {
}
```
-Read more about custom extractors on the [Advanced: Custom Extractor](/guides/custom-extractor) page.
+Read more about custom extractors on the [Advanced: Custom Extractor](../guides/custom-extractor.md) page.
### Configuration migrations for deprecated options were deleted
diff --git a/website/docs/releases/migration-5.md b/website/docs/releases/migration-5.md
index 05716c247..24ad3a2e3 100644
--- a/website/docs/releases/migration-5.md
+++ b/website/docs/releases/migration-5.md
@@ -2,7 +2,7 @@
This guide will help you migrate from Lingui 4.x to 5.x. It covers the most important changes and breaking changes.
-Need to upgrade an older project to v4 first? See our [older migration guide](/releases/migration-4).
+Need to upgrade an older project to v4 first? See our [older migration guide](./migration-4.md).
If you're looking for 4.x documentation, you can find it [here](https://js-lingui-m3z8jlqt6-crowdin.vercel.app/).
@@ -134,9 +134,9 @@ Simply add `babel-plugin-lingui-macro` to your Babel configuration to get starte
## Introducing `useLingui` Macro
-The new [`useLingui`](/ref/macro#uselingui) macro simplifies handling non-JSX messages within React components.
+The new [`useLingui`](../ref/macro.mdx#uselingui) macro simplifies handling non-JSX messages within React components.
-Previously, you had to combine the `t` or `msg` macro with the `i18n` instance returned by the [`useLingui`](/ref/react#uselingui) hook:
+Previously, you had to combine the `t` or `msg` macro with the `i18n` instance returned by the [`useLingui`](../ref/react.md#uselingui) hook:
```jsx
import { t, msg } from "@lingui/macro";
@@ -196,7 +196,7 @@ Now:
msgid "Hello {0} {value}"
```
-This feature is enabled by default and can be disabled by setting `printPlaceholdersInComments` to `false` in the [configuration](/ref/catalog-formats#po).
+This feature is enabled by default and can be disabled by setting `printPlaceholdersInComments` to `false` in the [configuration](../ref/catalog-formats.md#po).
## Compiled Messages Structure Changes
@@ -229,7 +229,7 @@ The structure of compiled messages has been changed to make them more predictabl
### Migration
-You'll need to re-[`compile`](/ref/cli#compile) your messages in the new format.
+You'll need to re-[`compile`](../ref/cli.md#compile) your messages in the new format.
:::info
Read more about the motivation and discuss the changes in the related [issue](https://github.com/lingui/js-lingui/issues/2043).
diff --git a/website/docs/tools/crowdin.md b/website/docs/tools/crowdin.md
index fdfe9aa6e..22ced8376 100644
--- a/website/docs/tools/crowdin.md
+++ b/website/docs/tools/crowdin.md
@@ -53,7 +53,7 @@ Decide who will translate your content:
Source strings are pulled automatically and are always up to date for your translators. Translated content is automatically pushed to your repository as a request.
-
+
## CLI
diff --git a/website/docs/tools/introduction.md b/website/docs/tools/introduction.md
index 411dd4270..a697bc29e 100644
--- a/website/docs/tools/introduction.md
+++ b/website/docs/tools/introduction.md
@@ -14,7 +14,7 @@ It becomes increasingly difficult and time-consuming to manage the back-and-fort
### Regular Workflow
-
+
This is the most basic workflow which involves sending the `.po` files to your translators (usually by email) and syncing them back manually into your application.
@@ -22,7 +22,7 @@ This workflow is manageable when your application is still quite small, doesn't
### Sync & Collaboration Tool Workflow
-
+
When the amount of text to translate increases, and the number of target languages grows, it becomes more efficient to use a sync and collaboration tool to assist you with the management of your team of translators, and co-evolution between your code and the translated files.
@@ -39,5 +39,5 @@ Instead of manually sending and receiving many emails and fixing the inconsisten
## Available Tools
-- [Crowdin](/tools/crowdin)
-- [Translation.io](/tools/translation-io)
+- [Crowdin](./crowdin.md)
+- [Translation.io](./translation-io.md)
diff --git a/website/docs/tools/translation-io.md b/website/docs/tools/translation-io.md
index 838a7be04..26d2a1322 100644
--- a/website/docs/tools/translation-io.md
+++ b/website/docs/tools/translation-io.md
@@ -48,7 +48,7 @@ Sometimes you have no choice but to confront your translators with HTML or inter
### Smart Plural Management
-Lingui allows to write plurals using the [ICU MessageFormat](/guides/message-format) syntax that looks like this:
+Lingui allows to write plurals using the [ICU MessageFormat](../guides/message-format.md) syntax that looks like this:
```icu-message-format
{count, plural, =0 {No messages}
@@ -62,7 +62,7 @@ That's why plural syntaxes are deconstructed to make translation easier, and the
If the target language has more plural forms than the source language, examples are also provided to the translator, as it may be unclear which plural form the `few` or `other` keyword refers to in that specific target language (for instance, Czech has three plural forms).
-
+
### Efficient Search
@@ -108,7 +108,7 @@ The configuration file looks like this:
}
```
-The synchronization will then be part of the [`extract`](/ref/cli#extract) command.
+The synchronization will then be part of the [`extract`](../ref/cli.md#extract) command.
### Add the following scripts
diff --git a/website/docs/tutorials/javascript.md b/website/docs/tutorials/javascript.md
index 81b9c01c0..936e20069 100644
--- a/website/docs/tutorials/javascript.md
+++ b/website/docs/tutorials/javascript.md
@@ -13,8 +13,8 @@ If you're looking for a working solution, check out the [Vanilla JS example](htt
## Installing Lingui
-1. Follow the [Installation and Setup](/installation) page for initial setup.
-2. Install the [`@lingui/core`](/ref/core) package, which is responsible for translation and message catalog handling.
+1. Follow the [Installation and Setup](../installation.mdx) page for initial setup.
+2. Install the [`@lingui/core`](../ref/core.md) package, which is responsible for translation and message catalog handling.
## Setting up i18n
@@ -31,14 +31,14 @@ i18n.activate("en");
The `messages.js` is generated by the Lingui CLI and contains compiled message catalogs.
:::tip
-Alternatively, you can load catalogs dynamically using the [`@lingui/loader`](/ref/loader) or [`@lingui/vite-plugin`](/ref/vite-plugin) without the need to import compiled messages manually.
+Alternatively, you can load catalogs dynamically using the [`@lingui/loader`](../ref/loader.md) or [`@lingui/vite-plugin`](../ref/vite-plugin.md) without the need to import compiled messages manually.
:::
## Localizing Your App
-To localize your application, you need to wrap your localizable texts in [Macros](/ref/macro). Lingui provides a set of Core Macros that transform tagged template literals and can be used in any JavaScript context.
+To localize your application, you need to wrap your localizable texts in [Macros](../ref/macro.mdx). Lingui provides a set of Core Macros that transform tagged template literals and can be used in any JavaScript context.
-Let's wrap our text in the [`t`](/ref/macro#t) macro:
+Let's wrap our text in the [`t`](../ref/macro.mdx#t) macro:
```js
import { t } from "@lingui/core/macro";
@@ -51,7 +51,7 @@ t`My name is ${name}`;
// becomes "Je m'appelle Fred"
```
-Plurals and selections are possible using [`plural`](/ref/macro#plural) and [`select`](/ref/macro#select) macros:
+Plurals and selections are possible using [`plural`](../ref/macro.mdx#plural) and [`select`](../ref/macro.mdx#select) macros:
```js
import { plural } from "@lingui/core/macro";
@@ -85,12 +85,12 @@ select(gender, {
```
:::caution
-All Core Macros cannot be used at the module level. They must be used within a component or function. See the [Macros](/ref/macro#using-macros) documentation for more information.
+All Core Macros cannot be used at the module level. They must be used within a component or function. See the [Macros](../ref/macro.mdx#using-macros) documentation for more information.
:::
## See Also
-- [Message Extraction Guide](/guides/message-extraction)
-- [Pluralization Guide](/guides/plurals)
-- [Dynamic Loading of Message Catalogs](/guides/dynamic-loading-catalogs)
-- [Lingui CLI Reference](/ref/cli)
+- [Message Extraction Guide](../guides/message-extraction.md)
+- [Pluralization Guide](../guides/plurals.md)
+- [Dynamic Loading of Message Catalogs](../guides/dynamic-loading-catalogs.md)
+- [Lingui CLI Reference](../ref/cli.md)
diff --git a/website/docs/tutorials/react-native.md b/website/docs/tutorials/react-native.md
index 86b4a2117..eebbf4bbc 100644
--- a/website/docs/tutorials/react-native.md
+++ b/website/docs/tutorials/react-native.md
@@ -7,7 +7,7 @@ description: Learn how to add internationalization to a React Native application
In this tutorial, we'll learn how to add internationalization to an existing application in React Native.
-The React Native tutorial is similar to the one for [React](/tutorials/react) and we highly recommend you read that one first because it goes into greater detail on many topics. Here, we will only cover parts that are relevant for React Native.
+The React Native tutorial is similar to the one for [React](./react.md) and we highly recommend you read that one first because it goes into greater detail on many topics. Here, we will only cover parts that are relevant for React Native.
:::tip Example
If you're looking for a working solution, check out the [React Native example](https://github.com/lingui/js-lingui/tree/main/examples/react-native). This example application shows a complete setup using Lingui and React Native.
@@ -20,9 +20,9 @@ If some `Intl` feature is not supported by your runtime, you can [polyfill it](#
## Installing Lingui
-1. Follow the [Installation and Setup](/installation?transpiler=babel) page for initial setup (for Babel).
-2. Install the [`@lingui/core`](/ref/core) and [`@lingui/react`](/ref/react) packages.
-3. _(optional)_ Install and configure the [`@lingui/metro-transformer`](/ref/metro-transformer) package that enables Metro to compile `.po` files on the fly.
+1. Follow the [Installation and Setup](../installation.mdx) page for initial setup (for Babel).
+2. Install the [`@lingui/core`](../ref/core.md) and [`@lingui/react`](../ref/react.md) packages.
+3. _(optional)_ Install and configure the [`@lingui/metro-transformer`](../ref/metro-transformer.mdx) package that enables Metro to compile `.po` files on the fly.
:::caution Warning
With the dependencies installed and set up, before running your app, please clear your Metro bundler cache with `npx expo start -c` or `npx react-native start --reset-cache` (if you do not use Expo).
@@ -94,12 +94,12 @@ As you can see, it's a simple mailbox application with only one screen.
## Internationalization in React (Native)
:::tip TL;DR
-There are several ways to render translations: You may use the [`Trans`](/ref/react#trans) component or the [`useLingui`](/ref/react#uselingui) hook together with the [`t`](/ref/macro#t) or [`msg`](/ref/macro#definemessage) macros. When you change the active locale or load new messages, all components that consume the Lingui context provided by [`I18nProvider`](/ref/react#i18nprovider) will re-render, making sure the UI shows the correct translations.
+There are several ways to render translations: You may use the [`Trans`](../ref/react.md#trans) component or the [`useLingui`](../ref/react.md#uselingui) hook together with the [`t`](../ref/macro.mdx#t) or [`msg`](../ref/macro.mdx#definemessage) macros. When you change the active locale or load new messages, all components that consume the Lingui context provided by [`I18nProvider`](../ref/react.md#i18nprovider) will re-render, making sure the UI shows the correct translations.
:::
-Not surprisingly, this part isn't too different from the [React tutorial](/tutorials/react).
+Not surprisingly, this part isn't too different from the [React tutorial](./react.md).
-First, we need to wrap our app with [`I18nProvider`](/ref/react#i18nprovider) and then we can use the [`Trans`](/ref/macro#trans) macro to translate the screen heading:
+First, we need to wrap our app with [`I18nProvider`](../ref/react.md#i18nprovider) and then we can use the [`Trans`](../ref/macro.mdx#trans) macro to translate the screen heading:
```tsx
import { I18nProvider, TransRenderProps } from "@lingui/react";
@@ -122,10 +122,10 @@ const DefaultComponent = (props: TransRenderProps) => {
```
:::tip Hint
-We're importing the default `i18n` object from `@lingui/core`. Read more about the `i18n` object in the [reference](/ref/core).
+We're importing the default `i18n` object from `@lingui/core`. Read more about the `i18n` object in the [reference](../ref/core.md).
:::
-Translating the heading is done. Now, let's translate the `title` prop in the `` element. In this case, `Button` expects to receive a `string`, so we cannot use the [`Trans`](/ref/macro#trans) macro here!
+Translating the heading is done. Now, let's translate the `title` prop in the `` element. In this case, `Button` expects to receive a `string`, so we cannot use the [`Trans`](../ref/macro.mdx#trans) macro here!
The solution is to use the `t` macro which we can obtain from the `useLingui` hook. We use it like this to get a translated string:
@@ -137,7 +137,7 @@ const { t } = useLingui();
```
-Under the hood, [`I18nProvider`](/ref/react#i18nprovider) takes the instance of the `i18n` object and passes it to `Trans` components through React context. `I18nProvider` will update the context value (which then rerenders components that consume the provided context value) when locale or message catalogs are updated.
+Under the hood, [`I18nProvider`](../ref/react.md#i18nprovider) takes the instance of the `i18n` object and passes it to `Trans` components through React context. `I18nProvider` will update the context value (which then rerenders components that consume the provided context value) when locale or message catalogs are updated.
The `Trans` component uses the `i18n` instance to get the translations from it. If we cannot use `Trans`, we can use the `useLingui` hook to get hold of the `i18n` instance ourselves and get the translations from there.
@@ -167,7 +167,7 @@ const Inbox = ({ markAsRead }) => {
## Internationalization Outside of React
-Until now, we have covered the [`Trans`](/ref/react#trans) macro and the [`useLingui`](/ref/react#uselingui) hook. Using them will make sure our components are always in sync with the currently active locale and message catalog.
+Until now, we have covered the [`Trans`](../ref/react.md#trans) macro and the [`useLingui`](../ref/react.md#uselingui) hook. Using them will make sure our components are always in sync with the currently active locale and message catalog.
However, you may want to show localized strings outside of React, for example when you want to show an Alert from some business logic code.
@@ -178,7 +178,7 @@ By default, Lingui uses an `i18n` object instance that you can import as follows
import { i18n } from "@lingui/core";
```
-This instance is the source of truth for the active locale. For string constants that will be translated at runtime, use the [`msg`](/ref/macro#definemessage) macro as follows:
+This instance is the source of truth for the active locale. For string constants that will be translated at runtime, use the [`msg`](../ref/macro.mdx#definemessage) macro as follows:
```ts
const deleteTitle = msg`Are you sure to delete this?`
@@ -190,7 +190,7 @@ const showDeleteConfirmation = () => {
## Changing the Active Locale
-The last remaining piece of the puzzle is changing the active locale. The `i18n` object exposes [`i18n.loadAndActivate()`](/ref/core#i18n.loadAndActivate) for that. Call the method and the [`I18nProvider`](/ref/react#i18nprovider) will re-render the translations. It all becomes clear when you take a look at the [final code](https://github.com/lingui/js-lingui/tree/main/examples/react-native/src/MainScreen.tsx#L29).
+The last remaining piece of the puzzle is changing the active locale. The `i18n` object exposes [`i18n.loadAndActivate()`](../ref/core.md) for that. Call the method and the [`I18nProvider`](../ref/react.md#i18nprovider) will re-render the translations. It all becomes clear when you take a look at the [final code](https://github.com/lingui/js-lingui/tree/main/examples/react-native/src/MainScreen.tsx#L29).
However, we don't recommend that you change the locale like this in mobile apps, as it can cause conflicts in how your app ui is localized. This is further [explained here](https://www.youtube.com/live/uLicTDG5hSs?feature=share&t=9088).
@@ -202,7 +202,7 @@ Instead, please refer to [Expo localization](https://docs.expo.dev/versions/late
## Rendering and Styling of Translations
-As described in the [reference](/ref/react#rendering-translations), by default, translation components render translation as text without a wrapping tag. In React Native though, all text must be wrapped in the `Text` component. This means we would need to use the [`Trans`](/ref/macro#trans) component like this:
+As described in the [reference](../ref/react.md#rendering-translations), by default, translation components render translation as text without a wrapping tag. In React Native though, all text must be wrapped in the `Text` component. This means we would need to use the [`Trans`](../ref/macro.mdx#trans) component like this:
```tsx
@@ -210,19 +210,19 @@ As described in the [reference](/ref/react#rendering-translations), by default,
```
-You'll surely agree the `Text` component looks a little redundant. That's why the [`I18nProvider`](/ref/react#i18nprovider) component accepts a `defaultComponent` prop. Just supply the `Text` component as the `defaultComponent` prop and the previous example can be simplified to:
+You'll surely agree the `Text` component looks a little redundant. That's why the [`I18nProvider`](../ref/react.md#i18nprovider) component accepts a `defaultComponent` prop. Just supply the `Text` component as the `defaultComponent` prop and the previous example can be simplified to:
```tsx
Message Inbox
```
-Alternatively, you may override the default locally on the i18n components, using the `render` or `component` props, as documented in the [reference](/ref/react#rendering-translations). Use them to apply styling to the rendered string.
+Alternatively, you may override the default locally on the i18n components, using the `render` or `component` props, as documented in the [reference](../ref/react.md#rendering-translations). Use them to apply styling to the rendered string.
## Nesting Components
-The [`Trans`](/ref/macro#trans) macro and `Text` component may be nested, for example to achieve the effect shown in the picture. This is thanks to how React Native [handles nested text](https://facebook.github.io/react-native/docs/text#nested-text).
+The [`Trans`](../ref/macro.mdx#trans) macro and `Text` component may be nested, for example to achieve the effect shown in the picture. This is thanks to how React Native [handles nested text](https://facebook.github.io/react-native/docs/text#nested-text).
-
+
This can be achieved by the following code:
@@ -244,10 +244,10 @@ The important point here is that the sentence isn't broken into pieces but remai
## See Also
-- [React i18n Tutorial](/tutorials/react)
-- [Message Extraction Guide](/guides/message-extraction)
-- [`@lingui/react` Reference](/ref/react)
-- [Lingui CLI Reference](/ref/cli)
+- [React i18n Tutorial](./react.md)
+- [Message Extraction Guide](../guides/message-extraction.md)
+- [`@lingui/react` Reference](../ref/react.md)
+- [Lingui CLI Reference](../ref/cli.md)
- [Localizing React Native apps talk from React Native EU 2022](https://www.youtube.com/live/uLicTDG5hSs?feature=share&t=7512)
---
diff --git a/website/docs/tutorials/react-rsc.md b/website/docs/tutorials/react-rsc.md
index d32976006..521ffdd33 100644
--- a/website/docs/tutorials/react-rsc.md
+++ b/website/docs/tutorials/react-rsc.md
@@ -11,7 +11,7 @@ There's a working example available [here](https://github.com/lingui/js-lingui/t
The example uses both Pages Router and App Router, so you can see how to use Lingui with both in [this commit](https://github.com/lingui/js-lingui/pull/1944/commits/100fc74abb49cff677f4b1cac1dfd5da60262b67).
:::
-Before going further, please follow the [Installation and Setup](/installation?transpiler=swc) instructions (for SWC or Babel depending on which you use - most likely it's SWC). You may also need to configure your `tsconfig.json` according to [this visual guide](https://twitter.com/mattpocockuk/status/1724462050288587123). This is so that TypeScript understands the values exported from `@lingui/react` package.
+Before going further, please follow the [Installation and Setup](../installation.mdx) instructions (for SWC or Babel depending on which you use - most likely it's SWC). You may also need to configure your `tsconfig.json` according to [this visual guide](https://twitter.com/mattpocockuk/status/1724462050288587123). This is so that TypeScript understands the values exported from `@lingui/react` package.
### Adding i18n Support to Next.js
@@ -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](/ref/macro).
+Secondly, add the `swc-plugin` to the `next.config.js`, so that you can use [Lingui Macros](../ref/macro.mdx).
```js title="next.config.js"
/** @type {import('next').NextConfig} */
@@ -38,7 +38,7 @@ module.exports = {
With Lingui, the experience of localizing React is the same in client and server components: `Trans` and `useLingui` can be used identically in both worlds, even though internally there are two implementations.
:::tip Under the hood
-Translation strings, one way or another, are obtained from an [I18n](/ref/core) object instance. In client components, this instance is passed around using React context. Because context is not available in Server components, instead [`cache`](https://react.dev/reference/react/cache) is used to maintain an I18n instance for each request.
+Translation strings, one way or another, are obtained from an [I18n](../ref/core.md) object instance. In client components, this instance is passed around using React context. Because context is not available in Server components, instead [`cache`](https://react.dev/reference/react/cache) is used to maintain an I18n instance for each request.
:::
To make Lingui work in both server and client components, we need to take the `lang` prop which Next.js passes to our layouts and pages, and create a corresponding instance of the I18n object. We then make it available to the components in our app. This is a 2-step process:
@@ -149,7 +149,7 @@ This means you need to repeat the `setI18n` in every page and layout. Luckily, y
Most likely, your users will not need to change the language of the application because it will render in their preferred language (obtained from the `accept-language` header in the [middleware](https://github.com/lingui/js-lingui/blob/2f1c1c3ae9e079c1c0e1a2ff617b1d0775af3170/examples/nextjs-swc/src/middleware.ts#L30)), or with a fallback.
-To change language, redirect users to a page with the new locale in the url. We do not recommend [dynamic](/guides/dynamic-loading-catalogs.md) switching because server-rendered locale-dependent content would become stale.
+To change language, redirect users to a page with the new locale in the url. We do not recommend [dynamic](../guides/dynamic-loading-catalogs.md) switching because server-rendered locale-dependent content would become stale.
### Static Rendering Pitfall
@@ -177,9 +177,9 @@ export default function SomePage() {
}
```
-Read more about [Lazy Translation](/guides/lazy-translations) to see how to handle translation defined on the module level.
+Read more about [Lazy Translation](../guides/lazy-translations.md) to see how to handle translation defined on the module level.
## See Also
-- [React i18n Tutorial](/tutorials/react)
-- [`@lingui/react` Reference](/ref/react)
+- [React i18n Tutorial](./react.md)
+- [`@lingui/react` Reference](../ref/react.md)
diff --git a/website/docs/tutorials/react.md b/website/docs/tutorials/react.md
index 279a9d301..564c29ec6 100644
--- a/website/docs/tutorials/react.md
+++ b/website/docs/tutorials/react.md
@@ -15,8 +15,8 @@ It includes examples for _Create React App_, _React with Vite and Babel_, _React
## Installing Lingui
-1. Follow the [Installation and Setup](/installation) page for initial setup.
-2. Install the [`@lingui/core`](/ref/core) and [`@lingui/react`](/ref/react) packages.
+1. Follow the [Installation and Setup](../installation.mdx) page for initial setup.
+2. Install the [`@lingui/core`](../ref/core.md) and [`@lingui/react`](../ref/react.md) packages.
## Example Component
@@ -76,9 +76,9 @@ This application is a simple mailbox with a header, a paragraph with a link and
We will start translating the `Inbox` component right away, but we need to do one more step to set up our application.
-Components need to read information about current language and message catalogs from the [`i18n`](/ref/core#i18n) instance. Lingui uses the [`I18nProvider`](/ref/react#i18nprovider) to pass the `i18n` instance to your React components.
+Components need to read information about current language and message catalogs from the [`i18n`](../ref/core.md#i18n) instance. Lingui uses the [`I18nProvider`](../ref/react.md#i18nprovider) to pass the `i18n` instance to your React components.
-Let's add all required imports and wrap our app inside [`I18nProvider`](/ref/react#i18nprovider):
+Let's add all required imports and wrap our app inside [`I18nProvider`](../ref/react.md#i18nprovider):
```jsx title="src/index.js"
import React from "react";
@@ -107,7 +107,7 @@ root.render(
```
:::info
-You might be wondering: how are we going to change the active language? That's what the [`I18n.load`](/ref/core#i18n.load) and [`i18n.activate`](/ref/core#i18n.activate) calls are for! However, we cannot change the language unless we have the translated message catalog. And to get the catalog, we first need to extract all messages from the source code.
+You might be wondering: how are we going to change the active language? That's what the [`I18n.load`](../ref/core.md#i18n.load) and [`i18n.activate`](../ref/core.md#i18n.activate) calls are for! However, we cannot change the language unless we have the translated message catalog. And to get the catalog, we first need to extract all messages from the source code.
:::
## Introducing Internationalization
@@ -120,7 +120,7 @@ Let's start with the basics - static messages. These messages don't have any var
Message Inbox
```
-To make this heading translatable, simply wrap it in the [`Trans`](/ref/macro#trans) macro:
+To make this heading translatable, simply wrap it in the [`Trans`](../ref/macro.mdx#trans) macro:
```jsx
import { Trans } from "@lingui/react/macro";
@@ -134,9 +134,9 @@ Using JSX Macros is the easiest way to translate your React components. It handl
### Macros vs. Components
-If you're wondering what [Macros](/ref/macro) are and the difference between macros and runtime components, here's a quick explanation.
+If you're wondering what [Macros](../ref/macro.mdx) are and the difference between macros and runtime components, here's a quick explanation.
-In general, macros are executed at compile time and serve to transform the source code to make the message writing process easier. Under the hood, all JSX macros are transformed into the runtime component [`Trans`](/ref/react#trans) (imported from `@lingui/react`).
+In general, macros are executed at compile time and serve to transform the source code to make the message writing process easier. Under the hood, all JSX macros are transformed into the runtime component [`Trans`](../ref/react.md#trans) (imported from `@lingui/react`).
Below is a brief example demonstrating this transformation:
@@ -152,7 +152,7 @@ import { Trans } from "@lingui/react";
;
```
-As you can see, the [`Trans`](/ref/react#trans) runtime component gets `id` and `message` props with a message in [ICU MessageFormat](/guides/message-format) syntax. We could write it manually, but it's just easier and shorter to write JSX as we're used to and let macros generate the message for us.
+As you can see, the [`Trans`](../ref/react.md#trans) runtime component gets `id` and `message` props with a message in [ICU MessageFormat](../guides/message-format.md) syntax. We could write it manually, but it's just easier and shorter to write JSX as we're used to and let macros generate the message for us.
:::tip Bundle Size Impact
Another advantage of using macros is that all non-essential properties are excluded from the production build. This results in a significant reduction in the size footprint for internationalization:
@@ -173,10 +173,10 @@ Back to our project. It's nice to use JSX and let macros generate messages under
All messages from the source code must be extracted into external message catalogs. Message catalogs are interchange files between developers and translators. We're going to have one file per language.
:::info
-Refer to the [Message Extraction](/guides/message-extraction) guide for more information about various message extraction concepts and strategies.
+Refer to the [Message Extraction](../guides/message-extraction.md) guide for more information about various message extraction concepts and strategies.
:::
-Let's switch to the command line for a moment. Execute the [`extract`](/ref/cli#extract) CLI command. If everything is set up correctly, you should see the extracted message statistics in the output:
+Let's switch to the command line for a moment. Execute the [`extract`](../ref/cli.md#extract) CLI command. If everything is set up correctly, you should see the extracted message statistics in the output:
```bash
> lingui extract
@@ -211,7 +211,7 @@ msgstr ""
// highlight-end
```
-It contains the message we wrapped in the [`Trans`](/ref/macro#trans) macro. Let's add the Czech translation:
+It contains the message we wrapped in the [`Trans`](../ref/macro.mdx#trans) macro. Let's add the Czech translation:
```po title="src/locales/cs/messages.po" {3}
#: src/Inbox.js:12
@@ -219,7 +219,7 @@ msgid "Message Inbox"
msgstr "Příchozí zprávy"
```
-If we run the [`extract`](/ref/cli#extract) command again, we'll see that all the Czech messages have been translated:
+If we run the [`extract`](../ref/cli.md#extract) command again, we'll see that all the Czech messages have been translated:
```bash
> lingui extract
@@ -235,7 +235,7 @@ Catalog statistics:
That's great! So how do we load it into your application? Lingui introduces the concept of compiled message catalogs. Before we load messages into our application, we need to compile them.
-Use the [`compile`](/ref/cli#compile) command to do this:
+Use the [`compile`](../ref/cli.md#compile) command to do this:
```bash
> lingui compile
@@ -285,22 +285,22 @@ root.render(
When we run the app, we see the inbox header is translated into Czech.
:::tip
-Alternatively, you can load catalogs dynamically using the [`@lingui/loader`](/ref/loader) or [`@lingui/vite-plugin`](/ref/vite-plugin) without the need to import compiled messages manually.
+Alternatively, you can load catalogs dynamically using the [`@lingui/loader`](../ref/loader.md) or [`@lingui/vite-plugin`](../ref/vite-plugin.md) without the need to import compiled messages manually.
:::
### Summary of Basic Workflow
Let's go through the workflow again:
-1. Add an [`I18nProvider`](/ref/react#i18nprovider), this component provides the active language and catalog(s) to other components.
-2. Wrap messages in the [`Trans`](/ref/macro#trans) macro.
-3. Run [`extract`](/ref/cli#extract) command to generate message catalogs.
+1. Add an [`I18nProvider`](../ref/react.md#i18nprovider), this component provides the active language and catalog(s) to other components.
+2. Wrap messages in the [`Trans`](../ref/macro.mdx#trans) macro.
+3. Run [`extract`](../ref/cli.md#extract) command to generate message catalogs.
4. Translate message catalogs (send them to translators usually).
-5. Run [`compile`](/ref/cli#compile) to create runtime catalogs.
+5. Run [`compile`](../ref/cli.md#compile) to create runtime catalogs.
6. Load runtime catalog.
7. Profit! 🎉
-It's not necessary to extract/translate messages one by one. This is usually done in batches. When you finish your work or PR, run [`extract`](/ref/cli#extract) to generate the latest message catalogs, and before building the application for production, run [`compile`](/ref/cli#compile).
+It's not necessary to extract/translate messages one by one. This is usually done in batches. When you finish your work or PR, run [`extract`](../ref/cli.md#extract) to generate the latest message catalogs, and before building the application for production, run [`compile`](../ref/cli.md#compile).
## Formatting
@@ -314,7 +314,7 @@ Let's move on to another paragraph in our project. The following paragraph has s
```
-Although it looks complex, there's really nothing special here. Just wrap the content of the paragraph in [`Trans`](/ref/macro#trans) and let the macro do the magic:
+Although it looks complex, there's really nothing special here. Just wrap the content of the paragraph in [`Trans`](../ref/macro.mdx#trans) and let the macro do the magic:
```jsx
@@ -326,7 +326,7 @@ Although it looks complex, there's really nothing special here. Just wrap the co
```
-Let's see how this message actually looks in the message catalog. Run the [`extract`](/ref/cli#extract) command and take a look at the message:
+Let's see how this message actually looks in the message catalog. Run the [`extract`](../ref/cli.md#extract) command and take a look at the message:
```gettext
#: src/Inbox.js:20
@@ -338,7 +338,7 @@ You may notice that components and html tags are replaced with indexed tags (`<0
### JSX to MessageFormat Transformations
-At first glance, these transformations might seem somewhat unconventional; however, they are straightforward, intuitive, and align well with React principles. There is no need to focus on MessageFormat, as the library handles its creation for us. We can write our components as we typically would and simply wrap the text in the [`Trans`](/ref/macro#trans) macro.
+At first glance, these transformations might seem somewhat unconventional; however, they are straightforward, intuitive, and align well with React principles. There is no need to focus on MessageFormat, as the library handles its creation for us. We can write our components as we typically would and simply wrap the text in the [`Trans`](../ref/macro.mdx#trans) macro.
Let's see some examples with MessageFormat equivalents:
@@ -382,7 +382,7 @@ Any expressions are allowed, not just simple variables. The only difference is,
```
:::caution
-Try to keep your messages simple and avoid complex expressions. During extraction, these expressions will be replaced by placeholders, resulting in a lack of context for translators. There is also a special rule in Lingui [ESLint Plugin](/ref/eslint-plugin) to catch these cases: [`no-expression-in-message`](https://github.com/lingui/eslint-plugin/blob/main/docs/rules/no-expression-in-message.md).
+Try to keep your messages simple and avoid complex expressions. During extraction, these expressions will be replaced by placeholders, resulting in a lack of context for translators. There is also a special rule in Lingui [ESLint Plugin](../ref/eslint-plugin.md) to catch these cases: [`no-expression-in-message`](https://github.com/lingui/eslint-plugin/blob/main/docs/rules/no-expression-in-message.md).
:::
### Dates and Numbers
@@ -393,9 +393,9 @@ Take a look at the message in the footer of our component. It is a bit special b
```
-Dates (as well as numbers) are formatted differently in different languages, but we don't have to do this manually. The heavy lifting is done by the [`Intl` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl), we'll just use the [`i18n.date()`](/ref/core#i18n.date) function.
+Dates (as well as numbers) are formatted differently in different languages, but we don't have to do this manually. The heavy lifting is done by the [`Intl` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl), we'll just use the [`i18n.date()`](../ref/core.md#i18n.date) function.
-The `i18n` object can be accessed with the [`useLingui`](/ref/react#uselingui) hook:
+The `i18n` object can be accessed with the [`useLingui`](../ref/react.md#uselingui) hook:
```jsx title="src/Inbox.js" {4,9}
import { useLingui, Trans } from "@lingui/react/macro";
@@ -413,7 +413,7 @@ export default function Inbox() {
}
```
-This will format the date using the conventional format for the active language. To format numbers, use the [`i18n.number()`](/ref/core#i18n.number) function.
+This will format the date using the conventional format for the active language. To format numbers, use the [`i18n.number()`](../ref/core.md#i18n.number) function.
### Message ID
@@ -443,7 +443,7 @@ There are two approaches for creating a message ID:
- Explicit message ID set by the developer (e.g. `days.monday`).
:::info
-Refer to the [Explicit vs Generated IDs](/guides/explicit-vs-generated-ids) guide for more information about the pros and cons of each approach.
+Refer to the [Explicit vs Generated IDs](../guides/explicit-vs-generated-ids.md) guide for more information about the pros and cons of each approach.
:::
## Plurals
@@ -458,7 +458,7 @@ Let's take a closer look at the following code in our component:
```
-This message is a bit special, because it depends on the value of the `messagesCount` variable. Most languages use different forms of words when describing quantities - this is called [pluralization](/guides/plurals).
+This message is a bit special, because it depends on the value of the `messagesCount` variable. Most languages use different forms of words when describing quantities - this is called [pluralization](../guides/plurals.md).
What's tricky is that different languages use different number of plural forms. For example, English has only two forms - singular and plural - as we can see in the example above. However, Czech language has three plural forms. Some languages have up to 6 plural forms and some don't have plurals at all!
@@ -478,7 +478,7 @@ How do we know which plural form we should use? It's very simple: we, as develop
> Plural form
-We don't need to select these forms manually. We'll use [`Plural`](/ref/macro#plural-1) component, which takes a `value` prop and based on the active language, selects the right plural form:
+We don't need to select these forms manually. We'll use [`Plural`](../ref/macro.mdx#plural-1) component, which takes a `value` prop and based on the active language, selects the right plural form:
```jsx
import { Plural } from "@lingui/react/macro";
@@ -490,7 +490,7 @@ import { Plural } from "@lingui/react/macro";
This component will render `There's 1 message in your inbox` when `messageCount = 1` and `There are # messages in your inbox` for any other values of `messageCount`. `#` is a placeholder, which is replaced with `value`.
-Let's run the [`extract`](/ref/cli#extract) command to see the extracted message:
+Let's run the [`extract`](../ref/cli.md#extract) command to see the extracted message:
```icu-message-format
{messagesCount, plural,
@@ -575,7 +575,7 @@ Let's go back to our original pluralized message:
```
-To include variables or components within messages, simply wrap them in the [`Trans`](/ref/macro#trans) macro or use template literals (for example, with a variable `name`):
+To include variables or components within messages, simply wrap them in the [`Trans`](../ref/macro.mdx#trans) macro or use template literals (for example, with a variable `name`):
```jsx
@@ -605,7 +605,7 @@ const markAsRead = () => {
};
```
-To translate it, we will use the [`useLingui`](/ref/macro#uselingui) macro hook:
+To translate it, we will use the [`useLingui`](../ref/macro.mdx#uselingui) macro hook:
```js
import { useLingui } from "@lingui/react/macro";
@@ -646,7 +646,7 @@ export default function ImageWithCaption() {
:::
:::caution
-All Core Macros cannot be used at the module level. They must be used within a component or function. See the [Macros](/ref/macro#using-macros) documentation for more information.
+All Core Macros cannot be used at the module level. They must be used within a component or function. See the [Macros](../ref/macro.mdx#using-macros) documentation for more information.
:::
## Review
@@ -700,6 +700,6 @@ That's it for this tutorial! For more details, see the reference documentation o
## See Also
-- [React Server Components Tutorial](/tutorials/react-rsc)
-- [React Native i18n Tutorial](/tutorials/react-native)
-- [`@lingui/react` Reference](/ref/react)
+- [React Server Components Tutorial](./react-rsc.md)
+- [React Native i18n Tutorial](./react-native.md)
+- [`@lingui/react` Reference](../ref/react.md)
diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts
index c70c5b09c..87412e1d5 100644
--- a/website/docusaurus.config.ts
+++ b/website/docusaurus.config.ts
@@ -54,7 +54,8 @@ const config: Config = {
},
items: [
{
- to: "/introduction",
+ type: "docSidebar",
+ sidebarId: "sidebar",
label: "Docs",
position: "left",
},
@@ -73,6 +74,11 @@ const config: Config = {
label: "Community",
position: "left",
},
+ {
+ type: "docsVersionDropdown",
+ position: "right",
+ dropdownActiveClassDisabled: true,
+ },
{
href: "https://github.com/lingui/js-lingui",
position: "right",
@@ -175,6 +181,19 @@ const config: Config = {
remarkPlugins: [
[require("@docusaurus/remark-plugin-npm2yarn"), { sync: true, converters: ["yarn", "pnpm"] }],
],
+ lastVersion: "current",
+ includeCurrentVersion: true,
+ versions: {
+ current: {
+ label: "Version 5.x",
+ banner: "none",
+ },
+ "4.x": {
+ label: "Version 4.x",
+ banner: "unmaintained",
+ },
+ },
+ onlyIncludeVersions: ["current", "4.x"],
},
blog: {
showReadingTime: true,
diff --git a/website/package.json b/website/package.json
index 21b4654eb..2ec1fbc95 100644
--- a/website/package.json
+++ b/website/package.json
@@ -1,6 +1,6 @@
{
"name": "js-lingui-website",
- "version": "0.0.0",
+ "version": "0.1.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -15,36 +15,34 @@
"fixFormat": "prettier --write ."
},
"dependencies": {
- "@docusaurus/core": "3.6.3",
- "@docusaurus/preset-classic": "3.6.3",
- "@docusaurus/remark-plugin-npm2yarn": "3.6.3",
- "@docusaurus/theme-mermaid": "3.6.3",
+ "@docusaurus/core": "^3.8.1",
+ "@docusaurus/preset-classic": "^3.8.1",
+ "@docusaurus/remark-plugin-npm2yarn": "^3.8.1",
+ "@docusaurus/theme-mermaid": "^3.8.1",
"@mdx-js/react": "3.1.0",
"clsx": "2.1.1",
"docusaurus-plugin-sass": "^0.2.6",
+ "prism-react-renderer": "^2.4.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"sass": "^1.83.0"
},
- "browserslist": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
"devDependencies": {
- "@docusaurus/eslint-plugin": "3.6.3",
- "@docusaurus/module-type-aliases": "3.6.3",
- "@docusaurus/utils": "3.6.3",
- "@eslint/js": "^9.17.0",
- "@tsconfig/docusaurus": "2.0.3",
- "@types/react": "19.0.2",
- "@types/react-helmet": "6.1.11",
- "@types/react-router-dom": "5.3.3",
- "@typescript-eslint/eslint-plugin": "8.18.2",
- "@typescript-eslint/parser": "8.18.2",
- "eslint": "9.17.0",
- "eslint-plugin-react": "7.37.3",
- "eslint-plugin-react-hooks": "5.1.0",
+ "@docusaurus/eslint-plugin": "^3.8.1",
+ "@docusaurus/module-type-aliases": "^3.8.1",
+ "@docusaurus/plugin-content-docs": "^3.8.1",
+ "@docusaurus/tsconfig": "^3.8.1",
+ "@docusaurus/types": "^3.8.1",
+ "@docusaurus/utils": "^3.8.1",
+ "@eslint/js": "^9.29.0",
+ "@types/react": "^19.0.2",
+ "@types/react-helmet": "^6.1.11",
+ "@types/react-router-dom": "^5.3.3",
+ "@typescript-eslint/eslint-plugin": "^8.34.1",
+ "@typescript-eslint/parser": "^8.34.1",
+ "eslint": "^9.29.0",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^5.2.0",
"globals": "^15.14.0",
"prettier": "3.4.2",
"remark-cli": "12.0.1",
@@ -66,11 +64,20 @@
"retext-sentence-spacing": "6.0.0",
"retext-syntax-mentions": "4.0.0",
"retext-syntax-urls": "4.0.0",
- "typescript": "5.7.2",
+ "typescript": "^5.8.3",
"typescript-plugin-css-modules": "^5.1.0",
"unified": "11.0.5"
},
- "resolutions": {
- "mermaid": "^10.0.0"
+ "browserslist": {
+ "production": [
+ ">0.5%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 3 chrome version",
+ "last 3 firefox version",
+ "last 5 safari version"
+ ]
}
}
diff --git a/website/docs/community.md b/website/src/pages/community.md
similarity index 100%
rename from website/docs/community.md
rename to website/src/pages/community.md
diff --git a/website/docs/examples.md b/website/src/pages/examples.md
similarity index 100%
rename from website/docs/examples.md
rename to website/src/pages/examples.md
diff --git a/website/tsconfig.json b/website/tsconfig.json
index a66ebc461..d763b55a7 100644
--- a/website/tsconfig.json
+++ b/website/tsconfig.json
@@ -1,7 +1,9 @@
{
- "extends": "@tsconfig/docusaurus/tsconfig.json",
+ "extends": "@docusaurus/tsconfig",
"include": ["src/"],
+ "exclude": [".docusaurus", "build"],
"compilerOptions": {
+ "baseUrl": ".",
"jsx": "react",
"plugins": [{ "name": "typescript-plugin-css-modules" }],
"moduleResolution": "node"
diff --git a/website/versioned_docs/version-4.x/assets/Crowdin__js-lingui-cli.png b/website/versioned_docs/version-4.x/assets/Crowdin__js-lingui-cli.png
new file mode 100644
index 000000000..938f63910
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/Crowdin__js-lingui-cli.png differ
diff --git a/website/versioned_docs/version-4.x/assets/Crowdin__js-lingui-vcs.png b/website/versioned_docs/version-4.x/assets/Crowdin__js-lingui-vcs.png
new file mode 100644
index 000000000..5e548d31b
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/Crowdin__js-lingui-vcs.png differ
diff --git a/website/versioned_docs/version-4.x/assets/dynamic-loading-catalogs-1.png b/website/versioned_docs/version-4.x/assets/dynamic-loading-catalogs-1.png
new file mode 100644
index 000000000..309100c30
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/dynamic-loading-catalogs-1.png differ
diff --git a/website/versioned_docs/version-4.x/assets/dynamic-loading-catalogs-2.png b/website/versioned_docs/version-4.x/assets/dynamic-loading-catalogs-2.png
new file mode 100644
index 000000000..c02227ddd
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/dynamic-loading-catalogs-2.png differ
diff --git a/website/versioned_docs/version-4.x/assets/extractor-deps-scheme-dark.jpg b/website/versioned_docs/version-4.x/assets/extractor-deps-scheme-dark.jpg
new file mode 100644
index 000000000..05381ff6f
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/extractor-deps-scheme-dark.jpg differ
diff --git a/website/versioned_docs/version-4.x/assets/extractor-deps-scheme.jpg b/website/versioned_docs/version-4.x/assets/extractor-deps-scheme.jpg
new file mode 100644
index 000000000..064359f78
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/extractor-deps-scheme.jpg differ
diff --git a/website/versioned_docs/version-4.x/assets/extractor-glob-scheme-dark.jpg b/website/versioned_docs/version-4.x/assets/extractor-glob-scheme-dark.jpg
new file mode 100644
index 000000000..7629d7efb
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/extractor-glob-scheme-dark.jpg differ
diff --git a/website/versioned_docs/version-4.x/assets/extractor-glob-scheme.jpg b/website/versioned_docs/version-4.x/assets/extractor-glob-scheme.jpg
new file mode 100644
index 000000000..2397c79f8
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/extractor-glob-scheme.jpg differ
diff --git a/website/versioned_docs/version-4.x/assets/rn-component-nesting.png b/website/versioned_docs/version-4.x/assets/rn-component-nesting.png
new file mode 100644
index 000000000..1dcb0bf7c
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/rn-component-nesting.png differ
diff --git a/website/versioned_docs/version-4.x/assets/translation-lingui-logo.png b/website/versioned_docs/version-4.x/assets/translation-lingui-logo.png
new file mode 100644
index 000000000..43951e694
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/translation-lingui-logo.png differ
diff --git a/website/versioned_docs/version-4.x/assets/translation-lingui-plural-forms.png b/website/versioned_docs/version-4.x/assets/translation-lingui-plural-forms.png
new file mode 100644
index 000000000..a2e7b4dae
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/translation-lingui-plural-forms.png differ
diff --git a/website/versioned_docs/version-4.x/assets/with-collaboration-tool.png b/website/versioned_docs/version-4.x/assets/with-collaboration-tool.png
new file mode 100644
index 000000000..419b8ddcf
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/with-collaboration-tool.png differ
diff --git a/website/versioned_docs/version-4.x/assets/without-collaboration-tool.png b/website/versioned_docs/version-4.x/assets/without-collaboration-tool.png
new file mode 100644
index 000000000..af3a9d4e4
Binary files /dev/null and b/website/versioned_docs/version-4.x/assets/without-collaboration-tool.png differ
diff --git a/website/versioned_docs/version-4.x/guides/custom-extractor.md b/website/versioned_docs/version-4.x/guides/custom-extractor.md
new file mode 100644
index 000000000..c52b4f599
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/custom-extractor.md
@@ -0,0 +1,42 @@
+---
+title: Creating a Custom Message Extractor
+description: Learn how to write a custom message extractor for your project
+---
+
+# Custom Extractor
+
+If your project is not working well with Lingui's Extractor, you can write your custom extractor implementation.
+
+That might be the case if you use some experimental features (stage0 - stage2) or frameworks with custom syntax such as Vue.js or Svelte.
+
+```ts title="./my-custom-extractor.ts"
+import { extractor as defaultExtractor } from "@lingui/cli/api";
+
+export const extractor: ExtractorType = {
+ match(filename: string) {
+ return filename.endsWith(".custom");
+ },
+ extract(filename: string, code: string, onMessageExtracted, ctx: ExtractorCtx) {
+ // transform to plain JS + Sourcemaps
+ const { code, sourcemaps } = transformMyCustomFileToJs(filename, code);
+
+ // you can acess lingui config from using `ctx.linguiConfig`
+ // reuse extractor from cli
+ return defaultExtractor.extract(filename, code, onMessageExtracted, { sourcemaps, ...ctx });
+ },
+};
+```
+
+Then in your `lingui.config.ts`:
+
+```ts title="lingui.config.ts"
+import { extractor } from './my-custom-extractor.ts'
+module.exports = {
+ [...]
+ extractors: [extractor]
+}
+```
+
+:::caution Important
+If you use TypeScript to create your extractor, you should use the `ts` extension for your Lingui configuration file.
+:::
diff --git a/website/versioned_docs/version-4.x/guides/custom-formatter.md b/website/versioned_docs/version-4.x/guides/custom-formatter.md
new file mode 100644
index 000000000..3dcced19a
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/custom-formatter.md
@@ -0,0 +1,74 @@
+---
+title: Creating a Custom Message Formatter
+description: Learn how to write a custom localization message formatter for your project
+---
+
+# Custom Formatter
+
+If your project requires some special format or handling logic, you can write your own format implementation.
+
+Formatter is a simple object with 2 main functions `parse` and `serialize`, which should take Lingui catalog and serialize it to string and vice versa.
+
+You don't need to create a separate package for formatter, you can write it directly in `lingui.config.{ts,js}`.
+
+```ts title="lingui.config.{ts,js}"
+import { extractor } from './my-custom-extractor.ts'
+module.exports = {
+ [...]
+ format: {
+ catalogExtension: "json",
+ parse: (content: string): CatalogType => JSON.parse(content),
+ serialize: (catalog: CatalogType): string => JSON.stringify(catalog),
+ }
+}
+```
+
+The shape of formatter is the following:
+
+```ts
+export type CatalogFormatter = {
+ catalogExtension: string;
+ /**
+ * Set extension used when extract to template
+ * Omit if the extension is the same as catalogExtension
+ */
+ templateExtension?: string;
+ parse(
+ content: string,
+ ctx: { locale: string | null; sourceLocale: string; filename: string }
+ ): Promise | CatalogType;
+ serialize(
+ catalog: CatalogType,
+ ctx: { locale: string | null; sourceLocale: string; filename: string; existing: string | null }
+ ): Promise | string;
+};
+```
+
+Lingui Catalog is an object with the following structure:
+
+```ts
+export type CatalogType = {
+ [msgId: string]: MessageType;
+};
+
+type CatalogExtra = Record;
+
+export type MessageType = {
+ message?: string;
+ origin?: MessageOrigin[];
+ comments?: string[];
+ obsolete?: boolean;
+ context?: string;
+ translation?: string;
+
+ /**
+ * the generic field where
+ * formatters can store additional data
+ */
+ extra?: Extra;
+};
+```
+
+:::caution Important
+If you are using TypeScript to build your formatter, you should use the `ts` extension for your Lingui configuration file.
+:::
diff --git a/website/versioned_docs/version-4.x/guides/dynamic-loading-catalogs.md b/website/versioned_docs/version-4.x/guides/dynamic-loading-catalogs.md
new file mode 100644
index 000000000..3096bc713
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/dynamic-loading-catalogs.md
@@ -0,0 +1,71 @@
+# Dynamic Loading of Message Catalogs
+
+[`I18nProvider`](../ref/react.md#i18nprovider) doesn't assume anything about your app and it's the developer responsibility to load messages based on active language.
+
+Here's an example of a basic setup with a dynamic load of catalogs.
+
+## Final i18n Loader Helper
+
+Here's the full source of `i18n.ts` logic:
+
+```tsx title="i18n.ts"
+import { i18n } from "@lingui/core";
+
+export const locales = {
+ en: "English",
+ cs: "Česky",
+};
+export const defaultLocale = "en";
+
+/**
+ * We do a dynamic import of just the catalog that we need
+ * @param locale any locale string
+ */
+export async function dynamicActivate(locale: string) {
+ const { messages } = await import(`./locales/${locale}/messages`);
+ i18n.load(locale, messages);
+ i18n.activate(locale);
+}
+```
+
+**How should I use the dynamicActivate in our application?**
+
+```jsx
+import React, { useEffect } from "react";
+import App from "./App";
+
+import { I18nProvider } from "@lingui/react";
+import { i18n } from "@lingui/core";
+import { defaultLocale, dynamicActivate } from "./i18n";
+
+const I18nApp = () => {
+ useEffect(() => {
+ // With this method we dynamically load the catalogs
+ dynamicActivate(defaultLocale);
+ }, []);
+
+ return (
+
+
+
+ );
+};
+```
+
+## Conclusion
+
+Looking at the content of build dir, we see one chunk per language:
+
+```bash
+i18n-0.c433b3bd.chunk.js
+i18n-1.f0cf2e3d.chunk.js
+main.ab4626ef.js
+```
+
+When page is loaded initially, only main bundle and bundle for the first language are loaded:
+
+
+
+After changing language in UI, the second language bundle is loaded:
+
+
diff --git a/website/versioned_docs/version-4.x/guides/explicit-vs-generated-ids.md b/website/versioned_docs/version-4.x/guides/explicit-vs-generated-ids.md
new file mode 100644
index 000000000..1d0b0aed8
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/explicit-vs-generated-ids.md
@@ -0,0 +1,224 @@
+---
+title: Choosing between generated and explicit IDs
+description: Learn about the differences between explicit and generated IDs and how to choose the right approach for your project
+---
+
+# Explicit vs Generated IDs
+
+When internationalizing your application, you may need to decide whether to use explicit or generated IDs for your messages.
+
+In this guide, we will explore the fundamental concepts of explicit and generated IDs, and then delve into a comprehensive comparison, highlighting the benefits and drawbacks of each approach.
+
+## What are Explicit IDs and Generated IDs?
+
+### Explicit ID
+
+An explicit ID, often referred to as a user-defined or custom ID, is a manually assigned identifier associated with a specific message. These IDs are typically chosen by developers and are explicitly specified within your code. The typical explicit id may look like `index.header.title` or `modal.buttons.cancel`.
+
+Lingui example:
+
+```jsx
+LinguiJS example
+
+// extracted as
+{
+ id: "index.header.title",
+ message: "LinguiJS example",
+}
+```
+
+### Generated IDs
+
+On the other hand, generated IDs are automatically created by the internalization library. In Lingui, such IDs are created based on the source message and [context](#context).
+
+Lingui example:
+
+```jsx
+LinguiJS example
+
+// extracted as
+{
+ id: "uxV9Xq",
+ message: "LinguiJS example",
+}
+```
+
+### Benefits of Generated IDs
+
+1. **Avoiding the "Naming Things" problem:** You don't need to come up with a name for each single phrase in the app. Lingui generates the IDs (in the form of short hashes) from the messages.
+2. **Better Developer Experience:** Developers can focus on coding without needing to manually assign IDs, leading to a more streamlined development process. Searching for a user-facing string will lead to the place in code where it's used, as opposed to taking you to a (typically JSON) file full of translations.
+3. **Avoiding Duplicates:** Duplicate messages are merged together automatically. Your translators will not have to translate the same phrases again and again. This could lead to cost savings, especially if translators charge by word count.
+4. **Smaller bundle:** Lingui generates short IDs such as `uxV9Xq` which are typically shorter than manually crafted IDs like `index.header.title`. This results in a smaller bundle size, optimizing your application's performance.
+5. **Preventing ID collisions:** As your application scales, explicit IDs can potentially lead to conflicts. Lingui's generated IDs ensure you steer clear of such collisions.
+
+### Benefits of Explicit IDs
+
+1. **Control:** Developers have full control over the naming and assignment of explicit IDs. This control allows for precise targeting and easy maintenance of internationalization keys.
+2. **Loose Coupling:** Explicit IDs are loosely coupled with the messages (as opposed to when they are generated from the messages). This means that if the message changes, the ID remains the same. When your team uses a TMS (Translation Management System), this makes it easier even for non-technical people to update the strings.
+3. **Readability:** Explicit IDs often have meaningful names, making it easier for developers, translators, and content creators to understand their purpose within the codebase.
+4. **Predictability:** Since explicit IDs are manually assigned, they remain stable across different versions of your application, reducing the likelihood of breaking changes during updates.
+
+In conclusion, the choice between these two strategies depends on your project requirements and priorities. However, it's important to note that Lingui provides the full range of benefits, especially with generated IDs.
+
+:::note
+You don't need to worry about the readability of IDs because you would barely see them. When extracted into a PO file, translators would see source string and its corresponding translation, while the IDs remain behind the scenes.
+
+```gettext
+#: src/App.tsx:1
+msgid "LinguiJS example"
+msgstr "LinguiJS przyklad"
+```
+
+:::
+
+## Using ID generated from a message
+
+### With [`Trans`](../ref/macro.mdx#trans) macro
+
+```jsx
+import { Trans } from "@lingui/macro";
+
+function render() {
+ return (
+ <>
+
+ >
+ );
+}
+```
+
+In the example code above, the content of [`Trans`](../ref/macro.mdx#trans) is transformed into a message in MessageFormat syntax. By default, this message is used for generating the ID. Considering the example above, the catalog would contain these entries:
+
+```js
+const catalog = [
+ {
+ id: "uxV9Xq",
+ message: "LinguiJS example",
+ },
+ {
+ id: "9/omjw",
+ message: "Hello <0>{name}0>.",
+ },
+];
+```
+
+### With non-JSX macro
+
+In the following example, the message `Hello World` will be extracted and used to create an ID.
+
+```jsx
+import { msg } from "@lingui/macro";
+
+msg`Hello World`;
+```
+
+### Context {#context}
+
+By default, when using generated IDs, the same text elements are extracted with the same ID, and then translated once. However, this might not always be desirable since the same text can have different meanings and translations. For example, consider the word "right" and its two possible meanings:
+
+- correct as in "you are right"
+- direction as in "turn right"
+
+To distinguish these two cases, you can add `context` to messages. The same text elements with different contexts are extracted with different IDs. Then, they can be translated differently and merged back into the application as different translation entries.
+
+Regardless of whether you use generated IDs or not, adding context makes the translation process less challenging and helps translators interpret the source accurately. You, in return, get translations of better quality faster and decrease the number of context-related issues you would need to solve.
+
+#### Providing context for a message
+
+```jsx
+import { Trans } from "@lingui/macro";
+right;
+right;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+;
+```
+
+or with non-jsx macro
+
+```jsx
+import { msg } from "@lingui/macro";
+
+const ex1 = msg({
+ message: `right`,
+ context: "direction",
+});
+
+const ex2 = msg({
+ message: `right`,
+ context: "correctness",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+const ex1 = {
+ id: "d1wX4r",
+ message: `right`,
+};
+const ex2 = {
+ id: "16eaSK",
+ message: `right`,
+};
+```
+
+## Using custom ID
+
+### With [`Trans`](../ref/macro.mdx#trans)
+
+If you're using custom IDs in your project, add `id` prop to i18n components:
+
+```jsx
+import { Trans } from "@lingui/macro";
+
+function render() {
+ return (
+ <>
+
+ >
+ );
+}
+```
+
+The messages with IDs `msg.header` and `msg.hello` will be extracted with their default values as `LinguiJS example` and `Hello <0>{name}0>.` respectively.
+
+### With non-JSX macro
+
+If you're using custom IDs in your project, call the [`msg`](../ref/macro.mdx#definemessage) function with a message descriptor object, passing the ID using the `id` property:
+
+```jsx
+import { msg } from "@lingui/macro";
+
+msg({ id: "msg.greeting", message: `Hello World` });
+```
+
+Message `msg.greeting` will be extracted with default value `Hello World`.
+
+For all other js macros ([`plural`](../ref/macro.mdx#plural), [`select`](../ref/macro.mdx#select), [`selectOrdinal`](../ref/macro.mdx#selectordinal), use them inside [`msg`](../ref/macro.mdx#definemessage) macro to pass ID (in this case, `'msg.caption'`).
+
+```jsx
+import { msg, plural } from "@lingui/macro";
+
+msg({
+ id: "msg.caption",
+ message: plural(count, {
+ one: "# image caption",
+ other: "# image captions",
+ }),
+});
+```
diff --git a/website/versioned_docs/version-4.x/guides/lazy-translations.md b/website/versioned_docs/version-4.x/guides/lazy-translations.md
new file mode 100644
index 000000000..4d27d944a
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/lazy-translations.md
@@ -0,0 +1,120 @@
+---
+title: Lazy Translations
+description: Lazy translations allow you to defer translation of a message until it is actually displayed
+---
+
+# Lazy Translations
+
+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`](/ref/macro#definemessage) macro to create a _message descriptor_ that can be saved, passed around as a variable, and rendered later.
+
+## Usage Example
+
+To render the message descriptor as a string-only translation, pass it to the [`i18n._()`](/ref/core#i18n._) method:
+
+```jsx
+import { msg } from "@lingui/core/macro";
+import { i18n } from "@lingui/core";
+
+const favoriteColors = [msg`Red`, msg`Orange`, msg`Yellow`, msg`Green`];
+
+export function getTranslatedColorNames() {
+ return favoriteColors.map((color) => i18n._(color));
+}
+```
+
+## Usage in React
+
+To render the message descriptor in a React component, pass its `id` to the [`Trans`](/ref/react#trans) component as a value of the `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 (
+
+ {favoriteColors.map((color) => (
+
+
+
+ ))}
+
+ );
+}
+```
+
+:::info Important
+Please note that we import the `` 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.
+:::
+
+### Picking a Message Based on a Variable
+
+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.
+
+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`](/ref/macro#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
{_(statusMessages[statusCode])}
;
+}
+```
+
+### Memoization Pitfall
+
+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.
+
+To avoid bugs with stale translations, use the `t` function returned from the [`useLingui`](/ref/macro#uselingui) macro: it is safe to use with memoization because its reference changes whenever the Lingui context updates.
+
+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.
+
+```jsx
+import { i18n } from "@lingui/core";
+import { msg } from "@lingui/core/macro";
+import { useLingui } from "@lingui/react/macro";
+
+const welcomeMessage = msg`Welcome!`;
+
+// ❌ Bad! This code won't work
+export function Welcome() {
+ const buggyWelcome = useMemo(() => {
+ return i18n._(welcomeMessage);
+ }, []);
+
+ return
{buggyWelcome}
;
+}
+
+// ❌ Bad! This code won't work either because the reference to i18n does not change
+export function Welcome() {
+ const { i18n } = useLingui();
+
+ const buggyWelcome = useMemo(() => {
+ return i18n._(welcomeMessage);
+ }, [i18n]);
+
+ return
{buggyWelcome}
;
+}
+
+// ✅ Good! `useMemo` consumes the `t` function from the `useLingui` macro
+export function Welcome() {
+ const { t } = useLingui();
+
+ const welcome = useMemo(() => {
+ return t(welcomeMessage);
+ }, [t]);
+
+ return
{welcome}
;
+}
+```
diff --git a/website/versioned_docs/version-4.x/guides/message-extraction.md b/website/versioned_docs/version-4.x/guides/message-extraction.md
new file mode 100644
index 000000000..855a53d00
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/message-extraction.md
@@ -0,0 +1,200 @@
+---
+title: Message Extraction
+description: Learn about message extraction in i18n and how to use Lingui to extract messages from your application
+---
+
+# Message Extraction
+
+Message extraction is an essential step in the internationalization process. It involves analyzing your code and extracting all messages defined in it so that your message catalogs are always up-to-date with the source code.
+
+To extract messages (as marked with ``, `t` or other macros) from your application, use the `lingui extract` cli command.
+
+## Supported patterns
+
+The extractor operates on a static level and doesn't execute your code. As a result, complex patterns and dynamic code are not supported.
+
+### Macro usages
+
+Extractor supports all macro usages, such as the following examples:
+
+```tsx
+t`Message`;
+
+t({
+ id: "ID Some",
+ message: "Message with id some",
+});
+
+const jsx = Hi, my name is {name};
+```
+
+For more usage examples, refer to the [macro documentation](../ref/macro.mdx).
+
+### Non-Macro usages
+
+Note that the non-macro usage is not common. We recommend you use macros.
+
+The extractor matches `i18n._` or `i18n.t` function calls. It also matches when these functions are called from other member expressions, such as `ctx.i18n.t()`.
+
+:::note
+Extractor matches calls only by name. It doesn't check whether they were really imported from Lingui packages.
+:::
+
+```ts
+i18n._("message.id");
+i18n._({ id: "message.id" });
+
+ctx.i18n._("message.id");
+ctx.i18n.t("message.id");
+
+ctx.request.i18n.t("message.id");
+
+// and so on
+```
+
+You can ignore a specific call expression by adding a `lingui-extract-ignore` comment.
+
+```ts
+/* lingui-extract-ignore */
+ctx.i18n._("Message");
+```
+
+This message would not be extracted.
+
+### Explicitly marking messages
+
+Apart from call expressions, which are the most commonly used method, the extractor tool also supports simple string literals and message descriptors with explicit annotations.
+
+To do this, simply prefix your expression with the `/*i18n*/` comment, like so:
+
+```ts
+const messageDescriptor: MessageDescriptor = /*i18n*/ { id: "Description", comment: "description" };
+const stringLiteral = /*i18n*/ "Message";
+```
+
+## Unsupported Patterns
+
+The extractor is limited to extracting messages from code that is written in a certain way. It cannot extract messages from variables or function calls. It also cannot follow program structure and get the value of a variable defined elsewhere.
+
+This means that in order for a message to be extracted, it must be defined directly in the function call.
+
+For example, the following code cannot be extracted:
+
+```ts
+const message = "Message";
+i18n._(message);
+```
+
+Instead, you should define the message directly in the function arguments:
+
+```ts
+i18n._("Message");
+```
+
+## Defining sources for analyzing
+
+The lingui extract command can discover source files in two ways: by using a glob pattern or by crawling the dependency tree.
+
+### Glob Pattern
+
+By default, `lingui extract` uses a glob pattern to search for source files that contain messages.
+
+The pattern is defined in the `catalogs` property in the `lingui.config.js` file, which is located in the root directory of your project.
+
+
+
+
+### Dependency tree crawling (experimental)
+
+:::caution
+This is experimental feature. Experimental features not covered by semver and might be subject of a change.
+:::
+
+Although the glob-based extraction process is effective for most projects, however, multipage (MPA) frameworks such as NextJS pose a problem because the glob-based approach creates a catalog consisting of all messages from all pages.
+
+This means that the entire catalog must be loaded for each page/navigation, which results in loading messages that are not used on that page.
+
+To address this issue, a new `experimental-extractor` has been introduced in version 4.
+
+This extractor uses the dependency tree of files, rather than just a glob pattern, to crawl imports and discover files more accurately.
+
+By doing so, it creates a more optimized catalog that only contains the messages needed for each page.
+
+The catalogs would still contain duplicating messages for common components, but it would be much better than the current approach.
+
+
+
+
+To start using `experimental-extractor`, you need to add the following section to lingui config:
+
+```ts
+/**
+ *
+ * @type {import('@lingui/conf').LinguiConfig}
+ */
+module.exports = {
+ // remove everethying from `catalogs` property
+ catalogs: [],
+ // highlight-start
+ experimental: {
+ extractor: {
+ // glob pattern of entrypoints
+ // this will find all nextjs pages
+ entries: ["/src/pages/**/*.tsx"],
+ // output pattern, this instruct extractor where to store catalogs
+ // src/pages/faq.tsx -> src/pages/locales/faq/en.po
+ output: "/{entryDir}/locales/{entryName}/{locale}",
+ },
+ },
+ // highlight-end
+};
+```
+
+And then call in the terminal:
+
+```bash
+lingui extract-experimental
+```
+
+#### Notes
+
+It's worth noting that the accuracy of the catalog heavily relies on tree-shaking, a technique used by modern bundlers to eliminate unused code from the final bundle.
+
+If the code passed to the extractor is written in a tree-shakeable way, the user will receive highly accurate catalogs.
+
+While you might think that your code is tree-shakeable, in practice, tree-shaking might work differently than what you expect and some unwanted strings may be included in the catalogs.
+
+To illustrate, let's consider the following code:
+
+```ts
+import { msg } from "@lingui/macro";
+
+export const species = {
+ Cardano: [
+ {
+ startsAt: 0,
+ name: msg`Ghost`,
+ icon: "Ghost",
+ },
+ {
+ startsAt: 0.000001,
+ name: msg`Plankton`,
+ icon: "Plankton",
+ },
+ ],
+};
+```
+
+On the surface, it may appear that this code can be safely removed from the final bundle if it's not used. However, the `msg` function call can potentially produce a side effect, preventing the bundler from removing the entire `species` object from the final bundle. As a result, messages defined in this snippet may be included in more catalogs than expected.
+
+To avoid this issue, one solution is to wrap the `species` object inside an Immediately Invoked Function Expression (IIFE) and add the `/* @__PURE__ */` annotation.
+
+By adding this annotation to the IIFE, we are telling the bundler that the entire `species` object can be safely removed if it is not used or exported elsewhere in the code.
+
+## Supported source types
+
+The extractor supports TypeScript, Flow and JavaScript (Stage 3) out of the box.
+
+If you use some experimental features (Stage 0 - Stage 2) or frameworks with custom syntax such as Vue.js or Svelte, you may want to implement your custom extractor.
+
+Visit [Advanced: Custom Extractor](./custom-extractor.md) to learn how to create a custom extractor.
diff --git a/website/versioned_docs/version-4.x/guides/message-format.md b/website/versioned_docs/version-4.x/guides/message-format.md
new file mode 100644
index 000000000..2d068fafd
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/message-format.md
@@ -0,0 +1,60 @@
+# ICU MessageFormat
+
+ICU MessageFormat is a flexible yet powerful syntax to express all nuances of grammar for each language.
+
+## Overview
+
+### Simple text
+
+Example: `Refresh inbox`
+
+### Variables
+
+Example: `Attachment {name} saved`
+
+### Plurals
+
+> Using language specific plural forms (`one`, `other`):
+
+```icu-message-format
+{count, plural, one {Message} other {Messages}}
+```
+
+> Using exact matches (`=0`):
+
+```icu-message-format
+{count, plural, =0 {No messages}
+ one {# message}
+ other {# messages}}
+```
+
+> Offsetting plural form:
+
+```icu-message-format
+{count, plural, offset:1
+ =0 {Nobody read this message}
+ =1 {Only you read this message}
+ one {You and # friend read this message}
+ other {You and # friends read this message}
+```
+
+### Select
+
+```icu-message-format
+{gender, select, male {He replied to your message}
+ female {She replied to your message}
+ other {They replied to your message}}
+```
+
+### Ordinals
+
+```icu-message-format
+{count, selectOrdinal, one {#st message}
+ two {#nd message}
+ few {#rd message}
+ other {#th message}}
+```
+
+## Further reading
+
+- [ICU Playground](https://format-message.github.io/icu-message-format-for-translators/editor.html)
diff --git a/website/versioned_docs/version-4.x/guides/monorepo.md b/website/versioned_docs/version-4.x/guides/monorepo.md
new file mode 100644
index 000000000..d9ab5b1d8
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/monorepo.md
@@ -0,0 +1,7 @@
+# Monorepo
+
+If you're using lingui within a monorepo you need:
+
+- 1x `babel.config.js` within root
+- 1x `lingui.config.js` within root
+- And **n**-times `lingui.config.js` per package which extends/overrides from root
diff --git a/website/versioned_docs/version-4.x/guides/plurals.md b/website/versioned_docs/version-4.x/guides/plurals.md
new file mode 100644
index 000000000..aec117613
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/plurals.md
@@ -0,0 +1,109 @@
+---
+title: Pluralization
+description: Learn about pluralization and how to use it in your application with Lingui
+---
+
+# Pluralization
+
+Plurals are essential when dealing with internationalization. [LinguiJS](https://github.com/lingui/js-lingui) uses [CLDR Plural Rules](https://unicode-org.github.io/cldr-staging/charts/latest/supplemental/language_plural_rules.html).
+In general, there are 6 plural forms (taken from [CLDR Plurals](https://cldr.unicode.org/index/cldr-spec/plural-rules) page):
+
+- zero
+- one (singular)
+- two (dual)
+- few (paucal)
+- many (also used for fractions if they have a separate class)
+- other (required — general plural form — also used if the language
+ only has a single form)
+
+Only the last one, _other_, is required because it's the only common plural form used in all languages.
+
+All other plural forms depends on language. For example, English has only two: _one_ and _other_ (1 book vs. 2 books). In Czech, we have four: _one_, _few_, _many_ and _other_ (1 kniha, 2 knihy, 1,5 knihy, 5 knih). Some languages have even more, like Arabic.
+
+## Using plural forms
+
+Good thing is that **as developers, we have to know only plural forms for the source language**.
+
+If we use English in the source code, then we'll use only _one_ and _other_:
+
+```js
+plural(numBooks, {
+ one: "# book",
+ other: "# books",
+});
+```
+
+When `numBooks == 1`, this will render as _1 book_ and for `numBook == 2` it will be _2 books_.
+
+Interestingly, for `numBooks == -1`, it will be _-1 book_. This is because the "one" plural form also applies to -1. It is therefore important to remember that the plural forms (such as "one" or "two") do not represent the numbers themselves, but rather _categories_ of numbers.
+If you want to specify a message for an exact number, use [`exact matches`](/guides/message-format#plurals).
+
+> Funny fact for non-English speakers: In English, 0 uses plural form too, _0 books_.
+
+Under the hood, [`plural`](../ref/macro.mdx#plural) is replaced with low-level [`i18n._`](../ref/core.md#i18n._). For production, the above example will become:
+
+```js
+i18n._({
+ id: "d1wX4r",
+ // stripped on production
+ // message: '{numBooks, plural, one {# book} other {# books}}',
+ values: { numBooks },
+});
+```
+
+When we extract messages from source code using the [CLI tool](../ref/cli.md), we get:
+
+```icu-message-format
+{numBooks, plural, one {# book} other {# books}}
+```
+
+Now, we give it to our Czech translator, and they'll translate it as:
+
+```icu-message-format
+{numBooks, plural, one {# kniha} few {# knihy} many {# knihy} other {# knih}}
+```
+
+The important thing is that _we don't need to change our code to support languages with different plural rules_. Here's a step-by-step description of the process:
+
+1. In source code, we have:
+
+ ```js
+ plural(numBooks, {
+ one: "# book",
+ other: "# books",
+ });
+ ```
+
+2. Code is compiled to:
+
+ ```js
+ i18n._({
+ id: "d1wX4r",
+ // stripped on production
+ // message: '{numBooks, plural, one {# book} other {# books}}',
+ values: { numBooks },
+ });
+ ```
+
+3. Message `{numBooks, plural, one {# book} other {# books}}` is translated to:
+
+ ```icu-message-format
+ {numBooks, plural, one {# kniha} few {# knihy} many {# knihy} other {# knih}}
+ ```
+
+4. Finally, message is formatted using Czech plural rules.
+
+## Source code in language other than English
+
+As mentioned above, as developers, we have to know and use only plural forms for the source language. Go see what [plural forms](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html) your languages has and then you can use them. Here's the example in Czech:
+
+```js
+plural(numBooks, {
+ one: "# kniha",
+ few: "# knihy",
+ many: "# knihy",
+ other: "# knih",
+});
+```
+
+This make [LinguiJS](https://github.com/lingui/js-lingui) useful also for unilingual projects, i.e: if you don't translate your app at all. Plurals, number and date formatting are common in every language.
diff --git a/website/versioned_docs/version-4.x/guides/pseudolocalization.md b/website/versioned_docs/version-4.x/guides/pseudolocalization.md
new file mode 100644
index 000000000..62c450d67
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/pseudolocalization.md
@@ -0,0 +1,48 @@
+---
+title: Pseudolocalization
+description: Learn how to use pseudolocalization to test the internationalization aspects of your application with Lingui
+---
+
+# Pseudolocalization
+
+There is built in support for [pseudolocalization](https://en.wikipedia.org/wiki/Pseudolocalization). Pseudolocalization is a method for testing the internationalization aspects of your application by replacing your strings with altered versions and maintaining string readability. It also makes hard coded strings and improperly concatenated strings easy to spot so that they can be properly localized.
+
+> Example: Ţĥĩś ţēxţ ĩś ƥśēũďōĺōćàĺĩźēď
+
+## Configuration
+
+To setup pseudolocalization add [`pseudoLocale`](../ref/conf.md#pseudolocale) to your lingui [`configuration file`](../ref/conf.md):
+
+```json
+{
+ "lingui": {
+ "locale": ["en", "pseudo-LOCALE"],
+ "pseudoLocale": "pseudo-LOCALE",
+ "fallbackLocales": {
+ "pseudo-LOCALE": "en"
+ }
+ }
+}
+```
+
+[`pseudoLocale`](../ref/conf.md#pseudolocale) option can be any string that is in `locale`
+
+Examples: `en-PL`, `pseudo-LOCALE`, `pseudolocalization` or `en-UK`
+
+## Create pseudolocalization
+
+[`pseudoLocale`](../ref/conf.md#pseudolocale) string has to be in [`locales`](../ref/conf.md#locales) config as well. Otherwise, no folder and no pseudolocalization is going to be created. After running [`extract`](../ref/cli.md#extract) verify that the folder has been created. The pseudolocalization is automatically created on [`compile`](../ref/cli.md#compile) from messages. In case fallbackLocales has been used, the pseudolocalization is going to be created from translated fallback locale.
+
+## Switch browser into specified pseudoLocale
+
+We can use browsers settings or extensions. Extensions allow to use any locale. Browsers are usually limited into valid language tags (BCP 47). In that case, the locale for pseudolocalization has to be standard locale, which is not used in your application for example `zu_ZA` Zulu - SOUTH AFRICA
+
+Chrome:
+
+- With extension (valid locale) - [Locale Switcher](https://chrome.google.com/webstore/detail/locale-switcher/kngfjpghaokedippaapkfihdlmmlafcc)
+- Without extension (valid locale) - [chrome://settings/?search=languages](chrome://settings/?search=languages)
+
+Firefox:
+
+- With extension (any string) - [Quick Accept-Language Switcher](https://addons.mozilla.org/en-GB/firefox/addon/quick-accept-language-switc/?src=search)
+- Without extension (valid locale) - [about:preferences#general](about:preferences#general) > _Language_
diff --git a/website/versioned_docs/version-4.x/guides/testing.md b/website/versioned_docs/version-4.x/guides/testing.md
new file mode 100644
index 000000000..02a0fd020
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/testing.md
@@ -0,0 +1,43 @@
+# Testing
+
+Components using [`Trans`](../ref/react.md#trans) or [`useLingui`](../ref/react.md#uselingui) require access to the context of [`I18nProvider`](../ref/react.md#i18nprovider). How you can wrap your component with the I18nProvider depends on the test library you use.
+
+Here is a working example with [react-testing-library](https://testing-library.com/docs/react-testing-library/intro/), using the [wrapper-property](https://testing-library.com/docs/react-testing-library/api#wrapper):
+
+```tsx title="index.js"
+import React from "react";
+import { getByText, render, act } from "@testing-library/react";
+import { i18n } from "@lingui/core";
+import { I18nProvider } from "@lingui/react";
+
+import { messages } from "./locales/en/messages";
+import { messages as csMessages } from "./locales/cs/messages";
+import App from "./App";
+
+i18n.load({
+ en: messages,
+ cs: csMessages,
+});
+
+const TestingProvider = ({ children }: any) => {children};
+
+test("Content should be translated correctly in English", () => {
+ act(() => {
+ i18n.activate("en");
+ });
+ const { getByTestId, container } = render(, { wrapper: TestingProvider });
+ expect(getByTestId("h3-title")).toBeInTheDocument();
+ expect(getByText(container, "Language switcher example:")).toBeDefined();
+});
+
+test("Content should be translated correctly in Czech", () => {
+ act(() => {
+ i18n.activate("cs");
+ });
+ const { getByTestId, container } = render(, { wrapper: TestingProvider });
+ expect(getByTestId("h3-title")).toBeInTheDocument();
+ expect(getByText(container, "Příklad přepínače jazyků:")).toBeDefined();
+});
+```
+
+You could define a custom renderer to re-use this TestingProvider, see [react testing library - Custom Render](https://testing-library.com/docs/react-testing-library/setup#custom-render)
diff --git a/website/versioned_docs/version-4.x/guides/typescript.md b/website/versioned_docs/version-4.x/guides/typescript.md
new file mode 100644
index 000000000..1bdbcdf3e
--- /dev/null
+++ b/website/versioned_docs/version-4.x/guides/typescript.md
@@ -0,0 +1,22 @@
+# Typescript
+
+Lingui is written in Typescript and ships with TS typings out of the box. You should not need to do anything to get type support working.
+
+## Macros types in non-React environments
+
+We investigated how macros can be used on Typescript environments where React isn't required.
+
+Now we're shipping two declaration types:
+
+- `index.d.ts` files with `@lingui/core`, `@lingui/react` and `react` as peerDependencies.
+- `global.d.ts` files with just `@lingui/core` as peerDependencies.
+
+Now you can modify your `tsconfig.json` in your root directory and reference the global file:
+
+```json title="tsconfig.json"
+{
+ "compilerOptions": {
+ "types": ["./node_modules/@lingui/macro/global"]
+ }
+}
+```
diff --git a/website/versioned_docs/version-4.x/installation.mdx b/website/versioned_docs/version-4.x/installation.mdx
new file mode 100644
index 000000000..523632205
--- /dev/null
+++ b/website/versioned_docs/version-4.x/installation.mdx
@@ -0,0 +1,200 @@
+---
+title: Installation and Setup
+description: Learn how to install Lingui in your project
+---
+
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
+# Installation and Setup
+
+Lingui is more than just a package; it's a comprehensive suite of tools designed to simplify internationalization. You have the flexibility to choose the specific tools that best fit your project's needs.
+
+Learn how to install Lingui in your project, whether you use JavaScript, React (including RSC) or React Native. Lingui also supports various transpilers and build tools, such as Babel, SWC, and Vite.
+
+## Prerequisites
+
+- Make sure you have [Node.js](https://nodejs.org/) installed (v20 or higher).
+- Install [Lingui CLI](/ref/cli) to manage your translations and catalogs.
+
+:::tip
+Don't miss the [Lingui ESLint Plugin](/ref/eslint-plugin) which can help you find and prevent common i18n mistakes in your code.
+:::
+
+## Choosing a Transpiler
+
+> A transpiler converts code within a language, transforming newer features into older equivalents for compatibility, or expanding concise syntax into more verbose implementations.
+
+Lingui needs a transpiler to work. It's responsible for transforming Lingui's JS/JSX components into [ICU MessageFormat](/guides/message-format) and extracting message IDs. Both Babel and SWC transpilers are supported. Follow the Babel or SWC setup depending on what transpiler your project already uses.
+
+
+
+
+> Babel is a JavaScript transpiler that converts modern code into backward-compatible versions and allows custom syntax transformations.
+
+Lingui requires `@lingui/babel-plugin-lingui-macro` (recommended) or [`babel-plugin-macros`](https://github.com/kentcdodds/babel-plugin-macros) to perform the transformation.
+
+If you are using a framework that doesn't allow you to change the Babel configuration (e.g. Create React App > 2.0), these frameworks may support `babel-plugin-macros` out of the box.
+
+Follow these steps to set up Lingui with Babel:
+
+1. Install the `@lingui/babel-plugin-lingui-macro` package as a development dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev @lingui/babel-plugin-lingui-macro
+ ```
+
+2. Add `@lingui/babel-plugin-lingui-macro` to the top of the `plugins` section of your Babel config (e.g. `.babelrc`):
+
+ ```json
+ {
+ "plugins": ["@lingui/babel-plugin-lingui-macro"]
+ }
+ ```
+
+:::tip
+When using a preset, first check if it includes the `macros` plugin. If so, then you don't need to install and set up `@lingui/babel-plugin-lingui-macro`. For example, `react-scripts` already includes the `macros` plugin.
+:::
+
+
+
+
+> SWC is an extensible Rust-based platform for the next generation of fast developer tools.
+
+Lingui supports SWC with a dedicated plugin [`@lingui/swc-plugin`](/ref/swc-plugin). SWC is significantly faster than Babel and is a good choice for large projects.
+
+Follow these steps to set up Lingui with SWC:
+
+1. Install `@lingui/swc-plugin` as a development dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev @lingui/swc-plugin
+ ```
+
+2. [Add necessary configurations](/ref/swc-plugin#usage).
+
+:::caution
+SWC Plugin support is still experimental. Semver backwards compatibility between different `@swc/core` versions is not guaranteed. See the [SWC compatibility](/ref/swc-plugin#swc-compatibility) for more information.
+:::
+
+
+
+
+## Basic Configuration
+
+Lingui needs a configuration file to work. The configuration file specifies the source files, message catalogs, and other settings.
+
+Let's create a basic configuration file in the root of your project (next to `package.json`):
+
+```js title="lingui.config.js"
+import { defineConfig } from "@lingui/cli";
+
+export default defineConfig({
+ sourceLocale: "en",
+ locales: ["cs", "en"],
+ catalogs: [
+ {
+ path: "/src/locales/{locale}/messages",
+ include: ["src"],
+ },
+ ],
+});
+```
+
+The configuration above specifies the source locale as English and the target locales as Czech and English.
+
+According to this configuration, Lingui will extract messages from source files in the `src` directory and write them to message catalogs in `src/locales` (the English catalog would be in `src/locales/en/messages.po`, for example). See [Configuration](/ref/conf) for a complete reference.
+
+:::note
+Replace `src` with the name of the directory where you have the source files.
+:::
+
+The PO format is the default and recommended format for message catalogs. See the [Catalog Formats](/ref/catalog-formats) for other available formats.
+
+## Build Tools
+
+### Vite
+
+> Vite is a blazing fast frontend build tool powering the next generation of web applications.
+
+Lingui supports Vite with a dedicated plugin [`@lingui/vite-plugin`](/ref/vite-plugin). This plugin is responsible for extracting messages from your source code and compiling message catalogs.
+
+There are two ways to set up Lingui with Vite by using the [`@vitejs/plugin-react`](https://www.npmjs.com/package/@vitejs/plugin-react) or [`@vitejs/plugin-react-swc`](https://www.npmjs.com/package/@vitejs/plugin-react-swc). You need to choose the one that fits your project setup.
+
+
+
+
+The `@vitejs/plugin-react` plugin uses Babel to transform your code. To use Lingui with Vite and Babel, follow these steps:
+
+1. Follow the [Choosing a Transpiler](#choosing-a-transpiler) instructions.
+
+2. Install `@lingui/vite-plugin` as a development dependency and `@lingui/react` as a runtime dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev @lingui/vite-plugin
+ npm install --save @lingui/react
+ ```
+
+3. Setup Lingui in `vite.config.ts`:
+
+ ```ts title="vite.config.ts"
+ import { defineConfig } from "vite";
+ import react from "@vitejs/plugin-react";
+ import { lingui } from "@lingui/vite-plugin";
+
+ export default defineConfig({
+ plugins: [
+ react({
+ babel: {
+ plugins: ["@lingui/babel-plugin-lingui-macro"],
+ },
+ }),
+ lingui(),
+ ],
+ });
+ ```
+
+:::info
+The `@vitejs/plugin-react` does not use the Babel config (e.g. `babel.rc`) from your project by default. You have to enable it manually or specify Babel options directly in `vite.config.ts`.
+:::
+
+
+
+
+The `@vitejs/plugin-react-swc` plugin uses SWC to transform your code, which is significantly faster than Babel. To use Lingui with Vite and SWC, follow these steps:
+
+1. Follow the [Choosing a Transpiler](#choosing-a-transpiler) instructions.
+
+2. Install `@lingui/vite-plugin`, `@lingui/swc-plugin` as development dependencies and `@lingui/react` as a runtime dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev @lingui/vite-plugin @lingui/swc-plugin
+ npm install --save @lingui/react
+ ```
+
+3. Setup Lingui in `vite.config.ts`:
+
+ ```ts title="vite.config.ts"
+ import { defineConfig } from "vite";
+ import react from "@vitejs/plugin-react-swc";
+ import { lingui } from "@lingui/vite-plugin";
+
+ export default defineConfig({
+ plugins: [
+ react({
+ plugins: [["@lingui/swc-plugin", {}]],
+ }),
+ lingui(),
+ ],
+ });
+ ```
+
+
+
+
+## See Also
+
+- [React i18n Tutorial](/tutorials/react)
+- [React Server Components Tutorial](/tutorials/react-rsc)
+- [React Native i18n Tutorial](/tutorials/react-native)
+- [JavaScript i18n Tutorial](/tutorials/javascript)
diff --git a/website/versioned_docs/version-4.x/introduction.md b/website/versioned_docs/version-4.x/introduction.md
new file mode 100644
index 000000000..ef9946288
--- /dev/null
+++ b/website/versioned_docs/version-4.x/introduction.md
@@ -0,0 +1,111 @@
+---
+title: Internationalization Framework for Global Products
+description: Lingui is a universal, clean and readable, lightweight and powerful internationalization framework for global projects
+---
+
+# Introduction
+
+📖 A readable, automated, and optimized (3 kb) internationalization for JavaScript
+
+> **Internationalization** is the design and development of a product, application or document content that enables easy **localization** for target audiences that vary in culture, region, or language.
+>
+> — [W3C Web Internationalization FAQ](https://www.w3.org/International/questions/qa-i18n)
+
+[](https://github.com/lingui/js-lingui/)
+
+## Key features
+
+Lingui is an easy yet powerful internationalization framework for global projects.
+
+### Clean and readable
+
+Keep your code clean and readable, while the library uses battle-tested and powerful **ICU MessageFormat** under the hood.
+
+### Universal
+
+Use it everywhere. [`@lingui/core`](./ref/core.md) provides the essential intl functionality which works in any JavaScript project while [`@lingui/react`](./ref/react.md) offers components to leverage React rendering.
+
+### Full rich-text support
+
+Use React components inside localized messages without any limitation. Writing rich-text messages is as easy as writing JSX.
+
+### AI Translations Ready
+
+For AI to do great translations for you, context is critical. Translating UI copy is difficult because it's usually a list of short strings without enough context. Lingui's localization formats allow developers to write descriptions of where and how your keys are used. This allows both human translators and AI to make better translations.
+
+### Powerful tooling
+
+Manage the whole intl workflow using Lingui [CLI](./ref/cli.md). It extracts messages from source code, validates messages coming from translators and checks that all messages are translated before shipping to production.
+
+### Unopinionated
+
+Integrate Lingui into your existing workflow. It supports explicit message keys as well as auto-generated ones. Translations are stored either in JSON or standard PO file, which is supported in almost all translation tools.
+
+### Lightweight and optimized
+
+Core library is only [1.5 kB gzipped](https://bundlephobia.com/result?p=@lingui/core), React components are additional [1.3 kB gzipped](https://bundlephobia.com/result?p=@lingui/react). That's less than Redux for a full-featured intl library.
+
+### Active community
+
+Join us on [GitHub Discussions](https://github.com/lingui/js-lingui/discussions) to discuss the latest development or ask questions.
+
+### Compatible with react-intl
+
+Low-level React API is very similar to react-intl and the message format is the same. It's easy to migrate an existing project.
+
+## Quick overview
+
+```jsx
+import React from "react";
+import { t, Trans, Plural } from "@lingui/macro";
+
+export default function Lingui({ numUsers, name = "You" }) {
+ return (
+
+
+ {/* Localized messages are simply wrapped in */}
+ Internationalization in React
+
+
+ {/* Element attributes are translated using t macro */}
+
+
+
+ {/* Variables are passed to messages in the same way as in JSX */}
+
+ Hello {name}, LinguiJS is a readable, automated, and optimized (3 kb) internationalization for JavaScript.
+
+
+
+ {/* React Elements inside messages works in the same way as in JSX */}
+
+
+ {/*
+ Plurals are managed using ICU plural rules.
+ Content of one/other slots is localized using .
+ Nesting of i18n components is allowed.
+ Syntactically valid message in ICU MessageFormat is guaranteed.
+ */}
+
+ Only one user is using this library!
+
+ }
+ other={
+
+ {numUsers} users are using this library!
+
+ }
+ />
+
+ );
+}
+```
diff --git a/website/versioned_docs/version-4.x/misc/i18next.md b/website/versioned_docs/version-4.x/misc/i18next.md
new file mode 100644
index 000000000..2e04ce5a7
--- /dev/null
+++ b/website/versioned_docs/version-4.x/misc/i18next.md
@@ -0,0 +1,221 @@
+---
+title: Lingui vs i18next
+description: Comparison of Lingui and i18next internationalization libraries
+---
+
+# Comparison with i18next
+
+[i18next](https://www.i18next.com/) is an internationalization-framework written in and for JavaScript. i18next and Lingui are two popular internationalization (i18n) libraries used for translating and localizing JS-based applications. Both libraries have their strengths and weaknesses, and which one is best for a particular project depends on the project's specific needs.
+
+## Basic comparison
+
+Here is a basic example of the i18next usage:
+
+```js
+import i18next from "i18next";
+
+i18next.init({
+ lng: "en",
+ resources: {
+ en: {
+ translation: {
+ key: "Hello world",
+ },
+ },
+ },
+});
+// ...
+document.getElementById("output").innerHTML = i18next.t("key");
+```
+
+Since the Lingui v4 release, there is a core function [i18n.t(...)](../ref/core.md#i18n.t) that allows doing pretty much the same thing. The following example shows how this works with Lingui:
+
+```js title="lingui.config.{js,ts}"
+/** @type {import('@lingui/conf').LinguiConfig} */
+module.exports = {
+ sourceLocale: "en",
+ locales: ["en", "cs", "fr"],
+ catalogs: [
+ {
+ path: "/src/locales/{locale}/messages",
+ include: ["src"],
+ },
+ ],
+};
+```
+
+```js
+import { i18n } from "@lingui/core";
+
+document.getElementById("output").innerHTML = i18n.t({ id: "key", message: "Hello world" });
+```
+
+:::note
+The `message` property can be specified in the case of [Message Extraction](../guides/message-extraction.md) usage flow. You can use the `i18n.t` function only with the `id`, but in this case you'll have to manage your localization catalogs yourself, without advantages of the [message extraction](../guides/message-extraction.md) feature.
+:::
+
+## Interpolation
+
+Interpolation is one of the most used functionalities in I18N. It allows integrating dynamic values into your translations.
+
+i18next sample:
+
+```js
+i18next.t("msg.name", { name: "Tom" });
+```
+
+Lingui sample:
+
+```js
+i18n._({ id: "msg.name", message: "My name is {name}", values: { name: "Tom" } });
+```
+
+## Formatting
+
+Both the Lingui and i18next formatting functions are based on the [Intl API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl).
+
+### Numbers
+
+i18next sample:
+
+```js
+i18next.t("intlNumber", { val: 1000 });
+// --> Some 1,000
+i18next.t("intlNumber", { val: 1000.1, minimumFractionDigits: 3 });
+// --> Some 1,000.100
+i18next.t("intlNumber", { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
+// --> Some 1,000.100
+i18next.t("intlNumberWithOptions", { val: 2000 });
+// --> Some 2,000.00
+i18next.t("intlNumberWithOptions", { val: 2000, minimumFractionDigits: 3 });
+// --> Some 2,000.000
+```
+
+Lingui sample:
+
+```js
+import { i18n } from "@lingui/core";
+
+i18n.activate("en");
+i18n.number(12345.678);
+// Returns "12,345.678"
+
+i18n.number(12345.678, { style: "currency", currency: "USD" });
+// Returns "$12,345.68"
+
+i18n.activate("cs");
+i18n.number(12345.678);
+// Returns "12 345,678"
+
+i18n.number(12345.678, { style: "currency", currency: "CZK" });
+// Returns "12 345,68 Kč"
+```
+
+### DateTime
+
+i18next sample:
+
+```js
+i18next.t("intlDateTime", { val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) });
+// --> On the 12/20/2012
+i18next.t("intlDateTime", {
+ val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)),
+ formatParams: {
+ val: { weekday: "long", year: "numeric", month: "long", day: "numeric" },
+ },
+});
+// --> On the Thursday, December 20, 2012
+```
+
+Lingui sample:
+
+```js
+import { i18n } from "@lingui/core";
+
+const d = new Date("2021-07-23T16:23:00");
+
+i18n.activate("en");
+i18n.date(d);
+// Returns "7/23/2021"
+
+i18n.date(d, { timeStyle: "medium" });
+// Returns "4:23:00 PM"
+
+i18n.date(d, { dateStyle: "medium", timeStyle: "medium" });
+// Returns "Jul 23, 2021, 4:23:00 PM"
+```
+
+## Plurals
+
+Lingui uses the ICU MessageFormat syntax to handle plurals. It provides a simple and translator-friendly approach to plurals localization.
+
+For example:
+
+```js
+plural(numBooks, {
+ one: "# book",
+ other: "# books",
+});
+```
+
+Under the hood, plural is replaced with low-level `i18n._`. For production, the above example will become:
+
+```js
+i18n._({
+ id: "d1wX4r",
+ // stripped on production
+ // message: '{numBooks, plural, one {# book} other {# books}}',
+ values: { numBooks },
+});
+```
+
+When we extract messages from source code using Lingui CLI, we get:
+
+```icu-message-format
+{numBooks, plural, one {# book} other {# books}}
+```
+
+i18next handles plurals differently. It requires a separate key to be defined for each plural form. This is not translator-friendly, lacks context, and is prone to errors:
+
+```json
+{
+ "key_one": "item",
+ "key_other": "items"
+}
+```
+
+```js
+i18next.t("key", { count: 0 }); // -> "items"
+i18next.t("key", { count: 1 }); // -> "item"
+i18next.t("key", { count: 5 }); // -> "items"
+```
+
+## Context
+
+By providing a context you can differ translations. Both i18next and Lingui have the context feature to differentiate messages.
+
+## Summary
+
+This is a rather short comparison. Both libraries have quite different concepts, but at the same time the core internationalization approaches are similar and use the same background.
+
+On top of that, [Lingui](https://github.com/lingui/js-lingui):
+
+- supports rich-text messages
+- provides macros to simplify writing messages using MessageFormat syntax
+- provides a CLI for extracting and compiling messages
+- supports a number of [Catalog formats](../ref/catalog-formats.md), including [Custom Formatters](../guides/custom-formatter.md)
+- is very small (**3kb** gzipped), fast, flexible, and stable
+- works for vanilla JS, Next.js, Vue.js, Node.js etc.
+- is actively maintained.
+
+On the other hand, [i18next](https://www.i18next.com/):
+
+- mature. Based on how long i18next already is available open source, there is no real i18n case that could not be solved with i18next
+- extensible
+- has a big ecosystem.
+
+Lingui is a great choice for projects that require modern and efficient translation approaches, support for popular frameworks, and tools for managing translations. However, whether Lingui is better than i18next or not depends on the specific needs of the project.
+
+## Discussion
+
+Do you have any comments or questions? Please join the discussion at [GitHub](https://github.com/lingui/js-lingui/discussions) or raise an [issue](https://github.com/lingui/js-lingui/issues/new). All feedback is welcome!
diff --git a/website/versioned_docs/version-4.x/misc/react-intl.md b/website/versioned_docs/version-4.x/misc/react-intl.md
new file mode 100644
index 000000000..8ca62759f
--- /dev/null
+++ b/website/versioned_docs/version-4.x/misc/react-intl.md
@@ -0,0 +1,235 @@
+---
+title: Lingui vs react-intl
+description: Comparison of Lingui and react-intl internationalization libraries
+---
+
+# Comparison with react-intl
+
+[react-intl](https://github.com/formatjs/formatjs) (Format.js) is popular and widely-used i18n library for React. [Lingui](https://github.com/lingui/js-lingui) is in many ways very similar: both libraries use the same syntax for messages (ICU MessageFormat) and they also have very similar API.
+
+Here's an example from [react-intl](https://github.com/formatjs/formatjs) docs:
+
+```jsx
+{name}, unreadCount }}
+/>
+```
+
+Looking at the low-level API of [Lingui](https://github.com/lingui/js-lingui), there isn't much difference:
+
+```jsx
+{name}, unreadCount }}
+/>
+```
+
+There's really no reason to reinvent the wheel when both libs are build on top of the same message syntax. The story doesn't end here, though.
+
+## Translations with rich-text markup
+
+Suppose we have the following text:
+
+```html
+
+```
+
+In react-intl, this would be translated as:
+
+```jsx
+{chunks},
+ }}
+/>
+```
+
+[Lingui](https://github.com/lingui/js-lingui) extends ICU MessageFormat with tags. The example above would be:
+
+```jsx
+ }} />
+```
+
+and the translator gets the message in one piece: `Read the documentation`.
+
+However, let's go yet another level deeper.
+
+## Macros for component-based message syntax
+
+[Lingui](https://github.com/lingui/js-lingui) provides macros [`@lingui/macro`](../ref/macro.mdx) which automatically generates a message syntax.
+
+Let's go back to the previous example:
+
+```html
+
+```
+
+The macro then parses the [`Trans`](../ref/macro.mdx#trans) macro children and generates `message` and `components` props automatically in the form described in the previous section.
+
+This is extremely useful when adding i18n to an existing project. All we need is to wrap all messages in [`Trans`](../ref/macro.mdx#trans) macro.
+
+Let's compare it with react-intl solution to see the difference:
+
+```jsx
+
+```
+
+:::note
+It' also worth mentioning that the message IDs are completely optional. [Lingui](https://github.com/lingui/js-lingui) is unopinionated in this way and perfectly works with messages as IDs as well:
+
+```html
+
+```
+
+The message ID is `Read the <0>documentation0>.` instead of `msg.docs`. Both solutions have pros and cons and the library lets you choose the one which works best for you.
+:::
+
+## Plurals
+
+Another very common linguistic feature is pluralization.
+
+Let's take a look at the original example from react-intl docs:
+
+```jsx
+{name}, unreadCount }}
+/>
+```
+
+Using [Lingui](https://github.com/lingui/js-lingui) macros, we could combine [`Trans`](../ref/macro.mdx#trans), [`Plural`](../ref/macro.mdx#plural-1) components and [`i18n.number`](../ref/core.md#i18n.number) macro:
+
+```jsx
+
+ Hello {name}, you have {i18n.number(unreadCount)}
+
+```
+
+and the final message would be very similar:
+
+```jsx
+{name}0>, you have {unreadCount, number} {unreadCount, plural,
+ one {message}
+ other {messages}
+ }`}
+ values={{ name, unreadCount }}
+/>
+```
+
+The only difference is the `<0>` tag included in the message, as [LinguiJS](https://github.com/lingui/js-lingui) can handle components in both variables and the message itself.
+
+:::note
+It's good to mention here that this isn't the best example of using plurals. Make your translators happy and move plurals to the top of the message:
+
+```jsx
+
+ Hello {name}, you have {i18n.number(unreadMessages)} message.
+ >
+ }
+ other={
+ <>
+ Hello {name}, you have {i18n.number(unreadMessages)} messages.
+ >
+ }
+/>
+```
+
+Even though both variants are syntactically valid in ICU MessageFormat, the second one is easier for translating, because (again) the translator gets the phrase in one piece.
+:::
+
+## Text attributes
+
+Components can't be used in some contexts, e.g. to translate text attributes. Whereas react-intl provides JS methods (e.g: `formatMessage`) which return plain strings, [Lingui](https://github.com/lingui/js-lingui) offers its core library for such translations. And it also provides macros for these use-cases!
+
+Here are a few short examples:
+
+```jsx
+{name}
+
+```
+
+Custom IDs are supported as well:
+
+```jsx
+{name}
+
+```
+
+:::note
+To inject `i18n` object into props, you need to use [`useLingui`](../ref/react.md#uselingui) hook. It's very similar to `useIntl` from [react-intl](https://formatjs.io/docs/react-intl/api/#useintl-hook).
+:::
+
+## External message catalog
+
+Let's say our app has been internationalized and we now want to send the messages to the translator.
+
+[react-intl](https://github.com/formatjs/formatjs) comes with the Babel plugin which extracts messages from individual files, but it's up to you to merge them into one file which you can send to translators.
+
+[Lingui](https://github.com/lingui/js-lingui) provides handy [`CLI`](../ref/cli.md) which extracts messages and merges them with any existing translations. Again, the story doesn't end here.
+
+## Compiling messages
+
+The biggest and slowest part of i18n libraries are message parsers and formatters. [Lingui](https://github.com/lingui/js-lingui) compiles messages from MessageFormat syntax into JS functions which only accept values for interpolation (e.g. components, variables, etc). This makes the final bundle smaller and makes the library faster. The compiled catalogs are also bundled with locale data like plurals, so it's not necessary to load them manually.
+
+## Summary
+
+- both libraries use the same MessageFormat syntax
+- similar API (easy to port from one to the other)
+
+On top of that, [Lingui](https://github.com/lingui/js-lingui):
+
+- supports rich-text messages
+- provides macros to simplify writing messages using MessageFormat syntax
+- provides a CLI for extracting and compiling messages
+- is very small (**3kb** gzipped), fast, flexible, and stable
+- works for vanilla JS, Next.js, Vue.js, Node.js etc.
+- is actively maintained
+
+On the other hand, [react-intl](https://github.com/formatjs/formatjs):
+
+- is the most popular and used i18n lib in React
+- is used in many production websites (stability)
+- has lots of resources available online
+
+## Discussion
+
+Do you have any comments or questions? Please join the discussion at [GitHub](https://github.com/lingui/js-lingui/discussions) or raise an [issue](https://github.com/lingui/js-lingui/issues/new). All feedback is welcome!
diff --git a/website/versioned_docs/version-4.x/misc/resources.md b/website/versioned_docs/version-4.x/misc/resources.md
new file mode 100644
index 000000000..071742004
--- /dev/null
+++ b/website/versioned_docs/version-4.x/misc/resources.md
@@ -0,0 +1,20 @@
+# Talks and Articles
+
+## Articles
+
+- [How to Localize JavaScript and React Apps with LinguiJS](https://crowdin.com/blog/2022/12/13/lingui-i18n?utm_source=lingui.dev&utm_medium=referral&utm_campaign=lingui.dev) - by [Crowdin](https://crowdin.com/?utm_source=lingui.dev&utm_medium=referral&utm_campaign=lingui.dev)
+- [How to build a fully internationalized Nextjs application using Lingui v4](https://bravo-kernel.com/blog/2023/05/how-to-build-a-fully-internationalized-nextjs-application-using-lingui-v4) by [Bravo_Kernel](https://twitter.com/bravo_kernel)
+- [The complete guide to internationalization in Next.js](https://blog.logrocket.com/complete-guide-internationalization-nextjs/) - by [Ivan Vlatkovic](https://blog.logrocket.com/author/ivanvlatkovic/)
+- [How We Migrated a Large React Application from react-i18next to LinguiJS in 1 Day](https://medium.com/@radist2s/one-command-one-day-multiple-languages-our-migration-from-react-i18next-to-linguijs-4b07ac73a9bb) - by [Alex Batalov](https://medium.com/@radist2s)
+- [Javascript i18n with Lingui](https://mikewilliamson.wordpress.com/2017/11/05/javascript-i18n-with-lingui/) - by [Mike Williamson](https://mikewilliamson.wordpress.com/)
+- [Translate your Remix.run app with Lingui](https://www.simondepelchin.be/articles/translate-your-remix-run-app-with-lingui) - by [Simon Depelchin](https://x.com/SimonDepelchin)
+
+## Talks
+
+- [Let React speak your language](https://www.youtube.com/watch?v=soAEB7ltQPk) - by Tomáš Ehrlich
+- [Internationalization is a piece of cake](https://www.youtube.com/watch?v=vhUiL_wUAjo) - by Eli Schutze
+- [Localizing React Native apps talk from React Native EU 2022](https://www.youtube.com/live/uLicTDG5hSs?feature=share&t=7512) - by [Vojtech Novak](https://twitter.com/vonovak); covers RN but general i18n topics as well
+
+---
+
+Would like to add something to the list? Visit the [Contributing](/community#contributing) section.
diff --git a/website/versioned_docs/version-4.x/misc/showroom.md b/website/versioned_docs/version-4.x/misc/showroom.md
new file mode 100644
index 000000000..59de2f1ae
--- /dev/null
+++ b/website/versioned_docs/version-4.x/misc/showroom.md
@@ -0,0 +1,40 @@
+# Projects using Lingui
+
+Feel free to [send a PR](https://github.com/lingui/js-lingui/issues/new) to list your project here.
+
+- [Bluesky](https://bsky.app/) ([source](https://github.com/bluesky-social/social-app))
+- [Fider](https://fider.io/) ([source](https://github.com/getfider/fider))
+- [Flood](https://flood.js.org/) ([source](https://github.com/jesec/flood))
+- [Lenster](https://lenster.xyz/) ([source](https://github.com/lensterxyz/lenster))
+- [Ansible AWX](https://github.com/ansible/awx) ([source](https://github.com/ansible/awx))
+- [flowchart.fun](https://flowchart.fun/) ([source](https://github.com/tone-row/flowchart-fun))
+- [AntD Admin](https://github.com/zuiidea/antd-admin#readme) ([source](https://github.com/zuiidea/antd-admin))
+- [Linkerd](https://linkerd.io/) ([source](https://github.com/linkerd/linkerd2))
+- [Remirror](https://remirror.io/) ([source](https://github.com/remirror/remirror))
+- [Zipkin](https://zipkin.io/) ([source](https://github.com/openzipkin/zipkin))
+- [Authentik](https://goauthentik.io/) ([source](https://github.com/goauthentik/authentik))
+- [GDevelop](https://gdevelop.io/) ([source](https://github.com/4ian/GDevelop))
+- [Web Maker](https://webmaker.app/) ([source](https://github.com/chinchang/web-maker))
+- [prePO](https://prepo.io/) ([source](https://github.com/prepo-io/prepo-monorepo))
+- [Laf](https://www.lafyun.com/) ([source](https://github.com/labring/laf))
+- [Caliopen](https://www.caliopen.org/) ([source](https://github.com/CaliOpen/Caliopen/tree/master/src/frontend/web_application))
+- [Palgakalkulaator](https://www.palgakalkulaator.ee/) ([source](https://github.com/madisvain/palgakalkulaator))
+- [ledgy.com](https://www.ledgy.com/)
+- [MyMusicTaste](https://www.mymusictaste.com/)
+- [Staycation](https://www.staycation.co/)
+- [Monitora](https://monitora.cz/)
+- [Turisto](https://turisto.com/)
+- [Nolt](https://nolt.io/)
+- [easyname](https://www.easyname.com/)
+- [LocalEthereum](https://localethereum.com/)
+- [Upcount](https://github.com/madisvain/upcount)
+- [Symbolovník](http://www.symbolovnik.cz)
+- [Notos](https://www.notos.co)
+- [OkCupid](https://www.okcupid.com)
+- [mapflow.ai](https://mapflow.ai)
+- [lefun.fun](https://lefun.fun)
+- [Rove.me](https://rove.me)
+
+---
+
+Do you use Lingui in production or for hobby project? Please [let us know](https://github.com/lingui/js-lingui/discussions/1404), so we can add you to the showroom!
diff --git a/website/versioned_docs/version-4.x/misc/tooling.md b/website/versioned_docs/version-4.x/misc/tooling.md
new file mode 100644
index 000000000..ac42c1549
--- /dev/null
+++ b/website/versioned_docs/version-4.x/misc/tooling.md
@@ -0,0 +1,18 @@
+---
+title: Third party modules
+---
+
+# Tooling
+
+| Tool | Type | Description |
+| ----------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------- |
+| [`svelte-i18n-lingui`](https://www.npmjs.com/package/svelte-i18n-lingui) | addon | Add i18n to Svelte/Sveltekit projects using Lingui, with gettext-style message as catalog id |
+| [`@graphcommerce/lingui-next`](https://github.com/graphcommerce-org/graphcommerce/tree/main/packages/lingui-next) | addon | This package adds lingui to GraphCommerce |
+| [Lingui String Exporter](https://store.crowdin.com/lingui-string-exporter) | tool | Deliver new translations to your Lingui application over-the-air |
+| [lingui-action](https://github.com/trisbee/lingui-action) | tool | Run Lingui CLI in GitHub Actions workflow |
+| [Auto Translation Tool](https://auto-translation.now.sh/) | service | Tool for translating JSON files for LinguiJS |
+| [simpleen.io](https://simpleen.io/) | service | Online and CLI Tool to machine translate LinguiJS JSON files |
+
+---
+
+Would like to add something to the list? Visit the [Contributing](/community#contributing) section.
diff --git a/website/versioned_docs/version-4.x/ref/catalog-formats.md b/website/versioned_docs/version-4.x/ref/catalog-formats.md
new file mode 100644
index 000000000..5d0d8e711
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/catalog-formats.md
@@ -0,0 +1,292 @@
+---
+title: Message Catalog Formats
+description: Learn about the different catalog formats supported by Lingui
+---
+
+# Catalog Formats
+
+Catalog format (configured by the [`format`](./conf.md#format) option) refers to the offline catalog file format. This format is never used in production, because the catalog is compiled into a JS module.
+
+The reason for this build step is that the choice of catalog format depends on the individual internationalization workflow. On the other hand, the runtime catalog should be as simple as possible so that it can be parsed quickly without additional overhead.
+
+## PO
+
+PO files are translation files used by [gettext](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) internationalization system. This is the **recommended** and the **default** catalog format in Lingui.
+
+[![Version][badge-version-po]][package-po]
+[![Downloads][badge-downloads-po]][package-po]
+
+The advantages of this format are:
+
+- readable even for large messages
+- supports comments for translators
+- supports metadata (origin, flags)
+- supports contexts
+- standard format supported by many localization tools
+
+### Installation {#po-installation}
+
+```bash npm2yarn
+npm install --save-dev @lingui/format-po
+```
+
+### Usage {#po-usage}
+
+```js title="lingui.config.{js,ts}"
+import { formatter } from "@lingui/format-po";
+
+export default {
+ // [...]
+ format: formatter({ lineNumbers: false }),
+};
+```
+
+### Configuration {#po-configuration}
+
+PO formatter accepts the following options:
+
+| Option | Type | Default | Description |
+| ------------------------ | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `origins` | boolean | `true` | Include comments in the PO file that indicate where each message is used in the source code. This provides additional context during the translation |
+| `lineNumbers` | boolean | `true` | Include line numbers in the origin comments. This makes it easier to locate messages in the source code |
+| `printLinguiId` | boolean | `false` | Add a `js-lingui-id: hash` comment to each message in the PO file. This ID is a hash generated by Lingui |
+| `explicitIdAsDefault` | boolean | `false` | Use the `msgid` as is for messages with explicit IDs. The formatter will add the `js-lingui-explicit-id` flag for such strings |
+| `customHeaderAttributes` | `{[key: string]: string}` | `{}` | Allows adding custom key-value pairs to the PO file header |
+
+### Examples {#po-examples}
+
+```po
+#: src/App.js:3
+#. Comment for translators
+msgid "messageId"
+msgstr "Translated Message"
+
+#: src/App.js:3
+#, obsolete
+msgid "obsoleteId"
+msgstr "Obsolete Message"
+```
+
+Messages with context are exported in the following way:
+
+```po
+#: src/Inbox.js:12
+msgctxt "my context"
+msgid "msg.inbox"
+msgstr "Message Inbox"
+```
+
+Messages with plurals are exported in ICU format:
+
+```po
+msgid "{count, plural, one {Message} other {Messages}}"
+msgstr "{count, plural, one {Message} other {Messages}}"
+```
+
+Read more about [ICU MessageFormat](../guides/message-format.md).
+
+## PO with gettext Plurals {#po-gettext}
+
+When using localization backends that don't understand the ICU plural syntax exported by the default `po` formatter, **po-gettext** can be used to read and write to PO files using gettext-native plurals.
+
+[![Version][badge-version-po-gettext]][package-po-gettext]
+[![Downloads][badge-downloads-po-gettext]][package-po-gettext]
+
+### Installation {#po-gettext-installation}
+
+```bash npm2yarn
+npm install --save-dev @lingui/format-po-gettext
+```
+
+### Usage {#po-gettext-usage}
+
+```js title="lingui.config.{js,ts}"
+import { formatter } from "@lingui/format-po-gettext";
+
+export default {
+ // [...]
+ format: formatter({ lineNumbers: false }),
+};
+```
+
+### Configuration {#po-gettext-configuration}
+
+The PO Gettext formatter accepts the following options:
+
+| Option | Type | Default | Description |
+| ---------------------- | ------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `origins` | boolean | `true` | Include comments in the PO file that indicate where each message is used in the source code. This provides additional context during the translation |
+| `lineNumbers` | boolean | `true` | Include line numbers in the origin comments. This makes it easier to locate messages in the source code |
+| `disableSelectWarning` | boolean | `false` | Disable warnings about unsupported `Select` features encountered in catalogs. This can be useful if you're aware of the limitation and want to suppress related warnings |
+| `customICUPrefix` | string | `"js-lingui:"` | Override the default prefix for ICU and plural comments in the final PO catalog |
+
+### Examples {#po-gettext-examples}
+
+With this format, plural messages are exported in the following ways, depending on whether an explicit ID is set:
+
+- Message **with custom ID "my_message"** that is pluralized on property "_someCount_".
+
+ ```po
+ #. js-lingui:pluralize_on=someCount
+ msgid "my_message"
+ msgid_plural "my_message_plural"
+ msgstr[0] "Singular case"
+ msgstr[1] "Case number {someCount}"
+ ```
+
+ Note that `msgid_plural` was created by appending a `_plural` suffix.
+
+- Message **without custom ID** that is pluralized on property "_anotherCount_".
+
+ To allow matching this PO item to the appropriate catalog entry when deserializing, the original ICU message is also stored in the generated comment.
+
+ ```po
+ #. js-lingui:icu=%7BanotherCount%2C+plural%2C+one+%7BSingular+case%7D+other+%7BCase+number+%7BanotherCount%7D%7D%7D&pluralize_on=anotherCount
+ msgid "Singular case"
+ msgid_plural "Case number {anotherCount}"
+ msgstr[0] "Singular case"
+ msgstr[1] "Case number {anotherCount}"
+ ```
+
+ Note how `msgid` and `msgid_plural` were extracted from the original message.
+
+- Message **with a custom comment prefix**.
+
+ Some TMS might modify the ICU comment by attempting to split lines to be 80 characters or less, or have trouble reading lingui comments because of the `js-lingui:` prefix. To change the prefix, set `customICUPrefix` to modify the prefix for ICU comments.
+
+ ```po
+ # with default prefix
+ #. js-
+ #. lingui:icu=%7BanotherCount%2C+plural%2C+one+%7BSingular+case%7D+other+%7BCase+number+%7BanotherCount%7D%7D%7D&pluralize_on=anotherCount
+
+ # customICUPrefix = jsi18n:
+ #. jsi18n:icu=%7BanotherCount%2C+plural%2C+one+%7BSingular+case%7D+other+%7BCase+number+%7BanotherCount%7D%7D%7D&pluralize_on=anotherCount
+ ```
+
+### Limitations {#po-gettext-limitations}
+
+This format comes with several caveats and should only be used when using ICU plurals in PO files is not an option:
+
+- Nested/multiple plurals in a message as shown in [`plural`](./macro.mdx#plural) are not supported because they cannot be expressed with gettext plurals. Messages containing nested/multiple formats will not be output correctly.
+- The [`select`](./macro.mdx#select) and [`selectOrdinal`](./macro.mdx#selectordinal) cannot be expressed with gettext plurals, but the original ICU format is still stored in the `msgid`/`msgstr` properties. To disable the warning that this may not be the expected behavior, add `{ disableSelectWarning: true }` to the [`format`](./conf.md#format) options.
+- Source/development languages with more than two plurals could experience difficulties when no custom IDs are used, as gettext cannot have more than two plurals cases identifying an item (`msgid` and `msgid_plural`).
+- Gettext doesn't support plurals for negative and fractional numbers even though some languages have special rules for these cases.
+
+## JSON
+
+This format is used to store messages in a JSON file. There are two types of JSON format: [minimal](#minimal-style) and [lingui](#lingui-style).
+
+[![Version][badge-version-json]][package-json]
+[![Downloads][badge-downloads-json]][package-json]
+
+### Installation {#json-installation}
+
+```bash npm2yarn
+npm install --save-dev @lingui/format-json
+```
+
+### Usage {#json-usage}
+
+```js title="lingui.config.{js,ts}"
+import { formatter } from "@lingui/format-json";
+
+export default {
+ // [...]
+ format: formatter({ style: "lingui" }),
+};
+```
+
+### Configuration {#json-configuration}
+
+JSON formatter accepts the following options:
+
+| Option | Type | Default | Description |
+| ------------- | ------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `style` | `"lingui"` \| `"minimal"` | `"lingui"` | Specify the output style of the JSON file. `lingui` includes full Lingui-specific metadata, while `minimal` may output a more compact format |
+| `origins` | boolean | `true` | Include information in the JSON file about where each message is used in the source code. This provides additional context during the translation |
+| `lineNumbers` | boolean | `true` | Include line numbers in the origin comments. This makes it easier to locate messages in the source code |
+| `indentation` | number | `2` | Set the number of spaces to use for indentation in the output JSON file. This affects the readability of the file when opened in a text editor |
+
+### Examples {#json-examples}
+
+#### Minimal style
+
+A simple JSON file where each key is a message ID and the value is the translation. The JSON is flat, and there's no reason to use nested keys. The usual motivation behind nested JSON is to save file space, but this file format is only used offline.
+
+The downside of this format is that all metadata about the message is lost. This includes the default message, the origin of the message, and any message flags (obsolete, fuzzy, etc.).
+
+```json
+{
+ "messageId": "translation"
+}
+```
+
+#### Lingui style
+
+This file format simply outputs all internal data in JSON format. It's the original file format used by Lingui before support for other catalog formats was added. It might be useful for tools build on top of Lingui CLI which needs to further process catalog data.
+
+```json
+{
+ "messageId": {
+ "translation": "Translated message",
+ "message": "Default message",
+ "description": "Comment for translators",
+ "origin": [["src/App.js", 3]]
+ },
+ "obsoleteId": {
+ "translation": "Obsolete message",
+ "origin": [["src/App.js", 3]],
+ "obsolete": true
+ }
+}
+```
+
+## CSV
+
+The CSV format is a simple format that can be used to import and export messages from spreadsheets or other tools that support CSV files. It has two columns: message ID and message (source or translation).
+
+[![Version][badge-version-csv]][package-csv]
+[![Downloads][badge-downloads-csv]][package-csv]
+
+### Installation {#csv-installation}
+
+```bash npm2yarn
+npm install --save-dev @lingui/format-csv
+```
+
+### Usage {#csv-usage}
+
+```js title="lingui.config.{js,ts}"
+import { formatter } from "@lingui/format-csv";
+
+export default {
+ // [...]
+ format: formatter(),
+};
+```
+
+This formatter has no configurable options.
+
+### Examples {#csv-examples}
+
+```csv
+messageId,Message
+msg.common,String for translation
+```
+
+## See Also
+
+- [Custom Formatter](../guides/custom-formatter.md)
+
+[package-po]: https://www.npmjs.com/package/@lingui/format-po
+[package-po-gettext]: https://www.npmjs.com/package/@lingui/format-po-gettext
+[package-json]: https://www.npmjs.com/package/@lingui/format-json
+[package-csv]: https://www.npmjs.com/package/@lingui/format-csv
+[badge-downloads-po]: https://img.shields.io/npm/dw/@lingui/format-po.svg?cacheSeconds=86400
+[badge-downloads-po-gettext]: https://img.shields.io/npm/dw/@lingui/format-po-gettext.svg?cacheSeconds=86400
+[badge-downloads-json]: https://img.shields.io/npm/dw/@lingui/format-json.svg?cacheSeconds=86400
+[badge-downloads-csv]: https://img.shields.io/npm/dw/@lingui/format-csv.svg?cacheSeconds=86400
+[badge-version-po]: https://img.shields.io/npm/v/@lingui/format-po.svg?cacheSeconds=86400
+[badge-version-po-gettext]: https://img.shields.io/npm/v/@lingui/format-po-gettext.svg?cacheSeconds=86400
+[badge-version-json]: https://img.shields.io/npm/v/@lingui/format-json.svg?cacheSeconds=86400
+[badge-version-csv]: https://img.shields.io/npm/v/@lingui/format-csv.svg?cacheSeconds=86400
diff --git a/website/versioned_docs/version-4.x/ref/cli.md b/website/versioned_docs/version-4.x/ref/cli.md
new file mode 100644
index 000000000..0d2232ae9
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/cli.md
@@ -0,0 +1,231 @@
+---
+title: Lingui CLI
+description: Learn how to set up and use Lingui CLI to extract, merge and compile message catalogs
+---
+
+# Lingui CLI
+
+The `@lingui/cli` tool provides the `lingui` command, which allows the extraction of messages from source files into message catalogs and the compilation of message catalogs for production use.
+
+## Install
+
+1. Install `@lingui/cli` as a development dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev @lingui/cli
+ ```
+
+2. Add the following scripts to your `package.json`:
+
+ ```json title="package.json"
+ {
+ "scripts": {
+ "extract": "lingui extract",
+ "compile": "lingui compile"
+ }
+ }
+ ```
+
+:::tip
+If you use TypeScript, you can add the `--typescript` flag to the `compile` script to produce compiled message catalogs with TypeScript types:
+
+```json title="package.json"
+{
+ "scripts": {
+ "compile": "lingui compile --typescript"
+ }
+}
+```
+
+:::
+
+## Global options
+
+### `--config `
+
+Path to the configuration file. If not set, the default file is loaded as described in the [Lingui configuration](./conf.md) reference.
+
+## Commands
+
+### `extract`
+
+```shell
+lingui extract [files...]
+ [--clean]
+ [--overwrite]
+ [--format ]
+ [--locale ]
+ [--convert-from ]
+ [--verbose]
+ [--watch [--debounce ]]
+```
+
+The `extract` command looks for messages in the source files and extracts them
+
+This command scans the source files, identifies messages, and creates a separate message catalog for each language. The process includes the following steps:
+
+1. Extract messages from files based on the `include` and `exclude` options in the [`catalogs`](./conf.md#catalogs) section of the configuration file.
+2. Merge them with existing message catalogs (if any)
+3. Write updated message catalogs.
+4. Print statistics about the extracted messages for each language, showing the total number of messages and the number of missing translations.
+
+:::tip
+Visit the [Message Extraction](../guides/message-extraction.md) guide to learn more about how it works.
+:::
+
+#### `files` {#extract-files}
+
+Filters source paths to only extract messages from passed files. For ex:
+
+```shell
+lingui extract src/components
+```
+
+Will only extract messages from `src/components/**/*` files, you can pass multiple paths.
+
+It's useful if you want to run the extract command on files that are staged, for example using `husky`, before committing will extract messages from staged files:
+
+```json title="package.json"
+{
+ "husky": {
+ "hooks": {
+ "pre-commit": "lingui extract $(git diff --name-only --staged)"
+ }
+ }
+}
+```
+
+#### `--clean` {#extract-clean}
+
+By default, the `extract` command merges messages extracted from source files with the existing message catalogs. This is safe as we won't accidentally lose translated messages.
+
+However, over time, some messages may be removed from the source code. We can use this option to clean up our message catalogs from obsolete messages.
+
+#### `--overwrite` {#extract-overwrite}
+
+Update translations for [`sourceLocale`](./conf.md#sourcelocale) from source.
+
+#### `--format ` {#extract-format}
+
+Extract message catalogs to the specified file format (see the [`format`](./conf.md#format) option for more details).
+
+#### `--locale ` {#extract-locale}
+
+Extract data for the specified locale only.
+
+#### `--convert-from ` {#extract-convert-from}
+
+Convert message catalogs from the previous format (see the [`format`](./conf.md#format) option for more details).
+
+#### `--verbose` {#extract-verbose}
+
+Prints additional information.
+
+#### `--watch` {#extract-watch}
+
+Watch mode. Only watches for changes in files in paths defined in the config file or in the command itself. Remember to use this only in development, as this command does not clean up obsolete translations.
+
+#### `--debounce ` {#extract-debounce}
+
+Delays the extraction by `` milliseconds, bundling multiple file changes together.
+
+### `extract-template`
+
+```shell
+lingui extract-template [--verbose]
+```
+
+This command extracts messages from source files and creates a `.pot` template file. Any artifacts created by this command may be ignored in version control. If your message catalogs are not synchronized with the source and don't contain some messages, the application will fall back to the template file. This command is useful to run before building the application.
+
+#### `--verbose` {#extract-template-verbose}
+
+Prints additional information.
+
+### `compile`
+
+```shell
+lingui compile
+ [--strict]
+ [--format ]
+ [--verbose]
+ [--typescript]
+ [--namespace ]
+ [--watch [--debounce ]]
+```
+
+Once we have all the catalogs ready and translated, we can use this command to compile all the catalogs into minified JS/TS files. It compiles message catalogs in the [`path`](./conf.md#catalogs) directory and outputs minified JavaScript files. The resulting file is basically a string that is parsed into a plain JS object using `JSON.parse`.
+
+The output looks like this:
+
+```ts
+export const messages = JSON.parse(`{
+// object with keys (translation ids) and values (translations)
+}`);
+```
+
+Messages added to the compiled file are collected in a specific order:
+
+1. Translated messages from the specified locale.
+2. Translated messages from the fallback locale for the specified locale.
+3. Translated message from default fallback locale.
+4. Message key.
+
+It is also possible to merge the translated catalogs into a single file per locale by specifying [`catalogsMergePath`](./conf.md#catalogsmergepath) in the configuration.
+
+:::tip
+The compiled files can be safely ignored by your version control system, since these files must be created each time you deploy to production. We recommend you to create the compiled catalogs in CI as part of your deployment process. Always remember to **use compiled catalogs** in deployments.
+
+```ignore title=".gitignore"
+your_locale_folder/**/*.js
+```
+
+:::
+
+#### `--overwrite` {#compile-overwrite}
+
+Overwrite source locale translations from source.
+
+#### `--strict` {#compile-strict}
+
+Fail if a catalog has missing translations.
+
+#### `--format ` {#compile-format}
+
+Format of message catalogs (see the [`format`](./conf.md#format) option for more details).
+
+#### `--verbose` {#compile-verbose}
+
+Prints additional information.
+
+#### `--namespace` {#compile-namespace}
+
+Specify the namespace for compiled message catalogs (see also [`compileNamespace`](./conf.md#compilenamespace) for global configuration).
+
+#### `--typescript` {#compile-typescript}
+
+Is the same as using [`compileNamespace`](./conf.md#compilenamespace) with the value "ts". Generates a `{compiledFile}.ts` file and the exported object is typed using TS.
+
+#### `--watch` {#compile-watch}
+
+Watch mode. Watches only for changes in locale files in your defined locale catalogs. For example, `locales\en\messages.po`.
+
+#### `--debounce ` {#compile-debounce}
+
+Delays compilation by `` milliseconds to avoid multiple compilations for subsequent file changes.
+
+## Configuring the source locale
+
+One drawback to checking for missing translations is that the English message catalog doesn't need translations because our source code is in English. This can be addressed by configuring the [`sourceLocale`](./conf.md#sourcelocale) in the configuration file.
+
+## Compiling Catalogs in CI {#compiling-catalogs-in-ci}
+
+If you're using CI, it's a good idea to add the `compile` command to your build process. Alternatively, you can also use a [Webpack loader](./loader.md), [Vite plugin](./vite-plugin.md) or [Metro transformer](./metro-transformer.mdx).
+
+Depending on your localization setup, you might also want to run the `extract` command in CI and upload the extracted messages to a [translation service](../tools/introduction.md).
+
+## Further reading
+
+- [Lingui Configuration](./conf.md)
+- [Message Extraction](../guides/message-extraction.md)
+- [Catalog Formats](./catalog-formats.md)
+- [Custom Extractor](../guides/custom-extractor.md)
diff --git a/website/versioned_docs/version-4.x/ref/conf.md b/website/versioned_docs/version-4.x/ref/conf.md
new file mode 100644
index 000000000..ca00c8e42
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/conf.md
@@ -0,0 +1,493 @@
+---
+title: Lingui Configuration
+description: Learn how to configure Lingui for your project
+---
+
+# Lingui Configuration
+
+Configuration is read from 4 different sources (the first found wins):
+
+- from `lingui` section in `package.json`
+- from `.linguirc`
+- from `lingui.config.js`
+- from `lingui.config.ts` _(since 3.4.0)_
+
+You can also define environment variable `LINGUI_CONFIG` with path to your config file.
+
+In the case of TypeScript based config you can use ESM format and _export default_.
+
+Default config:
+
+```json
+{
+ "catalogs": [
+ {
+ "path": "/locale/{locale}/messages",
+ "include": [""],
+ "exclude": ["**/node_modules/**"]
+ }
+ ],
+ "catalogsMergePath": "",
+ "compileNamespace": "cjs",
+ "extractorParserOptions": {},
+ "compilerBabelOptions": {},
+ "fallbackLocales": {},
+ "format": "po",
+ "locales": [],
+ "extractors": ["babel"],
+ "orderBy": "messageId",
+ "pseudoLocale": "",
+ "rootDir": ".",
+ "runtimeConfigModule": ["@lingui/core", "i18n"],
+ "sourceLocale": ""
+}
+```
+
+## catalogs
+
+Default:
+
+```js
+[
+ {
+ path: "/locale/{locale}/messages",
+ include: [""],
+ exclude: ["**/node_modules/**"],
+ },
+];
+```
+
+Defines location of message catalogs and what files are included when [`extract`](./cli.md#extract) is scanning for messages.
+
+`path` shouldn't end with slash and it shouldn't include file extension which depends on [`format`](./catalog-formats.md).
+`{locale}` token is replaced by catalog locale.
+
+Patterns in `include` and `exclude` are passed to [minimatch](https://github.com/isaacs/minimatch).
+
+`path`, `include`, and `exclude` are interpreted from the current process CWD. If you want to make these paths relative to the configuration file, you can prefix them with a [`rootDir`](#rootdir) token. By default, [`rootDir`](#rootdir) represents the configuration file's location.
+
+`{name}` token in `path` is replaced with a catalog name. Source path must include `{name}` pattern as well and it works as a `*` glob pattern:
+
+```json
+{
+ "catalogs": [
+ {
+ "path": "/components/{name}/locale/{locale}",
+ "include": ["/components/{name}/"]
+ }
+ ]
+}
+```
+
+#### Examples
+
+Let's assume we use `locales: ["en", "cs"]` and `format: "po"` in all examples.
+
+#### All catalogs in one directory
+
+```json
+{
+ "catalogs": [
+ {
+ "path": "locales/{locale}"
+ }
+ ]
+}
+```
+
+```bash
+locales/
+├── en.po
+└── cs.po
+```
+
+#### Catalogs in separate directories
+
+```js
+{
+ catalogs: [
+ {
+ path: "locales/{locale}/messages",
+ },
+ ];
+}
+```
+
+```bash
+locales
+├── en/
+│ └── messages.po
+└── cs/
+ └── messages.po
+```
+
+#### Separate catalogs per component, placed inside component dir
+
+```json
+{
+ "catalogs": [
+ {
+ "path": "components/{name}/locale/{locale}",
+ "include": ["components/{name}/"]
+ }
+ ]
+}
+```
+
+```shell
+components/
+├── RegistrationForm/
+│ ├── locale/
+│ │ ├── en.po
+│ │ └── cs.po
+│ ├── RegistrationForm.test.js
+│ └── RegistrationForm.js
+└── LoginForm/
+ ├── locale/
+ │ ├── en.po
+ │ └── cs.po
+ ├── LoginForm.test.js
+ └── LoginForm.js
+```
+
+#### Separate catalogs per component, placed inside shared directory
+
+```json
+{
+ "catalogs": [
+ {
+ "path": "locale/{locale}/{name}",
+ "include": ["components/{name}/"]
+ }
+ ]
+}
+```
+
+```shell
+.
+├── locale/
+│ ├── en/
+│ │ ├── RegistrationForm.po
+│ │ └── LoginForm.po
+│ └── cs/
+│ ├── RegistrationForm.po
+│ └── LoginForm.po
+└── components/
+ ├── RegistrationForm/
+ │ ├── RegistrationForm.test.js
+ │ └── RegistrationForm.js
+ └── LoginForm/
+ ├── LoginForm.test.js
+ └── LoginForm.js
+```
+
+## catalogsMergePath
+
+Default: `""`
+
+Specify the path to merge translated catalogs into a single file per locale during compile.
+
+#### Example
+
+Let's assume we have [separate catalogs for `locales: ["en", "cs"]` per component placed inside shared directory](#separate-catalogs-per-component-placed-inside-shared-directory).
+
+Using `catalogsMergePath`, separate catalogs can be merged during [`compile`](./cli.md#compile):
+
+```diff
+{
+ "catalogs": [
+ {
+ "path": "/locale/{locale}/{name}",
+ "include": ["components/{name}/"]
+ }
+ ],
++ "catalogsMergePath": "locales/{locale}"
+}
+```
+
+```diff
+.
+ ├── locale/
+ │ ├── en/
+ │ │ ├── RegistrationForm.po
+- │ │ ├── RegistrationForm.js
+ │ │ ├── LoginForm.po
+- │ │ └── LoginForm.js
+ │ └── cs/
+ │ ├── RegistrationForm.po
+- │ ├── RegistrationForm.js
+ │ ├── LoginForm.po
+- │ └── LoginForm.js
++ ├── locales/
++ │ ├── en.js
++ │ └── cs.js
+ └── components/
+ ├── RegistrationForm/
+ │ ├── RegistrationForm.test.js
+ │ └── RegistrationForm.js
+ └── LoginForm/
+ ├── LoginForm.test.js
+ └── LoginForm.js
+```
+
+## compileNamespace
+
+Default: `cjs`
+
+Specify namespace for exporting compiled messages. See [`compile`](./cli.md#compile) command.
+
+#### cjs
+
+Use CommonJS exports:
+
+```js
+/* eslint-disable */module.exports={messages: {"..."}}
+```
+
+#### es
+
+Use ES6 named export:
+
+```js
+/* eslint-disable */export const messages = {"..."}
+```
+
+#### ts
+
+Use ES6 named export + .ts file with an additional `{compiledFile}.d.ts` file:
+
+```js
+/* eslint-disable */export const messages = {"..."}
+```
+
+```js
+import { Messages } from '@lingui/core';
+declare const messages: Messages;
+export { messages };
+```
+
+#### json
+
+```json
+{"messages": {"..."}}
+```
+
+#### (window\|global).(.\*)
+
+Assign compiled messages to `window` or `global` object. Specify an identifier after `window` or `global` to which the catalog is assigned, e.g. `window.i18n`.
+
+For example, setting [`compileNamespace`](#compilenamespace) to `window.i18n` creates file similar to this:
+
+```js
+/* eslint-disable */window.i18n={messages: {"..."}}
+```
+
+## extractorParserOptions
+
+Default: `{}`
+
+Specify additional options used to parse source files when extracting messages.
+
+```json
+{
+ "extractorParserOptions": {
+ "tsExperimentalDecorators": false,
+ "flow": false
+ }
+}
+```
+
+#### tsExperimentalDecorators
+
+Default: `false`
+
+By default, standard decorators (Stage3) are applied to TS files. Enable this if you want to use TypeScript's experimental decorators.
+
+#### flow
+
+Default: `false`
+
+Lingui does not ship with [Flow](https://flow.org/) typing. However, you can use Lingui in projects written in Flow. Enable this option to tell the extractor that your sources use Flow syntax.
+
+## compilerBabelOptions
+
+Default:
+
+```json
+{
+ "minified": true,
+ "jsescOption": {
+ "minimal": true
+ }
+}
+```
+
+Specify extra babel options used to generate files when messages are being compiled. We use internally `@babel/generator` that accepts some configuration for generating code with/out ASCII characters. These are all the options available: [jsesc](https://github.com/mathiasbynens/jsesc).
+
+```json
+{
+ "compilerBabelOptions": {
+ "jsescOption": {
+ "minimal": false
+ }
+ }
+}
+```
+
+This example configuration will compile with escaped ASCII characters ([jsesc#minimal](https://github.com/mathiasbynens/jsesc#minimal)).
+
+## fallbackLocales
+
+Default: `{}`
+
+`fallbackLocales` by default is using [CLDR Parent Locales](https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/parentLocales.json), unless you disable it with a `false`:
+
+```json
+{
+ "fallbackLocales": false
+}
+```
+
+`fallbackLocales` object lets us configure fallback locales to each locale instance.
+
+```json
+{
+ "fallbackLocales": {
+ "en-US": ["en-GB", "en"],
+ "es-MX": "es"
+ }
+}
+```
+
+On this example if any translation isn't found on `en-US` then will search on `en-GB`, after that if not found we'll search in `en`.
+
+Also, we can configure a default one for everything:
+
+```json
+{
+ "fallbackLocales": {
+ "en-US": ["en-GB", "en"],
+ "es-MX": "es",
+ "default": "en"
+ }
+}
+```
+
+Translations from `fallbackLocales` is used when translation for given locale is missing.
+
+If `fallbackLocales` is `false` default message or message ID is used instead.
+
+## format
+
+Default: `po`
+
+Message catalog format. The `po` formatter is used by default. Other formatters are available as separate packages.
+
+```js title="lingui.config.{js,ts}"
+import { formatter } from "@lingui/format-po"
+
+export default {
+ [...]
+ format: formatter({ lineNumbers: false })
+}
+```
+
+Read more about available formatters in [Catalog Formats](./catalog-formats.md) or create your own [Custom Formatter](../guides/custom-formatter.md).
+
+## locales
+
+Default: `[]`
+
+Locale tags which are used in the project. [`extract`](./cli.md#extract) and [`compile`](./cli.md#compile) writes one catalog for each locale. Each locale should be a valid [BCP-47 code](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).
+
+## orderBy
+
+Default: `message`
+
+Order of messages in catalog:
+
+#### message
+
+Sort by source message.
+
+#### messageId
+
+Sort by the message ID, `js-lingui-id` will be used if no custom id provided.
+
+#### origin
+
+Sort by message origin (e.g. `App.js:3`)
+
+## pseudoLocale
+
+Default: `""`
+
+Locale used for pseudolocalization. For example when you set `pseudoLocale: "en"` then all messages in `en` catalog will be pseudo localized. The locale has to be included in `locales` config.
+
+## rootDir
+
+Default: The root of the directory containing your Lingui config file or the `package.json`.
+
+The root directory that Lingui CLI should scan when extracting messages from source files.
+
+Note that using `` as a string token in any other path-based config settings will refer back to this value.
+
+## runtimeConfigModule
+
+Default: `["@lingui/core", "i18n"]`
+
+Module path with exported i18n object. The first value in array is module path, the second is the import identifier. This value is used in macros, which need to reference the global `i18n` object.
+
+You only need to set this value if you use custom object created using [`setupI18n`](./core.md#setupi18n):
+
+```jsx
+// If you import `i18n` object from custom module like this:
+import { i18n } from "./custom-i18n-config";
+
+// ... then add following line to Lingui configuration:
+// "runtimeConfigModule": ["./custom-i18n-config", "i18n"]
+```
+
+You may use a different named export:
+
+```jsx
+import { myI18n } from "./custom-i18n-config";
+// "runtimeConfigModule": ["./custom-i18n-config", "myI18n"]
+```
+
+In some advanced cases you may also need to change the module from which [Trans](./macro.mdx#trans) is imported. To do that, pass an object to `runtimeConfigModule`:
+
+```jsx
+// If you import `i18n` object from custom module like this:
+import { Trans, i18n } from "./custom-config";
+
+// ... then add following line to Lingui configuration:
+// "runtimeConfigModule": {
+// i18n: ["./custom-config", "i18n"],
+// Trans: ["./custom-config", "Trans"]
+// }
+```
+
+## sourceLocale
+
+Default: `''`
+
+Locale of message IDs, which is used in source files. Catalog for `sourceLocale` doesn't require translated messages, because message IDs are used by default. However, it's still possible to override message ID by providing custom translation.
+
+The difference between [`fallbackLocales`](#fallbacklocales) and `sourceLocale` is that [`fallbackLocales`](#fallbacklocales) is used in translation, while `sourceLocale` is used for the message ID.
+
+## extractors
+
+Default: `[babel]`
+
+Extractors it's the way to customize which extractor you want for your codebase.
+
+```js
+{
+ "extractors": [
+ myCustomExtractor,
+ ]
+}
+```
+
+Visit [Advanced: Custom Extractor](../guides/custom-extractor.md) to learn how to create a custom extractor.
diff --git a/website/versioned_docs/version-4.x/ref/core.md b/website/versioned_docs/version-4.x/ref/core.md
new file mode 100644
index 000000000..654c27530
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/core.md
@@ -0,0 +1,433 @@
+---
+title: Lingui Core package
+description: The package provides the main i18n object, which manages message catalogs, active locale, and message translation and formatting
+---
+
+# The core i18n functionality
+
+`@lingui/core` package provides the main i18n object which manages message catalogs, active locale as well as translation and formatting of messages.
+
+## Installation
+
+```bash npm2yarn
+npm install --save @lingui/core
+```
+
+## Overview
+
+`@lingui/core` package exports the global instance of `i18n` object. Simply import it and use it:
+
+```ts
+import { i18n } from "@lingui/core";
+
+/**
+ * Load messages for requested locale and activate it.
+ * This function isn't part of the LinguiJS library because there are
+ * many ways how to load messages — from REST API, from file, from cache, etc.
+ */
+async function activate(locale: string) {
+ const { messages } = await import(`${locale}/messages.js`);
+ i18n.loadAndActivate({ locale, messages });
+}
+
+activate("cs");
+
+// returns the Czech translation of "Hello World"
+const translation = i18n._("Hello World");
+```
+
+If you don't want to use the global `i18n` instance and you want to setup your own, you can use [`setupI18n`](#setupi18n) method. You also need to set [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) for macros to work correctly:
+
+```ts
+// If you import `i18n` object from custom module like this:
+import { i18n } from "./custom-i18n-config";
+
+// ... then add following line to your Lingui configuration:
+// "runtimeConfigModule": ["./custom-i18n-config", "i18n"]
+```
+
+## Reference
+
+### Class `i18n()` {#i18n}
+
+### `i18n.loadAndActivate(options)` {#i18n.loadAndActivate}
+
+`options` is an object with following properties:
+
+- `locale`: initial active locale
+- `locales`: list of alternative locales (BCP 47 language tags) which are used for number and date formatting
+- `messages`: **compiled** message catalog
+
+Sets (overwrites) the catalog for given locale and activates the locale.
+
+```ts
+import { i18n } from "@lingui/core";
+
+const { messages } = await import(`${locale}/messages.js`);
+i18n.loadAndActivate({ locale, messages });
+```
+
+### `i18n.load(allMessages: AllMessages)` {#i18n.load(allMessages)}
+
+### `i18n.load(locale: string, messages: Messages)` {#i18n.load}
+
+Load messages for given locale or load multiple message catalogs at once.
+
+When some messages for the provided locale are already loaded, calling `i18n.load` will merge the new messages with the existing ones using `Object.assign`.
+
+```ts
+import { i18n } from "@lingui/core";
+
+const messagesEn = {
+ Hello: "Hello",
+ "Good bye": "Good bye",
+
+ // Just an example how catalog looks internally.
+ // Formatting of string messages works in development only.
+ // See note below.
+ "My name is {name}": "My name is {name}",
+};
+
+const messagesCs = {
+ Hello: "Ahoj",
+ "Good bye": "Nashledanou",
+ "My name is {name}": "Jmenuji se {name}",
+};
+
+i18n.load({
+ en: messagesEn,
+ cs: messagesCs,
+});
+
+// This is the same as loading message catalogs separately per language:
+// i18n.load('en', messagesEn)
+// i18n.load('cs', messagesCs)
+```
+
+:::tip
+
+Don't write catalogs manually.
+
+Code above contains an example of message catalogs. In real applications, messages are loaded from external message catalogs generated by [`compile`](./cli.md#compile) command.
+
+Formatting of messages as strings (e.g: `"My name is {name}"`) works in development only, when messages are parsed on the fly. In production, however, messages must be compiled using [`compile`](./cli.md#compile) command.
+
+The same example would in real application look like this:
+
+```ts
+import { i18n } from "@lingui/core"
+
+// File generated by `lingui compile`
+import { messages: messagesEn } from "./locale/en/messages.js"
+
+i18n.load('en', messagesEn)
+```
+
+:::
+
+### `i18n.activate(locale[, locales])` {#i18n.activate}
+
+Activate a locale and locales. From now on, calling `i18n._` will return messages in given locale.
+
+```ts
+import { i18n } from "@lingui/core";
+
+i18n.activate("en");
+i18n._("Hello"); // Return "Hello" in English
+
+i18n.activate("cs");
+i18n._("Hello"); // Return "Hello" in Czech
+```
+
+### `i18n._(messageId[, values[, options]])` {#i18n.\_}
+
+The core method for translating and formatting messages.
+
+`messageId` is a unique message ID which identifies message in catalog.
+
+`values` is an object of variables used in translated message.
+
+`options.message` is the default translation (optional). This is mostly used when application doesn't use message IDs in natural language (e.g.: `msg.id` or `Component.title`).
+
+```ts
+import { i18n } from "@lingui/core";
+
+// Simple message
+i18n._("Hello");
+
+// Message with variables
+i18n._("My name is {name}", { name: "Tom" });
+
+// Message with custom messageId
+i18n._("msg.id", { name: "Tom" }, { message: "My name is {name}" });
+```
+
+### `i18n._(messageDescriptor)`
+
+`messageDescriptor` is an object of message parameters.
+
+```ts
+import { i18n } from "@lingui/core";
+
+// Simple message
+i18n._({ id: "Hello" });
+
+// Simple message using custom ID
+i18n._({ id: "msg.hello", message: "Hello" });
+
+// Message with variable
+i18n._({ id: "My name is {name}", values: { name: "Tom" } });
+
+// Message with comment, custom ID and variable
+i18n._({
+ id: "msg.name",
+ message: "My name is {name}",
+ comment: "Message showing the passed in name",
+ values: { name: "Tom" },
+});
+```
+
+### `i18n.t(...)` {#i18n.t}
+
+Alias for [`i18n._`](#i18n._)
+
+```ts
+import { i18n } from "@lingui/core";
+
+i18n.t({
+ id: "msg.name",
+ message: "My name is {name}",
+ comment: "Message showing the passed in name",
+ values: { name: "Tom" },
+});
+
+i18n.t("msg.id", { name: "Tom" }, { message: "My name is {name}" });
+```
+
+### `i18n.date(value: string | Date[, format: Intl.DateTimeFormatOptions])` {#i18n.date}
+
+> **Returns**: Formatted date string Format a date using the conventional format for the active language.
+
+`date` is a Date object to be formatted. When `date` is a string, the Date object is created by `new Date(date)`.
+
+`format` is an object passed to the `options` argument of the [Intl.DateTimeFormat constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) (optional).
+
+```ts
+import { i18n } from "@lingui/core";
+
+const d = new Date("2021-07-23T16:23:00");
+
+i18n.activate("en");
+i18n.date(d);
+// Returns "7/23/2021"
+
+i18n.date(d, { timeStyle: "medium" });
+// Returns "4:23:00 PM"
+
+i18n.date(d, { dateStyle: "medium", timeStyle: "medium" });
+// Returns "Jul 23, 2021, 4:23:00 PM"
+
+i18n.activate("cs");
+i18n.date(d);
+// Returns "23. 7. 2021"
+```
+
+### `i18n.number(value: number[, format: Intl.NumberFormatOptions])` {#i18n.number}
+
+> **Returns**: Formatted number string
+
+Format a number using the conventional format for the active language.
+
+`number` is a number to be formatted.
+
+`format` is an object passed to the `options` argument of the [Intl.NumberFormat constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat) (optional).
+
+```ts
+import { i18n } from "@lingui/core";
+
+i18n.activate("en");
+i18n.number(12345.678);
+// Returns "12,345.678"
+
+i18n.number(12345.678, { style: "currency", currency: "USD" });
+// Returns "$12,345.68"
+
+i18n.activate("cs");
+i18n.number(12345.678);
+// Returns "12 345,678"
+
+i18n.number(12345.678, { style: "currency", currency: "CZK" });
+// Returns "12 345,68 Kč"
+```
+
+### `setupI18n([options])` {#setupi18n}
+
+> **Returns**: Instance of I18n
+
+Initialize and return a new I18n instance. Usually you want to call it just once and then use returned `i18n` object across whole codebase.
+
+:::note
+You don't need to setup i18n instance
+
+In most cases you can use the global `i18n` object exported from `@lingui/core` directly.
+
+However, if you do need to setup your own `i18n` instance, remember to also set [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) work macros to work properly:
+
+```ts
+// If you import `i18n` object from custom module like this:
+import { i18n } from "./custom-i18n-config";
+
+// ... then add following line to your Lingui configuration:
+// "runtimeConfigModule": ["./custom-i18n-config", "i18n"]
+```
+
+:::
+
+```ts
+import { setupI18n } from "@lingui/core";
+
+const i18n = setupI18n();
+```
+
+The factory function accepts one optional parameter, `options`:
+
+### `options.locale`
+
+Initial active locale.
+
+```tsx
+import { setupI18n } from "@lingui/core";
+
+const i18n = setupI18n({ locale: "en" });
+
+// This is a shortcut for:
+// const i18n = setupI18n()
+// i18n.activate("en")
+```
+
+### `options.locales`
+
+List of alternative locales (BCP 47 language tags) which are used for number and date formatting (some countries use more than one number/date format). If not set, active locale is used instead.
+
+```tsx
+import { setupI18n } from "@lingui/core";
+
+const i18n = setupI18n({
+ locale: "ar",
+ locales: ["en-UK", "ar-AS"],
+});
+
+// This is a shortcut for:
+// const i18n = setupI18n()
+// i18n.activate("ar", ["en-UK", "ar-AS"])
+```
+
+### `options.messages`
+
+Initial [`Messages`](#messages).
+
+```tsx
+import { setupI18n } from "@lingui/core";
+
+const messages = {
+ en: require("./locale/en/messages").messages, // your path to compiled messages here
+ cs: require("./locale/cs/messages").messages, // your path to compiled messages here
+};
+const i18n = setupI18n({ messages });
+
+// This is a shortcut for:
+// const i18n = setupI18n()
+// i18n.load(messages)
+```
+
+### `options.missing`
+
+Custom message to be returned when translation is missing. This is useful for debugging:
+
+```tsx
+import { setupI18n } from "@lingui/core";
+
+const i18n = setupI18n({ missing: "🚨" });
+i18n._("missing translation") === "🚨";
+```
+
+This might be also a function which is called with active locale and message ID:
+
+```tsx
+import { setupI18n } from "@lingui/core";
+
+function missing(locale, id) {
+ alert(`Translation in ${locale} for ${id} is missing!`);
+ return id;
+}
+
+const i18n = setupI18n({ missing });
+i18n._("missing translation"); // raises alert
+```
+
+### Catalogs
+
+Type of `catalogs` parameters in [`I18n.load`](#i18n.load) method:
+
+```ts
+type Catalogs = { [locale: string]: Catalog };
+
+// Example:
+const catalogs: Catalogs = {
+ en: {
+ messages: {
+ Hello: "Hello",
+ "Good bye": "Good bye",
+ },
+ },
+ cs: {
+ messages: {
+ Hello: "Ahoj",
+ "Good bye": "Nashledanou",
+ },
+ },
+};
+```
+
+### Catalog
+
+Message catalog contains messages and language data (plurals). This object is usually generated in CLI:
+
+```ts
+type Catalog = {
+ languageData: {
+ plurals: Function;
+ };
+ messages: Messages;
+};
+```
+
+### Messages
+
+Type of messages in [`Catalogs`](#catalogs). It's a mapping of a **messageId** to a translation in given language. This may be a function if messages are compiled.
+
+```ts
+type Messages = { [messageId: string]: string | Function };
+
+// Example
+const messagesEn: Messages = {
+ Hello: "Hello",
+ "Good bye": "Good bye",
+};
+```
+
+## Events
+
+### `change`
+
+Triggered **after** locale is changed or new catalog is loaded. There are no arguments.
+
+### `missing`
+
+Triggered when a translation is requested with [`i18n._`](./core.md#i18n._) that does not exist in the active locale's messages. Information about the locale and message are available from the event.
+
+```ts
+i18n.on('missing', (event) => {
+ alert(`alert(`Translation in ${event.locale} for ${event.id} is missing!`)`)
+})
+```
diff --git a/website/versioned_docs/version-4.x/ref/eslint-plugin.md b/website/versioned_docs/version-4.x/ref/eslint-plugin.md
new file mode 100644
index 000000000..f7482db93
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/eslint-plugin.md
@@ -0,0 +1,111 @@
+---
+title: Lingui ESLint Plugin
+description: Lingui ESLint Plugin helps you find and prevent common i18n mistakes in your code
+---
+
+# ESLint Plugin
+
+Lingui provides an ESLint plugin to help you find common Lingui usage errors in your code.
+
+[](https://www.npmjs.com/package/eslint-plugin-lingui)
+[](https://www.npmjs.com/package/eslint-plugin-lingui)
+
+## Installation
+
+Install [ESLint](http://eslint.org):
+
+```bash npm2yarn
+npm install --save-dev eslint
+```
+
+Next, install `eslint-plugin-lingui`:
+
+```bash npm2yarn
+npm install --save-dev eslint-plugin-lingui
+```
+
+:::info
+If you have installed ESLint globally (using the `-g` flag), you must also install `eslint-plugin-lingui` globally.
+:::
+
+## Usage
+
+### Flat Config (`eslint.config.js`) {#flat-config}
+
+Version 8 of ESLint introduced a new configuration format called [Flat Config](https://eslint.org/docs/latest/use/configure/configuration-files). Flat config files represent plugins and parsers as JavaScript objects.
+
+#### Recommended Setup
+
+To enable all the recommended rules for the plugin, add the following config:
+
+```js
+import pluginLingui from "eslint-plugin-lingui";
+
+export default [
+ pluginLingui.configs["flat/recommended"],
+ // Any other config...
+];
+```
+
+#### Custom Setup
+
+Alternatively, you can load the plugin and configure only the rules you want to use:
+
+```js
+import pluginLingui from "eslint-plugin-lingui";
+
+export default [
+ {
+ plugins: {
+ lingui: pluginLingui,
+ },
+ rules: {
+ "lingui/t-call-in-function": "error",
+ },
+ },
+ // Any other config...
+];
+```
+
+### Legacy Config (`.eslintrc`) {#legacy-eslintrc}
+
+The legacy configuration format has been deprecated by ESLint, but it's still supported. If you're using the legacy format, you can use the following configuration.
+
+#### Recommended Setup
+
+To enable all the recommended rules for the plugin, add `plugin:lingui/recommended` to the `extends` section:
+
+```json
+{
+ "extends": ["plugin:lingui/recommended"]
+}
+```
+
+#### Custom Setup
+
+Alternatively, add `lingui` to the `plugins` section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
+
+```json
+{
+ "plugins": ["lingui"]
+}
+```
+
+In the rules section, configure the rules you want to use:
+
+```json
+{
+ "rules": {
+ "lingui/no-unlocalized-strings": 2,
+ "lingui/t-call-in-function": 2,
+ "lingui/no-single-variables-to-translate": 2,
+ "lingui/no-expression-in-message": 2,
+ "lingui/no-single-tag-to-translate": 2,
+ "lingui/no-trans-inside-trans": 2
+ }
+}
+```
+
+:::tip
+See the [official repository](https://github.com/lingui/eslint-plugin) for more information about the rules.
+:::
diff --git a/website/versioned_docs/version-4.x/ref/extractor-vue.md b/website/versioned_docs/version-4.x/ref/extractor-vue.md
new file mode 100644
index 000000000..0d58aff62
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/extractor-vue.md
@@ -0,0 +1,39 @@
+# Vue.js Extractor
+
+The `@lingui/extractor-vue` package provides a custom extractor that handles Vue.js files.
+
+## Installation
+
+```bash npm2yarn
+npm install --save-dev @lingui/extractor-vue
+```
+
+## Usage
+
+It is required that you use JavaScript or TypeScript for your Lingui configuration.
+
+```js title="lingui.config.{js,ts}"
+import { vueExtractor } from "@lingui/extractor-vue";
+import babel from "@lingui/cli/api/extractors/babel";
+
+/** @type {import('@lingui/conf').LinguiConfig} */
+const linguiConfig = {
+ locales: ["en", "nb"],
+ sourceLocale: "en",
+ catalogs: [
+ {
+ path: "/src/{locale}",
+ include: ["/src"],
+ },
+ ],
+ extractors: [babel, vueExtractor],
+};
+
+export default linguiConfig;
+```
+
+## Further reading
+
+- [Message Extraction](../guides/message-extraction.md)
+- [Custom Extractor](../guides/custom-extractor.md)
+- [GitHub Repository](https://github.com/lingui/js-lingui/tree/main/packages/extractor-vue)
diff --git a/website/versioned_docs/version-4.x/ref/loader.md b/website/versioned_docs/version-4.x/ref/loader.md
new file mode 100644
index 000000000..a60e2a4a2
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/loader.md
@@ -0,0 +1,36 @@
+# Webpack Loader
+
+The Webpack loader compiles catalogs on the fly. It's an alternative to the [`lingui compile`](/ref/cli#compile) command: the loader enables you to `import` `.po` files directly, instead of running `lingui compile` and `import`ing the resulting JavaScript (or TypeScript) files.
+
+## Installation
+
+Install `@lingui/loader` as a development dependency:
+
+```bash npm2yarn
+npm install --save-dev @lingui/loader
+```
+
+## Usage
+
+Simply prepend `@lingui/loader!` in front of path to message catalog you want to import. Here's an example of dynamic import:
+
+The extension is mandatory.
+
+```ts
+export async function dynamicActivate(locale: string) {
+ const { messages } = await import(`@lingui/loader!./locales/${locale}/messages.po`);
+ i18n.load(locale, messages);
+ i18n.activate(locale);
+}
+```
+
+:::note
+Catalogs with the `.json` extension are treated differently by Webpack. They load as ES module with default export, so your import should look like this:
+
+```ts
+const { messages } = (await import(`@lingui/loader!./locales/${locale}/messages.json`)).default;
+```
+
+:::
+
+See the [guide about dynamic loading catalogs](../guides/dynamic-loading-catalogs.md) for more info.
diff --git a/website/versioned_docs/version-4.x/ref/locale-detector.md b/website/versioned_docs/version-4.x/ref/locale-detector.md
new file mode 100644
index 000000000..35ef0e0d0
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/locale-detector.md
@@ -0,0 +1,61 @@
+---
+title: Locale Detection
+description: Detect the user's locale with the `@lingui/detect-locale` package
+---
+
+# Locale Detection
+
+`@lingui/detect-locale` is little package _just (1 kB Gzip)_ with some helper functions that will help you detect the locale of the user:
+
+## Installation
+
+Install `@lingui/detect-locale` as a dependency:
+
+```bash npm2yarn
+npm install --save @lingui/detect-locale
+```
+
+## Usage
+
+`@lingui/detect-locale:` exports multiple methods:
+
+- `detect` - Will return the first occurrence of detectors
+- `multipleDetect` - Will return an array with all the locales detected by each detector
+
+and some helpers:
+
+- `fromCookie(key: string)` - Accepts a key as param will recover from navigator cookies the value
+- `fromHtmlTag(tag: string)` - Will find on HtmlDocument the attribute passed in params (normally it's used lang or xml:lang)
+- `fromNavigator()` - Recovers the navigator language, it's also compatible with old browsers like IE11
+- `fromPath(localePathIndex: number)` - Splits the location.pathname in an array so you have to specify the index of the array where's locale is set
+- `fromStorage(key: string, { useSessionStorage: boolean }` - Will search on localStorage by default the item that has that key, if **useSessionStorage** is passed, will search on sessionStorage
+- `fromSubdomain(localeSubdomainIndex: number)` - Like fromPath, splits the location.href on segments you must specify the index of that segment
+- `fromUrl(parameter: string)` - Uses a query-string parser to recover the correct parameter
+
+Practically all detectors accepts a custom document, location, or window object as param, it's useful when testing or using some server-side strategy.
+
+### Usage with `detect`
+
+```jsx
+import { detect, fromUrl, fromStorage, fromNavigator } from "@lingui/detect-locale";
+
+// can be a function with custom logic or just a string, `detect` method will handle it
+const DEFAULT_FALLBACK = () => "en";
+
+const result = detect(fromUrl("lang"), fromStorage("lang"), fromNavigator(), DEFAULT_FALLBACK);
+
+console.log(result); // "en"
+```
+
+### Usage with `multipleDetect`
+
+```jsx
+import { multipleDetect, fromUrl, fromStorage, fromNavigator } from "@lingui/detect-locale";
+
+// can be a function with custom logic or just a string, `detect` method will handle it
+const DEFAULT_FALLBACK = () => "en";
+
+const result = multipleDetect(fromUrl("lang"), fromStorage("lang"), fromNavigator(), DEFAULT_FALLBACK);
+
+console.log(result); // ["en", "es"]
+```
diff --git a/website/versioned_docs/version-4.x/ref/macro.mdx b/website/versioned_docs/version-4.x/ref/macro.mdx
new file mode 100644
index 000000000..2fb3496a3
--- /dev/null
+++ b/website/versioned_docs/version-4.x/ref/macro.mdx
@@ -0,0 +1,1027 @@
+---
+title: Lingui Macro package
+description: Transform JavaScript objects and JSX elements into ICU MessageFormat messages
+---
+
+# Macros
+
+The `@lingui/macro` package transforms JavaScript objects and JSX elements into ICU MessageFormat messages. You can use Babel macros or SWC plugin for this transformation.
+
+## Installation
+
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
+
+
+
+Babel macros require [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros) to work. If you use a framework (for example GatsbyJS, Create React App > 2.0) you might already have macros enabled. Otherwise, install it as any other Babel plugin:
+
+- Install `babel-plugin-macros` as a dev dependency and `@lingui/macro` as dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev babel-plugin-macros
+ npm install --save @lingui/macro
+ ```
+
+- Add `macros` to the top of plugins section in your Babel config:
+
+ ```json
+ {
+ "plugins": ["macros"]
+ }
+ ```
+
+ When using any preset, first check if it includes the `macros` plugin. These presets already includes the `macros` plugin: `react-scripts`.
+
+
+
+
+- Install `@lingui/swc-plugin` as a dev dependency and `@lingui/macro` as dependency:
+
+ ```bash npm2yarn
+ npm install --save-dev @lingui/swc-plugin
+ npm install --save @lingui/macro
+ ```
+
+- [Add necessary configurations](./swc-plugin.md#usage).
+
+
+
+
+:::note
+It's recommended to install `@lingui/macro` package as a production dependency rather than development one to avoid `import/no-extraneous-dependencies` errors in ESLint.
+:::
+
+:::tip
+Don't miss the [Lingui ESLint Plugin](./eslint-plugin.md) which can help you find and prevent common l10n mistakes in your code.
+:::
+
+## Overview
+
+The advantages of using macros are:
+
+- You don't need to learn ICU MessageFormat syntax. You always use familiar JS and JSX code.
+- Components and functions are type checked.
+- Short ID generated for your messages.
+- Additional validation of plural rules is performed during transformation.
+- Non-essential data are removed from the production build (e.g. comments and default messages) to shave a few bytes.
+
+**JSX macros** are transformed to [`Trans`](./react.md#trans) component from [`@lingui/react`](./react.md):
+
+```jsx
+import { Trans } from "@lingui/macro";
+Attachment {name} saved;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+```
+
+**JS macros** (i.e. macros that looks like a simple JavaScript functions) are transformed into [`i18n._`](./core.md#i18n._) call.
+
+```jsx
+import { t } from "@lingui/macro";
+t`Attachment ${name} saved`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+
+i18n._(
+ /*i18n*/ {
+ id: "nwR43V",
+ message: "Attachment {name} saved",
+ values: { name },
+ }
+);
+```
+
+:::note
+By default, the `i18n` object is imported from `@lingui/core`. If you use a custom instance of `i18n` object, you need to set [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) or pass a custom instance to [`t`](./macro.mdx#t).
+:::
+
+The only exception is [`defineMessage`](./macro.mdx#definemessage) which is transformed into message descriptor. In other words, the message isn't translated directly and can be used anytime later:
+
+```jsx
+import { i18n } from "@lingui/core"
+import { defineMessage } from "@lingui/macro"
+
+// define message
+const message = defineMessage({ message: `Attachment ${name} saved` })
+
+// translate it
+i18n._(message)
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core"
+
+// define message
+const message = /*i18n*/{ id: "nwR43V", message: "Attachment {name} saved", values: { name }})
+
+// translate it
+i18n._(message)
+```
+
+### Examples of JS macros
+
+```js
+t`Refresh inbox`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+i18n._(
+ /*i18n*/ {
+ id: "EsCV2T",
+ message: "Refresh inbox",
+ }
+);
+```
+
+```js
+t`Attachment ${name} saved`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+i18n._(
+ /*i18n*/ {
+ id: "nwR43V",
+ message: "Attachment {name} saved",
+ values: { name },
+ }
+);
+```
+
+```js
+t(customI18n)`Refresh inbox`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+customI18n._(
+ /*i18n*/ {
+ id: "EsCV2T",
+ message: "Refresh inbox",
+ }
+);
+```
+
+```js
+t(customI18n)`Attachment ${name} saved`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+customI18n._(
+ /*i18n*/ {
+ id: "nwR43V",
+ message: "Attachment {name} saved",
+ values: { name },
+ }
+);
+```
+
+```js
+plural(count, {
+ one: "# Message",
+ other: "# Messages",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+i18n._(
+ /*i18n*/ {
+ id: "4w2nim",
+ message: "{count, plural, one {# Message} other {# Messages}}",
+ values: { count },
+ }
+);
+```
+
+```js
+t({
+ id: "msg.refresh",
+ message: "Refresh inbox",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+i18n._(
+ /*i18n*/ {
+ id: "msg.refresh",
+ message: "Refresh inbox",
+ }
+);
+```
+
+```js
+t(customI18n)({
+ id: "msg.refresh",
+ message: "Refresh inbox",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+customI18n._(
+ /*i18n*/ {
+ id: "msg.refresh",
+ message: "Refresh inbox",
+ }
+);
+```
+
+```js
+const msg = defineMessage`Refresh inbox`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const msg = /*i18n*/ {
+ id: "EsCV2T",
+ message: "Refresh inbox",
+};
+```
+
+```js
+const msg = defineMessage({
+ id: "msg.refresh",
+ message: "Refresh inbox",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const msg = /*i18n*/ {
+ id: "msg.refresh",
+ message: "Refresh inbox",
+};
+```
+
+```js
+const msg = defineMessage({
+ id: "msg.plural",
+ message: plural(count, {
+ one: "# Message",
+ other: "# Messages",
+ }),
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const msg = /*i18n*/ {
+ id: "msg.plural",
+ message: "{count, plural, one {# Message} other {# Messages}}",
+ values: { count },
+};
+```
+
+### Examples of JSX macros
+
+```jsx
+Attachment {name} saved
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+
+```
+
+```jsx
+
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+
+```
+
+```jsx
+
+ Refresh inbox
+
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+
+```
+
+## JS macros
+
+These macros can be used in any context (e.g. outside JSX). All JS macros are transformed into a _Message Descriptor_ wrapped inside of [`i18n._`](./core.md#i18n._) call.
+
+:::note
+By default, the `i18n` object is imported from `@lingui/core`. If you use a custom instance of `i18n` object, you need to set [`runtimeConfigModule`](./conf.md#runtimeconfigmodule) or pass a custom instance to [`t`](./macro.mdx#t).
+:::
+
+_Message Descriptor_ is an object with message ID, default message and other parameters. [`i18n._`](./core.md#i18n._) accepts message descriptors and performs translation and formatting:
+
+```ts
+type MessageDescriptor = {
+ id: string;
+ message?: string;
+ values?: Record;
+ comment?: string;
+};
+```
+
+`id` is the message ID and the only required parameter. `id` and `message` are extracted to the message catalog. Only `id` and `values` are used at runtime, all other attributes are removed from production code for size optimization.
+
+You don't need to provide your ID manually. Macro will automatically create a short ID from your message.
+
+:::info Note
+i18n comment
+
+In the examples below you might notice `/*i18n*/` comment in macro output. This comment tells the extract plugin that following object should be collected to message catalog.
+:::
+
+### `t`
+
+The most common macro for messages. It transforms tagged template literal into message in ICU MessageFormat:
+
+```js
+import { t } from "@lingui/macro";
+const message = t`Hello World`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "mY42CM",
+ message: "Hello World",
+ }
+);
+```
+
+Message variables are supported:
+
+```js
+import { t } from "@lingui/macro";
+const message = t`My name is ${name}`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "mVmaLu",
+ message: "My name is {name}",
+ values: { name },
+ }
+);
+```
+
+In fact, any expression can be used inside template literal. However, only simple variables are referenced by name in a transformed message. All other expressions are referenced by numeric index:
+
+```js
+import { t } from "@lingui/macro";
+const message = t`Today is ${new Date()}`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+
+const message = i18n._(
+ /*i18n*/ {
+ id: "2aJT27",
+ message: "Today is {0}",
+ values: { 0: new Date() },
+ }
+);
+```
+
+Optionally, a custom `i18n` instance can be passed that can be used instead of the global instance:
+
+```jsx
+import { t } from "@lingui/macro";
+import { i18nCustom } from "./lingui";
+const message = t(i18nCustom)`Hello World`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18nCustom } from "./lingui";
+
+import { i18n } from "@lingui/core";
+const message = i18nCustom._(
+ /*i18n*/ {
+ id: "mY42CM",
+ message: "Hello World",
+ }
+);
+```
+
+It's also possible to pass custom `id` and `comment` for translators by calling `t` macro with a message descriptor:
+
+```jsx
+import { t } from "@lingui/macro";
+const message = t({
+ id: "msg.hello",
+ comment: "Greetings at the homepage",
+ message: `Hello ${name}`,
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "msg.hello",
+ comment: "Greetings at the homepage",
+ message: "Hello {name}",
+ values: { name },
+ }
+);
+```
+
+In this case the `message` is used as a default message and it's transformed as if it were wrapped in `t` macro. `message` also accepts any other macros:
+
+```js
+import { t } from "@lingui/macro";
+const message = t({
+ id: "msg.plural",
+ message: plural(value, { one: "...", other: "..." }),
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "msg.plural",
+ message: "{value, plural, one {...} other {...}}",
+ values: { value },
+ }
+);
+```
+
+### `plural`
+
+```ts
+plural(value: string | number, options: Object)
+```
+
+`plural` macro is used for pluralization, e.g: messages which has different form based on counter. The first argument `value` determines the plural form. The second argument is an object with available plural forms. Plural form used in the source code depends on your source locale (e.g. English has only `one` and `other`).
+
+```js
+import { plural } from "@lingui/macro";
+const message = plural(count, {
+ one: "# Book",
+ other: "# Books",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "V/M0Vc",
+ message: "{count, plural, one {# Book} other {# Books}}",
+ values: { count },
+ }
+);
+```
+
+If you need to add variables to plural form, you can use template string literals. This time [`t`](./macro.mdx#t) macro isn't required as template strings are transformed automatically:
+
+```js
+import { plural } from "@lingui/macro";
+const message = plural(count, {
+ one: `${name} has # friend`,
+ other: `${name} has # friends`,
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "CvuUwE",
+ message: "{count, plural, one {{name} has # friend} other {{name} has # friends}}",
+ values: { count, name },
+ }
+);
+```
+
+Plurals can also be nested to form complex messages. Here's an example using two counters:
+
+```js
+import { plural } from "@lingui/macro";
+const message = plural(numBooks, {
+ one: plural(numArticles, {
+ one: `1 book and 1 article`,
+ other: `1 book and ${numArticles} articles`,
+ }),
+ other: plural(numArticles, {
+ one: `${numBooks} books and 1 article`,
+ other: `${numBooks} books and ${numArticles} articles`,
+ }),
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+// Generated message was wrapped for better readability
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "XnUh4j",
+ message: `{numBooks, plural,
+ one {{numArticles, plural,
+ one {1 book and 1 article}
+ other {1 book and {numArticles} articles}
+ }}
+ other {{numArticles, plural,
+ one {{numBooks} books and 1 article}
+ other {{numBooks} books and {numArticles} articles}
+ }}
+ }`,
+ values: { numBooks, numArticles },
+ }
+);
+```
+
+:::tip
+This is just an example how macros can be combined to create a complex messages. However, simple is better because in the end it's the translator who's gonna have to translate these long and complex strings.
+:::
+
+:::tip
+Use `plural` inside [`t`](#t) or [`defineMessage`](#definemessage) macro if you want to add custom `id`, `context` or `comment` for translators.
+
+```js
+const message = t({
+ id: "my.custom.id",
+ comment: "My Comment",
+ message: plural(count, {
+ one: "# Book",
+ other: "# Books",
+ }),
+});
+```
+
+:::
+
+### `selectOrdinal`
+
+```ts
+selectOrdinal(value: string | number, options: Object)
+```
+
+`selectOrdinal` macro is similar to [`plural`](#plural) but instead of using cardinal plural forms it uses ordinal forms:
+
+```js
+import { selectOrdinal } from "@lingui/macro";
+const message = selectOrdinal(count, {
+ one: "#st",
+ two: "#nd",
+ few: "#rd",
+ other: "#th",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+const message = i18n._(
+ /*i18n*/ {
+ id: "V8xI3w",
+ message: "{count, selectOrdinal, one {#st} two {#nd} few {#rd} other {#th}}",
+ values: { count },
+ }
+);
+```
+
+:::tip
+Use `selectOrdinal` inside [`t`](#t) or [`defineMessage`](#definemessage) macro if you want to add custom `id`, `context` or `comment` for translators.
+
+```js
+const message = t({
+ id: "my.custom.id",
+ comment: "My Comment",
+ message: selectOrdinal(count, {
+ one: "#st",
+ two: "#nd",
+ few: "#rd",
+ other: "#th",
+ }),
+});
+```
+
+:::
+
+### `select`
+
+```ts
+select(value: string | number, options: Object)
+```
+
+`select` macro works as a switch statement — it select one of the forms provided in `options` object which key matches exactly `value`:
+
+```js
+import { select } from "@lingui/macro";
+const message = select(gender, {
+ male: "he",
+ female: "she",
+ other: "they",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { i18n } from "@lingui/core";
+
+const message = i18n._(
+ /*i18n*/ {
+ id: "VRptzI",
+ message: "{gender, select, male {he} female {she} other {they}}",
+ values: { gender },
+ }
+);
+```
+
+:::tip
+Use `select` inside [`t`](#t) or [`defineMessage`](#definemessage) macro if you want to add custom `id`, `context` or `comment` for translators.
+
+```js
+const message = t({
+ id: "my.custom.id",
+ comment: "My Comment",
+ message: select(gender, {
+ male: "he",
+ female: "she",
+ other: "they",
+ }),
+});
+```
+
+:::
+
+### `defineMessage` alias: `msg` {#definemessage}
+
+`defineMessage` 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.md#lazy-translations).
+
+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.
+
+```ts
+import { defineMessage } from "@lingui/macro";
+const message = defineMessage`Hello World`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const message = /*i18n*/ {
+ id: "mY42CM",
+ message: "Hello World",
+};
+```
+
+You also can use shorter alias of `defineMessage` macro:
+
+```ts
+import { msg } from "@lingui/macro";
+const message = msg`Hello World`;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const message = /*i18n*/ {
+ id: "mY42CM",
+ message: "Hello World",
+};
+```
+
+`defineMessage` macro also supports `MacroMessageDescriptor` object as input. That can be used to provide additional information for message such as comment or context.
+
+```ts
+type MacroMessageDescriptor = {
+ id?: string;
+ message?: string;
+ comment?: string;
+ context?: string;
+};
+```
+
+Either `id` or `message` property is required. `id` is a custom message ID. If it isn't set, the `message` (and `context` if provided) are used for generating an ID.
+
+```js
+import { defineMessage } from "@lingui/macro";
+const message = defineMessage({
+ id: "Navigation / About",
+ message: "About us",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const message = /*i18n*/ {
+ id: "Navigation / About",
+ message: "About us",
+};
+```
+
+`message` is the default message. Any JS macro can be used here. Template string literals don't need to be tagged with [`t`](#t).
+
+```js
+import { defineMessage } from "@lingui/macro";
+
+const name = "Joe";
+
+const message = defineMessage({
+ comment: "Greetings on the welcome page",
+ message: `Welcome, ${name}!`,
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const message = /*i18n*/ {
+ id: "dgJjNB",
+ comment: "Greetings on the welcome page",
+ message: "Welcome, {name}",
+ values: {
+ name,
+ },
+};
+```
+
+`comment` is a comment for translators. It's extracted to the message catalog and it gives translators extra information about the message. It's removed from the production code:
+
+```js
+import { defineMessage } from "@lingui/macro";
+const message = defineMessage({
+ comment: "Link in navigation pointing to About page",
+ message: "About us",
+});
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const message = /*i18n*/ {
+ id: "+mNwru",
+ comment: "Link in navigation pointing to About page",
+ message: "About us",
+};
+```
+
+:::caution Note
+In production build, the macro is stripped of `message`, `comment` and `context` properties:
+
+```js
+import { defineMessage } from "@lingui/macro";
+const message = defineMessage({
+ id: "msg.navigation.about",
+ comment: "Link in navigation pointing to About page",
+ message: "About us",
+ context: "Context about the link",
+});
+
+// process.env.NODE_ENV === "production"
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+const message = /*i18n*/ {
+ id: "msg.navigation.about",
+};
+```
+
+`message` and `comment` are used in message catalogs only. `context` is used only for generating ID and is stripped from the output.
+
+:::
+
+## JSX Macros
+
+### `Trans`
+
+| Prop name | Type | Description |
+| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | string | Custom message ID |
+| `comment` | string | Comment for translators |
+| `context` | string | Allows to extract the same messages with different IDs. See [Context](../guides/explicit-vs-generated-ids.md#context) for more detail |
+
+[`Trans`](./react.md#trans) is the basic macro for static messages, messages with variables, but also for messages with inline markup:
+
+#### `id`
+
+Each message in catalog is identified by **message ID**.
+
+While macro uses message (and `context` property if provided) to generate ID, it's possible to override it.
+
+```jsx
+import { Trans } from "@lingui/macro";
+Attachment {name} saved.;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+```
+
+#### `render`
+
+Render prop function used to render translation. This prop is directly passed to [`Trans`](./react.md#trans) component from [`@lingui/react`](./react.md). See [rendering of translations](./react.md#rendering-translations) for more info.
+
+#### `comment`
+
+Comment for translators to give them additional information about the message. It will be visible in the [TMS](/tools/introduction) if supported by it, and the [catalog format](/ref/catalog-formats).
+
+It's removed from the production code.
+
+#### `context` {#context-prop}
+
+Contextual information for translators. Similar to [`comment`](#comment) but also allows to extract the same messages with different IDs. It will be visible in the [TMS](/tools/introduction) if supported by it, and the [catalog format](/ref/catalog-formats).
+
+It's removed from the production code. See [Context](../guides/explicit-vs-generated-ids.md#context) for more details.
+
+```jsx
+import { Trans } from "@lingui/macro";
+Refresh inbox;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+```
+
+Lingui generates different IDs when `context` is provided:
+
+```jsx
+import { Trans } from "@lingui/macro";
+right;
+right;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+;
+```
+
+Custom `id` is preserved:
+
+```jsx
+import { Trans } from "@lingui/macro";
+Attachment {name} saved.;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+```
+
+This macro is especially useful when message contains inline markup.
+
+```jsx
+import { Trans } from "@lingui/macro";
+
+
+ Read the docs.
+;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/macro";
+ }} />;
+```
+
+Components and HTML tags are replaced with dummy indexed tags (`<0>0>`) which has several advantages:
+
+- both custom React components and built-in HTML tags are supported
+- change of component props doesn't break the translation
+- the message is extracted as a whole sentence (this seems to be obvious, but most i18n libs simply split message into pieces by tags and translate them separately)
+
+### `Plural`
+
+| Prop name | Type | Description |
+| ----------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `value` | number | (required) Value is mapped to plural form below |
+| `format` | string\|Object | Number format passed as options to [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) |
+| `offset` | number | Offset of value when calculating plural forms |
+| `zero` | string | Form for empty `value` |
+| `one` | string | _Singular_ form |
+| `two` | string | _Dual_ form |
+| `few` | string | _Paucal_ form |
+| `many` | string | _Plural_ form |
+| `other` | string | (required) general _plural_ form |
+| `_` | string | Exact match form, corresponds to `=N` rule |
+
+> MessageFormat: `{arg, plural, ...forms}`
+
+Props of [`Plural`](./macro.mdx#plural-1) macro are transformed into [`plural`](../guides/message-format.md) format.
+
+```jsx
+import { Plural } from "@lingui/macro";
+;
+
+// ↓ ↓ ↓ ↓ ↓ ↓
+
+import { Trans } from "@lingui/react";
+;
+```
+
+`#` are formatted using `number` format. `format` prop is passed to this formatter.
+
+Exact matches in MessageFormat syntax are expressed as `=int` (e.g. `=0`), but in React this isn't a valid prop name. Therefore, exact matches are expressed as `_int` prop (e.g. `_0`). This is commonly used in combination with `offset` prop. `offset` affects only plural forms, not exact matches.
+
+```jsx
+import { Plural } from "@lingui/macro";
+
+ `one` plural form
+ one="You and # other guest arrived"
+ // when value >= 3
+ other="You and # other guests arrived"
+/>;
+
+/*
+ This is transformed to Trans component with ID:
+ {count, plural, offset:1 _0 {Nobody arrived}
+ _1 {Only you arrived}
+ one {You and # other guest arrived}
+ other {You and # other guests arrived}}
+*/
+```
+
+Use `` inside `` macro if you want to provide `id`, `context` or `comment`.
+
+```jsx
+
+ ;
+
+```
+
+### `SelectOrdinal`
+
+| Prop name | Type | Description |
+| ----------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `value` | number | (required) Value is mapped to plural form below |
+| `offset` | number | Offset of value for plural forms |
+| `zero` | string | Form for empty `value` |
+| `one` | string | _Singular_ form |
+| `two` | string | _Dual_ form |
+| `few` | string | _Paucal_ form |
+| `many` | string | _Plural_ form |
+| `other` | string | (required) general _plural_ form |
+| `_` | string | Exact match form, correspond to `=N` rule. (e.g: `_0`, `_1`) |
+| `format` | string\|Object | Number format passed as options to [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) |
+
+> MessageFormat: `{arg, selectordinal, ...forms}`
+
+Props of `SelectOrdinal` macro are transformed into [`selectOrdinal`](../guides/message-format.md) format:
+
+```jsx
+import { SelectOrdinal } from "@lingui/macro";
+
+// count == 1 -> 1st
+// count == 2 -> 2nd
+// count == 3 -> 3rd
+// count == 4 -> 4th
+;
+```
+
+Use `` inside `` macro if you want to provide `id`, `context` or `comment`.
+
+```jsx
+
+
+
+```
+
+### `Select`
+
+| Prop name | Type | Description |
+| --------- | ------ | --------------------------------------------------- |
+| `value` | number | (required) Value determines which form is outputted |
+| `other` | number | (required) Default, catch-all form |
+
+> MessageFormat: `{arg, select, ...forms}`
+
+:::note
+The select cases except `other` should be prefixed with underscore: `_male` or `_female`.
+:::
+
+Props of `Select` macro are transformed into [`select`](../guides/message-format.md) format:
+
+```jsx
+import { Select } from "@lingui/macro";
+
+// gender == "female" -> Her book
+// gender == "male" -> His book
+// gender == "non-binary" -> Their book
+;
+```
+
+Use `
+```
+
+We can use nested macros, components, variables, expressions, really anything.
+
+This gives us enough flexibility for all usecases.
+
+### Custom Message ID
+
+Let's finish this with a short example of plurals with custom ID. We can pass an `id` prop to [`Plural`](../ref/macro.mdx#plural-1) as we would to [`Trans`](../ref/macro.mdx#trans):
+
+```jsx
+
+
+
+```
+
+## Formats
+
+The last message in our component is again a bit specific:
+
+```jsx
+
+```
+
+`lastLogin` is a date object, and we need to format it properly. Dates are formatted differently in different languages, but we don't have to do this manually. The heavy lifting is done by the [Intl object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl), we'll just use [`i18n.date()`](../ref/core.md#i18n.date) function. The `i18n` object can be accessed by [`useLingui`](../ref/react.md#uselingui) hook:
+
+```jsx title="src/Inbox.js"
+import { useLingui } from "@lingui/react";
+
+export default function Inbox() {
+ const { i18n } = useLingui();
+ // ...
+
+ return (
+
+ {/* ... */}
+
+
+ );
+}
+```
+
+This will format the date using the conventional format for the active language.
+
+## Review
+
+After all modifications, the final component with i18n looks like this:
+
+```jsx title="src/Inbox.js"
+import React from "react";
+import { Trans, Plural } from "@lingui/macro";
+import { useLingui } from "@lingui/react";
+
+export default function Inbox() {
+ const { i18n } = useLingui();
+ const messages = [{}, {}];
+ const messagesCount = messages.length;
+ const lastLogin = new Date();
+ const markAsRead = () => {
+ alert("Marked as read.");
+ };
+
+ return (
+