Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions examples/rspack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ This project shows how to use the [Rspack JavaScript bundler](https://www.rspack
1. Wrap any messages requiring translation in `<Trans>` or a related macro.
2. `npm run extract` to generate message catalogs in `src/locales/{locale}/messages`.
3. Translate any new messages in the catalogs.
4. `npm run compile` to create runtime catalogs.
4. Restart the dev server or rebuild the app. `@lingui/loader` compiles `.po` catalogs during bundling, so no separate `lingui compile` step is required.

## Configuration File Notes

- [`rspack.config.js`](./rspack.config.js) specifies the [`builtin:swc-loader`](https://www.rspack.dev/guide/builtin-swc-loader.html#builtinswc-loader) should transcompile all `.tsx` files using the Lingui SWC plugin, ensuring transcompilation of [Lingui Macros](https://lingui.dev/ref/macro) like `<Trans>` into their respective React components. When using Lingui SWC plugin, ensure version compatibility with `@rspack/core`. Refer to the [compatibility guide](https://github.com/lingui/swc-plugin#compatibility) for selecting the appropriate plugin version.
- [`rspack.config.js`](./rspack.config.js) configures both the [`builtin:swc-loader`](https://www.rspack.dev/guide/builtin-swc-loader.html#builtinswc-loader) and `@lingui/loader`. The SWC plugin transpiles [Lingui Macros](https://lingui.dev/ref/macro) like `<Trans>` into their React runtime equivalents, while `@lingui/loader` turns imported `.po` catalogs into runtime messages at build time. When using Lingui SWC plugin, ensure version compatibility with `@rspack/core`. Refer to the [compatibility guide](https://github.com/lingui/swc-plugin#compatibility) for selecting the appropriate plugin version.

- [`src/i18n.ts`](./src/i18n.ts) lazy-loads locale catalogs from `src/locales/{locale}/messages.po`, which lets `rspack` split translations into separate chunks and compile them through `@lingui/loader`.

- [`lingui.config.ts`](./lingui.config.ts) specifies the available locales, defaults, and paths where the message catalogs are stored.

Expand Down
16 changes: 8 additions & 8 deletions examples/rspack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
"scripts": {
"dev": "rspack serve",
"build": "rspack build",
"extract": "lingui extract",
"compile": "lingui compile --typescript"
"extract": "lingui extract"
},
"dependencies": {
"@lingui/core": "^5.9.0",
"@lingui/react": "^5.9.0",
"@lingui/core": "^5.9.5",
"@lingui/react": "^5.9.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@lingui/cli": "^4.7.2",
"@lingui/cli": "^5.9.5",
"@lingui/loader": "^5.9.5",
"@lingui/swc-plugin": "^5.10.0",
"@rspack/cli": "^1.7.4",
"@rspack/core": "^1.7.4",
"@rspack/cli": "^1.7.11",
"@rspack/core": "^1.7.11",
"@types/react": "^18.2.79",
"@types/react-dom": "18.2.1",
"@types/react-dom": "^18.2.1",
"typescript": "^5.9.3"
}
}
11 changes: 11 additions & 0 deletions examples/rspack/rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ module.exports = {
test: /\.svg$/,
type: "asset",
},
{
test: /\.po$/,
use: [
{
loader: "@lingui/loader",
options: {
failOnCompileError: true,
},
},
],
},
{
test: /\.(jsx?|tsx?)$/,
exclude: /node_modules/,
Expand Down
13 changes: 6 additions & 7 deletions examples/rspack/src/Inbox.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { useLingui } from "@lingui/react"

import { Plural, Trans } from "@lingui/react/macro"

import { useLingui, Plural, Trans } from "@lingui/react/macro"
import LocaleSwitcher from "./LocaleSwitcher"

export default function Inbox() {
const messages = [{}, {}]
const messagesCount = messages.length
const lastLogin = new Date()

const { i18n, t } = useLingui()

const markAsRead = () => {
alert("Marked as read.")
alert(t`Marked as read.`)
}
const { i18n } = useLingui()

return (
<div>
Expand All @@ -23,7 +22,7 @@ export default function Inbox() {
<Trans>
See all <a href="/unread">unread messages </a>
{" or "}
<a onClick={markAsRead}>mark them</a> as read.
<button onClick={markAsRead}>mark them</button> as read.
</Trans>
</p>
<p>
Expand Down
50 changes: 36 additions & 14 deletions examples/rspack/src/LocaleSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
import React from 'react';
import { useLingui } from '@lingui/react';
import Locale from './locales';
import { useState } from "react";
import { useLingui } from "@lingui/react";

import { dynamicActivate } from "./i18n";
import Locale from "./locales";

function LocaleSwitcher() {
const { i18n } = useLingui();
const { i18n } = useLingui();
const [isLoading, setIsLoading] = useState(false);

const handleLocaleChange = async (newLocale: Locale) => {
if (isLoading || newLocale === i18n.locale) {
return;
}

setIsLoading(true);

const handleLocaleChange = (newLocale: Locale) => {
i18n.activate(newLocale);
};
try {
await dynamicActivate(i18n, newLocale);
} finally {
setIsLoading(false);
}
};

return (
<div>
<button onClick={() => handleLocaleChange(Locale.ENGLISH)}>English</button>
<button onClick={() => handleLocaleChange(Locale.FRENCH)}>Français</button>
{/* Add more buttons for other supported locales */}
</div>
);
return (
<div>
<button
disabled={isLoading || i18n.locale === Locale.ENGLISH}
onClick={() => void handleLocaleChange(Locale.ENGLISH)}
>
English
</button>
<button
disabled={isLoading || i18n.locale === Locale.FRENCH}
onClick={() => void handleLocaleChange(Locale.FRENCH)}
>
Français
</button>
</div>
);
}

export default LocaleSwitcher;
24 changes: 24 additions & 0 deletions examples/rspack/src/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { I18n, Messages } from "@lingui/core";

import Locale from "./locales";

type CatalogModule = {
messages: Messages;
};

const catalogs: Record<Locale, () => Promise<CatalogModule>> = {
[Locale.ENGLISH]: () => import("./locales/en/messages.po"),
[Locale.FRENCH]: () => import("./locales/fr/messages.po"),
};

export async function loadCatalog(locale: Locale) {
const { messages } = await catalogs[locale]();

return messages;
}

export async function dynamicActivate(i18n: I18n, locale: Locale) {
const messages = await loadCatalog(locale);

i18n.loadAndActivate({ locale, messages });
}
4 changes: 0 additions & 4 deletions examples/rspack/src/locales/en/messages.d.ts

This file was deleted.

17 changes: 11 additions & 6 deletions examples/rspack/src/locales/en/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"

#: src/Inbox.tsx:30
#: src/Inbox.tsx:29
msgid "{messagesCount, plural, one {There's # message in your inbox.} other {There are # messages in your inbox.}}"
msgstr "{messagesCount, plural, one {There's # message in your inbox.} other {There are # messages in your inbox.}}"

#: src/Inbox.tsx:32
#. placeholder {0}: i18n.date(lastLogin)
#: src/Inbox.tsx:37
msgid "Last login on {0}."
msgstr "Last login on {0}."

#: src/Inbox.tsx:21
#: src/Inbox.tsx:12
msgid "Marked as read."
msgstr "Marked as read."

#: src/Inbox.tsx:19
msgid "Message Inbox"
msgstr "Message Inbox"

#: src/Inbox.tsx:23
msgid "See all <0>unread messages </0>or <1>mark them</1> as read."
msgstr "See all <0>unread messages </0>or <1>mark them</1> as read."
#: src/Inbox.tsx:22
msgid "See all <0>unread messages </0> or <1>mark them</1> as read."
msgstr "See all <0>unread messages </0> or <1>mark them</1> as read."
1 change: 0 additions & 1 deletion examples/rspack/src/locales/en/messages.ts

This file was deleted.

4 changes: 0 additions & 4 deletions examples/rspack/src/locales/fr/messages.d.ts

This file was deleted.

21 changes: 13 additions & 8 deletions examples/rspack/src/locales/fr/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"

#: src/Inbox.tsx:30
#: src/Inbox.tsx:29
msgid "{messagesCount, plural, one {There's # message in your inbox.} other {There are # messages in your inbox.}}"
msgstr "{messagesCount, plural, one {Il y a # message dans boîte de réception.} other {Il y a # messages dans votre boîte de réception.}}"
msgstr "{messagesCount, plural, one {Il y a # message dans votre boîte de réception.} other {Il y a # messages dans votre boîte de réception.}}"

#: src/Inbox.tsx:32
#. placeholder {0}: i18n.date(lastLogin)
#: src/Inbox.tsx:37
msgid "Last login on {0}."
msgstr "Dernière connexion le {0}."

#: src/Inbox.tsx:21
#: src/Inbox.tsx:12
msgid "Marked as read."
msgstr "Marqué comme lu."

#: src/Inbox.tsx:19
msgid "Message Inbox"
msgstr "Boîte de réception de messages."
msgstr "Boîte de réception"

#: src/Inbox.tsx:23
msgid "See all <0>unread messages </0>or <1>mark them</1> as read."
msgstr "Voir tous <0>les messages non lus </0>ou <1>les marquer</1> comme lus."
#: src/Inbox.tsx:22
msgid "See all <0>unread messages </0> or <1>mark them</1> as read."
msgstr "Voir tous <0>les messages non lus </0> ou <1>les marquer</1> comme lus."
1 change: 0 additions & 1 deletion examples/rspack/src/locales/fr/messages.ts

This file was deleted.

40 changes: 18 additions & 22 deletions examples/rspack/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import React from "react";
import { i18n, } from "@lingui/core";
import { createRoot } from "react-dom/client";

import { i18n } from "@lingui/core";
import { I18nProvider } from "@lingui/react";
import { messages as enMessages } from "./locales/en/messages";
import { messages as frMessages } from "./locales/fr/messages";

import Inbox from "./Inbox";
import { dynamicActivate } from "./i18n";
import Locale from "./locales";

i18n.load({
"en": enMessages,
"fr": frMessages,
});

i18n.activate("en");

const App = () => (
<I18nProvider i18n={i18n}>
<Inbox />
</I18nProvider>
);

const container = document.getElementById('root');
const container = document.getElementById("root");
const root = createRoot(container!);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
)

void dynamicActivate(i18n, Locale.ENGLISH)
.then(() => {
root.render(
<React.StrictMode>
<I18nProvider i18n={i18n}>
<Inbox />
</I18nProvider>
</React.StrictMode>,
);
})
.catch((error) => {
console.error("Failed to load initial catalog.", error);
});
7 changes: 4 additions & 3 deletions examples/rspack/src/react-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ declare module "*.txt" {
}

declare module "*.po" {
const src: string;
export default src;
}
import type { Messages } from "@lingui/core";

export const messages: Messages;
}
Loading
Loading