Skip to content

Commit 87ffe72

Browse files
feat: RSpack + Lingui Example (#1752)
1 parent 026c6ee commit 87ffe72

23 files changed

+783
-0
lines changed

examples/rspack/.gitignore

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
.pnpm-debug.log*
9+
10+
# Diagnostic reports (https://nodejs.org/api/report.html)
11+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12+
13+
# Runtime data
14+
pids
15+
*.pid
16+
*.seed
17+
*.pid.lock
18+
19+
# Directory for instrumented libs generated by jscoverage/JSCover
20+
lib-cov
21+
22+
# Coverage directory used by tools like istanbul
23+
coverage
24+
*.lcov
25+
26+
# nyc test coverage
27+
.nyc_output
28+
29+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30+
.grunt
31+
32+
# Bower dependency directory (https://bower.io/)
33+
bower_components
34+
35+
# node-waf configuration
36+
.lock-wscript
37+
38+
# Compiled binary addons (https://nodejs.org/api/addons.html)
39+
build/Release
40+
41+
# Dependency directories
42+
node_modules/
43+
jspm_packages/
44+
45+
# Snowpack dependency directory (https://snowpack.dev/)
46+
web_modules/
47+
48+
# TypeScript cache
49+
*.tsbuildinfo
50+
51+
# Optional npm cache directory
52+
.npm
53+
54+
# Optional eslint cache
55+
.eslintcache
56+
57+
# Optional stylelint cache
58+
.stylelintcache
59+
60+
# Microbundle cache
61+
.rpt2_cache/
62+
.rts2_cache_cjs/
63+
.rts2_cache_es/
64+
.rts2_cache_umd/
65+
66+
# Optional REPL history
67+
.node_repl_history
68+
69+
# Output of 'npm pack'
70+
*.tgz
71+
72+
# Yarn Integrity file
73+
.yarn-integrity
74+
75+
# dotenv environment variable files
76+
.env
77+
.env.development.local
78+
.env.test.local
79+
.env.production.local
80+
.env.local
81+
82+
# parcel-bundler cache (https://parceljs.org/)
83+
.cache
84+
.parcel-cache
85+
86+
# Next.js build output
87+
.next
88+
out
89+
90+
# Nuxt.js build / generate output
91+
.nuxt
92+
dist
93+
94+
# Gatsby files
95+
.cache/
96+
# Comment in the public line in if your project uses Gatsby and not Next.js
97+
# https://nextjs.org/blog/next-9-1#public-directory-support
98+
# public
99+
100+
# vuepress build output
101+
.vuepress/dist
102+
103+
# vuepress v2.x temp and cache directory
104+
.temp
105+
.cache
106+
107+
# Docusaurus cache and generated files
108+
.docusaurus
109+
110+
# Serverless directories
111+
.serverless/
112+
113+
# FuseBox cache
114+
.fusebox/
115+
116+
# DynamoDB Local files
117+
.dynamodb/
118+
119+
# TernJS port file
120+
.tern-port
121+
122+
# Stores VSCode versions used for testing VSCode extensions
123+
.vscode-test
124+
125+
# yarn v2
126+
.yarn/cache
127+
.yarn/unplugged
128+
.yarn/build-state.yml
129+
.yarn/install-state.gz
130+
.pnp.*
131+
132+
# Logs
133+
logs
134+
*.log
135+
npm-debug.log*
136+
yarn-debug.log*
137+
yarn-error.log*
138+
pnpm-debug.log*
139+
lerna-debug.log*
140+
141+
node_modules
142+
dist
143+
dist-ssr
144+
*.local
145+
146+
# Editor directories and files
147+
.vscode/*
148+
!.vscode/extensions.json
149+
.idea
150+
.DS_Store
151+
*.suo
152+
*.ntvs*
153+
*.njsproj
154+
*.sln
155+
*.sw?

examples/rspack/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Collin Brown
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

examples/rspack/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Lingui + RSpack Example
2+
3+
This project shows how to use the [Rspack JavaScript bundler](https://www.rspack.dev/guide/introduction.html) with [Lingui JS](https://lingui.dev/) to provide i18n for a React application (TypeScript).
4+
5+
![lingui-rspack-i18n-demo](demo.gif)
6+
7+
## Setup Instructions
8+
9+
1. `cd rspack-project && npm install`
10+
2. `npm run dev` to run the development server.
11+
3. `npm run build` to build the application.
12+
13+
## Update I18n
14+
15+
1. Wrap any messages requiring translation in `<Trans>` or a related macro.
16+
2. `npm run extract` to generate message catalogs in `src/locales/{locale}/messages`.
17+
3. Translate any new messages in the catalogs.
18+
4. `npm run compile` to create runtime catalogs.
19+
20+
## Configuration File Notes
21+
22+
- [rspack.config.js](./rspack-project/rspack.config.js) specifies that that babel should transcompile all `.tsx` files using the `@babel/preset-typscript` and `@babel/preset-react` [presets](https://babeljs.io/docs/presets), as well as the `macros` [plugin](https://babeljs.io/docs/plugins). This step is necessary so that [Lingui Macros](https://lingui.dev/ref/macro) such as `<Trans>` are correctly transcompiled into their respective React components.
23+
- [lingui.config.ts](./rspack-project/lingui.config.ts) specifies the available locales, defaults, and paths where the message catalogs are stored.
24+
- As per the [Rspack documentation](https://www.rspack.dev/guide/loader.html#builtinswc-loader), `builtin:swc-loader` does not currently support plugins, which is why the trans-compilation work is still done in babel. Once SWC plugins are supported, transcompilation should be done with Rspack's `builtin:swc-loader` for improved performance.
25+
26+
## Helpful Resources
27+
28+
- This [blog post](https://betterprogramming.pub/react-app-internationalization-with-linguijs-9486ccd80e07) shows a step-by-step guide to set up LinguiJS with React.
29+
- [Official documentation for React setup with LinguiJS](https://lingui.dev/tutorials/react). This repo closely follows the example from the official docs.

examples/rspack/demo.gif

1.76 MB
Loading

examples/rspack/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="zh-CN">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Rspack + React + TS</title>
8+
</head>
9+
10+
<body>
11+
<div id="root"></div>
12+
</body>
13+
14+
</html>

examples/rspack/lingui.config.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { LinguiConfig } from '@lingui/conf'
2+
3+
const config: Partial<LinguiConfig> = {
4+
locales: ["en", "fr"],
5+
sourceLocale: "en",
6+
catalogs: [{
7+
path: "src/locales/{locale}/messages",
8+
include: ["src"]
9+
}],
10+
format: "po"
11+
};
12+
13+
export default config;

examples/rspack/package.json

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "rspack-react-ts-starter",
3+
"private": true,
4+
"version": "1.0.0",
5+
"babel": {
6+
"plugins": [
7+
"macros"
8+
]
9+
},
10+
"scripts": {
11+
"dev": "rspack serve",
12+
"build": "rspack build",
13+
"extract": "lingui extract",
14+
"compile": "lingui compile --typescript"
15+
},
16+
"dependencies": {
17+
"@babel/preset-react": "^7.22.5",
18+
"@babel/preset-typescript": "^7.22.5",
19+
"@lingui/react": "^4.4.0",
20+
"babel-loader": "^9.1.3",
21+
"react": "^18.2.0",
22+
"react-dom": "^18.2.0"
23+
},
24+
"devDependencies": {
25+
"@babel/core": "^7.22.10",
26+
"@lingui/cli": "^4.4.0",
27+
"@lingui/macro": "^4.4.0",
28+
"@rspack/cli": "latest",
29+
"@types/react": "18.2.0",
30+
"@types/react-dom": "18.2.1",
31+
"babel-plugin-macros": "^3.1.0",
32+
"typescript": "^5.0.4"
33+
}
34+
}

examples/rspack/rspack.config.js

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @type {import('@rspack/cli').Configuration}
3+
*/
4+
module.exports = {
5+
context: __dirname,
6+
entry: {
7+
main: "./src/main.tsx"
8+
},
9+
builtins: {
10+
html: [
11+
{
12+
template: "./index.html"
13+
}
14+
]
15+
},
16+
module: {
17+
rules: [
18+
{
19+
test: /\.svg$/,
20+
type: "asset"
21+
},
22+
{
23+
test: /\.tsx$/,
24+
exclude: /node_modules/,
25+
use: {
26+
loader: 'babel-loader',
27+
options: {
28+
presets: [
29+
"@babel/preset-typescript",
30+
"@babel/preset-react"
31+
],
32+
plugins: [
33+
"macros"
34+
]
35+
}
36+
}
37+
}
38+
]
39+
}
40+
};

examples/rspack/src/Inbox.tsx

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from "react";
2+
3+
import { useLingui } from "@lingui/react";
4+
5+
import { Trans, Plural } from "@lingui/macro";
6+
7+
import LocaleSwitcher from './LocaleSwitcher';
8+
9+
export default function Inbox() {
10+
const messages = [{}, {}];
11+
const messagesCount = messages.length;
12+
const lastLogin = new Date();
13+
const markAsRead = () => {
14+
alert("Marked as read.");
15+
};
16+
const { i18n } = useLingui();
17+
18+
return (
19+
<div>
20+
<LocaleSwitcher />
21+
<h1><Trans>Message Inbox</Trans></h1>
22+
<p>
23+
<Trans>
24+
See all <a href="/unread">unread messages </a>
25+
{" or "}
26+
<a onClick={markAsRead}>mark them</a> as read.
27+
</Trans>
28+
</p>
29+
<p>
30+
<Plural value={messagesCount} one="There's # message in your inbox." other="There are # messages in your inbox." />
31+
</p>
32+
< footer > <Trans>Last login on {i18n.date(lastLogin)}.</Trans></footer>
33+
</div>
34+
);
35+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import { useLingui } from '@lingui/react';
3+
import Locale from './locales';
4+
5+
function LocaleSwitcher() {
6+
const { i18n } = useLingui();
7+
8+
const handleLocaleChange = (newLocale: Locale) => {
9+
i18n.activate(newLocale);
10+
};
11+
12+
return (
13+
<div>
14+
<button onClick={() => handleLocaleChange(Locale.ENGLISH)}>English</button>
15+
<button onClick={() => handleLocaleChange(Locale.FRENCH)}>Français</button>
16+
{/* Add more buttons for other supported locales */}
17+
</div>
18+
);
19+
}
20+
21+
export default LocaleSwitcher;

examples/rspack/src/assets/react.svg

+1
Loading

0 commit comments

Comments
 (0)