Skip to content

Commit

Permalink
feat: i18n plugin (#6149)
Browse files Browse the repository at this point in the history
* feat: antd5 example with i18n

* feat: fusion example with i18n

* feat: init plugin-i18n

* feat: init i18n example

* chore: lock

* feat: add language selector

* feat: i18n route

* feat: getAllLocales and getDefaultLocale API

* fix: ssr render error

* feat: support auto redirect

* fix: redirect without url param

* feat: support add response handler

* chore: lock

* feat: support blockCookie option

* fix: route test

* fix: ssg error

* fix: i18n response

* feat: add server example

* chore: add basename

* chore: add headers

* feat: i18n docs

* fix: i18n plugin

* feat: enhance disable cookie

* docs: update disabledCookie documentation

* chore: update desc

* fix: param not defined

* fix: generate routes.ts

* fix: remove todo

* test: i18n

* test: i18n

* fix: lint warning

* fix: addDefineRoutesFunc to addRoutesDefinition

* chore: i18n plugin version

* chore: changelog

* feat: use modifyRenderData instead of runtimeOptions.raw

* chore: update changelog

* chore: rename param

* fix: comment

* feat: use createLogger instead of consola directly

* feat: change customRuntimeOptions type from string to object

* refactor: route id

* chore: remove unused console.log

* chore: add comment

* fix: win32 test fail

* chore: changelog
  • Loading branch information
luhc228 authored Apr 24, 2023
1 parent 3ede3c5 commit 1c3d3fe
Show file tree
Hide file tree
Showing 78 changed files with 3,155 additions and 296 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-bats-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/app': patch
---

feat: support add routes definition
5 changes: 5 additions & 0 deletions .changeset/fluffy-hounds-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/route-manifest': minor
---

refactor: route id generation
5 changes: 5 additions & 0 deletions .changeset/hip-balloons-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/plugin-i18n': major
---

feat: init plugin
5 changes: 5 additions & 0 deletions .changeset/hot-baboons-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/route-manifest': minor
---

feat: support accept one more `defineExtraRoutes` functions
5 changes: 5 additions & 0 deletions .changeset/red-gorillas-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/runtime': patch
---

feat: support handler response
5 changes: 5 additions & 0 deletions .changeset/tasty-spies-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/app': patch
---

fix: routeSpecifier is not unique
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ yalc.lock
# Packages
packages/*/lib/
packages/*/esm/
packages/*/es2017/

# temp folder .ice
examples/*/.ice
Expand Down
5 changes: 5 additions & 0 deletions examples/with-antd5/ice.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineConfig } from '@ice/app';

export default defineConfig(() => ({
ssg: false,
}));
22 changes: 22 additions & 0 deletions examples/with-antd5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@examples/with-antd5",
"private": true,
"version": "1.0.0",
"scripts": {
"start": "ice start",
"build": "ice build"
},
"dependencies": {
"@ice/app": "workspace:*",
"@ice/runtime": "workspace:*",
"antd": "^5.0.0",
"dayjs": "^1.11.7",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-intl": "^6.3.2"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.2"
}
}
7 changes: 7 additions & 0 deletions examples/with-antd5/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineAppConfig } from 'ice';

export default defineAppConfig(() => ({
app: {
rootId: 'app',
},
}));
22 changes: 22 additions & 0 deletions examples/with-antd5/src/document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Meta, Title, Links, Main, Scripts } from 'ice';

function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ICE Demo" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Title />
<Links />
</head>
<body>
<Main />
<Scripts />
</body>
</html>
);
}

export default Document;
10 changes: 10 additions & 0 deletions examples/with-antd5/src/locales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const messages: Record<string, any> = {
en: {
changeLanguageTitle: 'Change locale:',
indexTitle: 'Index',
},
'zh-cn': {
changeLanguageTitle: '修改语言:',
indexTitle: '首页',
},
};
12 changes: 12 additions & 0 deletions examples/with-antd5/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DatePicker, Pagination } from 'antd';
import { FormattedMessage } from 'react-intl';

export default function Index() {
return (
<div>
<h2><FormattedMessage id="indexTitle" /></h2>
<Pagination defaultCurrent={1} total={50} showSizeChanger />
<DatePicker />
</div>
);
}
47 changes: 47 additions & 0 deletions examples/with-antd5/src/pages/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Outlet } from 'ice';
import { useState } from 'react';
import { IntlProvider, FormattedMessage } from 'react-intl';
import { ConfigProvider, Radio } from 'antd';
import type { RadioChangeEvent } from 'antd';
import enUS from 'antd/locale/en_US';
import zhCN from 'antd/locale/zh_CN';
import type { Locale } from 'antd/es/locale';

import * as dayjs from 'dayjs';
import { messages } from '@/locales';
import 'dayjs/locale/zh-cn';

export default function Layout() {
const [locale, setLocale] = useState<Locale>(enUS);

const changeLocale = (e: RadioChangeEvent) => {
const localeValue = e.target.value;
setLocale(localeValue);
if (localeValue) {
dayjs.locale('zh-cn');
} else {
dayjs.locale('en');
}
};

return (
<main>
<IntlProvider locale={locale.locale} messages={messages[locale.locale]}>
<div style={{ marginBottom: 16 }}>
<span style={{ marginRight: 16 }}><FormattedMessage id="changeLanguageTitle" /></span>
<Radio.Group value={locale} onChange={changeLocale}>
<Radio.Button key="en" value={enUS}>
English
</Radio.Button>
<Radio.Button key="cn" value={zhCN}>
中文
</Radio.Button>
</Radio.Group>
</div>
<ConfigProvider locale={locale}>
<Outlet />
</ConfigProvider>
</IntlProvider>
</main>
);
}
1 change: 1 addition & 0 deletions examples/with-antd5/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="@ice/app/types" />
17 changes: 17 additions & 0 deletions examples/with-antd5/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"baseUrl": "./",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext", "DOM.Iterable"],
"jsx": "react-jsx",
"moduleResolution": "node",
"strict": true,
"skipLibCheck": true,
"paths": {
"@/*": ["./src/*"],
"ice": [".ice"]
}
},
"include": ["src", ".ice"],
}
1 change: 1 addition & 0 deletions examples/with-fusion/ice.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export default defineConfig({
locales: ['af'],
}),
],
ssg: false,
});
9 changes: 5 additions & 4 deletions examples/with-fusion/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@
"dependencies": {
"@alifd/next": "^1.25.49",
"@ice/runtime": "workspace:*",
"moment": "^2.29.4",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"moment": "^2.29.4"
"react-intl": "^6.3.2"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.2",
"@ice/app": "workspace:*",
"@ice/plugin-css-assets-local": "workspace:*",
"@ice/plugin-fusion": "workspace:*",
"@ice/plugin-moment-locales": "workspace:*"
"@ice/plugin-moment-locales": "workspace:*",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.2"
}
}
8 changes: 8 additions & 0 deletions examples/with-fusion/src/locales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const messages: Record<string, any> = {
en: {
buttonText: 'Button',
},
'zh-cn': {
buttonText: '按钮',
},
};
8 changes: 6 additions & 2 deletions examples/with-fusion/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Button } from '@alifd/next';
import { Button, DatePicker } from '@alifd/next';
import '@alifd/next/dist/next.css';
import { FormattedMessage } from 'react-intl';

export default function Home() {
return (
<div>
<h1>with fusion</h1>
<Button type="primary">Button</Button>
<DatePicker />
<Button type="primary">
<FormattedMessage id="buttonText" />
</Button>
</div>
);
}
46 changes: 46 additions & 0 deletions examples/with-fusion/src/pages/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Outlet } from 'ice';
import { useState } from 'react';
import { ConfigProvider, Radio } from '@alifd/next';
import enUS from '@alifd/next/lib/locale/en-us';
import zhCN from '@alifd/next/lib/locale/zh-cn';
import { IntlProvider } from 'react-intl';
import { messages } from '@/locales';

const localeMap = new Map([
['en', enUS],
['zh-cn', zhCN],
]);

export default function Layout() {
const [locale, setLocale] = useState('en');
const list = [
{
value: 'en',
label: 'English',
},
{
value: 'zh-cn',
label: '中文',
},
];

function changeLocale(value: string) {
setLocale(value);
}
return (
<main>
<IntlProvider locale={locale} messages={messages[locale]}>
<Radio.Group
dataSource={list}
shape="button"
value={locale}
onChange={changeLocale}
/>
<ConfigProvider locale={localeMap.get(locale)}>
<Outlet />
</ConfigProvider>
</IntlProvider>
</main>
);
}

13 changes: 13 additions & 0 deletions examples/with-i18n/ice.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from '@ice/app';
import i18n from '@ice/plugin-i18n';

export default defineConfig({
plugins: [
i18n({
locales: ['zh-CN', 'en-US'],
defaultLocale: 'zh-CN',
autoRedirect: true,
}),
],
ssr: true,
});
26 changes: 26 additions & 0 deletions examples/with-i18n/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@examples/with-i18n",
"private": true,
"version": "1.0.0",
"scripts": {
"start": "ice start",
"build": "ice build",
"serve": "tsx server.mts"
},
"dependencies": {
"@ice/runtime": "workspace:*",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-intl": "^6.3.2"
},
"devDependencies": {
"@ice/app": "workspace:*",
"@ice/plugin-i18n": "workspace:*",
"@types/express": "^4.17.14",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.2",
"express": "^4.17.3",
"tslib": "^2.5.0",
"tsx": "^3.12.1"
}
}
27 changes: 27 additions & 0 deletions examples/with-i18n/server.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import express from 'express';
import { renderToHTML } from './build/server/index.mjs';

const app = express();
const port = 4000;
const basename = '/app';

app.use(express.static('build', {}));

app.use(async (req, res) => {
const { statusCode, statusText, headers, value: body } = await renderToHTML({ req, res }, { basename });
res.statusCode = statusCode;
res.statusMessage = statusText;
Object.entries((headers || {}) as Record<string, string>).forEach(([name, value]) => {
res.setHeader(name, value);
});
if (body && req.method !== 'HEAD') {
res.end(body);
} else {
res.end();
}
});


app.listen(port, () => {
console.log(`App listening on http://localhost:${port}${basename}`);
});
12 changes: 12 additions & 0 deletions examples/with-i18n/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineAppConfig } from 'ice';
import { defineI18nConfig } from '@ice/plugin-i18n/types';

export default defineAppConfig(() => ({
router: {
basename: '/app',
},
}));

export const i18nConfig = defineI18nConfig(() => ({
// disabledCookie: true,
}));
Loading

0 comments on commit 1c3d3fe

Please sign in to comment.