@lobehub/chat 0.120.2 → 0.120.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +47 -22
- package/README.zh-CN.md +53 -26
- package/docs/Deployment/Upstream-Sync.md +8 -2
- package/docs/Deployment/Upstream-Sync.zh-CN.md +9 -3
- package/docs/Development/Add-New-Locale.md +9 -1
- package/docs/Development/Add-New-Locale.zh-CN.md +9 -1
- package/docs/Development/Contributing-Guidelines.md +83 -0
- package/docs/Development/Contributing-Guidelines.zh-CN.md +13 -0
- package/docs/Development/Internationalization-Implementation.md +125 -0
- package/docs/Development/Internationalization-Implementation.zh-CN.md +9 -1
- package/docs/Development/State-Management/index.md +224 -0
- package/docs/Development/State-Management/index.zh-CN.md +25 -28
- package/docs/Home.md +9 -6
- package/docs/_Sidebar.md +9 -6
- package/locales/ar/common.json +1 -0
- package/locales/ar/setting.json +1 -3
- package/locales/de-DE/common.json +1 -0
- package/locales/de-DE/setting.json +1 -3
- package/locales/en-US/common.json +1 -0
- package/locales/en-US/setting.json +1 -3
- package/locales/es-ES/common.json +1 -0
- package/locales/es-ES/setting.json +1 -3
- package/locales/fr-FR/common.json +1 -0
- package/locales/fr-FR/setting.json +1 -3
- package/locales/it-IT/common.json +1 -0
- package/locales/it-IT/setting.json +1 -3
- package/locales/ja-JP/common.json +1 -0
- package/locales/ja-JP/setting.json +1 -3
- package/locales/ko-KR/common.json +1 -0
- package/locales/ko-KR/setting.json +1 -3
- package/locales/nl-NL/common.json +1 -0
- package/locales/nl-NL/setting.json +1 -3
- package/locales/pl-PL/common.json +1 -0
- package/locales/pl-PL/setting.json +1 -3
- package/locales/pt-BR/common.json +1 -0
- package/locales/pt-BR/setting.json +1 -3
- package/locales/ru-RU/common.json +1 -0
- package/locales/ru-RU/setting.json +1 -3
- package/locales/tr-TR/common.json +1 -0
- package/locales/tr-TR/setting.json +1 -3
- package/locales/vi-VN/common.json +1 -0
- package/locales/vi-VN/setting.json +1 -3
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-CN/setting.json +1 -3
- package/locales/zh-TW/common.json +1 -0
- package/locales/zh-TW/setting.json +1 -3
- package/next.config.mjs +2 -2
- package/package.json +6 -6
- package/scripts/docsWorkflow/const.ts +1 -1
- package/scripts/docsWorkflow/index.ts +9 -3
- package/scripts/readmeWorkflow/syncAgentIndex.ts +2 -2
- package/scripts/readmeWorkflow/syncPluginIndex.ts +2 -2
- package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +0 -2
- package/src/app/settings/common/Common.tsx +5 -6
- package/src/const/locale.ts +2 -2
- package/src/features/ChatInput/ActionBar/Clear.tsx +0 -2
- package/src/features/PluginDetailModal/index.tsx +0 -2
- package/src/features/PluginDevModal/index.tsx +0 -1
- package/src/layout/GlobalLayout/Locale.tsx +37 -10
- package/src/layout/GlobalLayout/index.tsx +1 -1
- package/src/locales/default/setting.ts +0 -2
- package/src/locales/resources.ts +9 -7
package/next.config.mjs
CHANGED
|
@@ -21,11 +21,11 @@ const nextConfig = {
|
|
|
21
21
|
compress: isProd,
|
|
22
22
|
experimental: {
|
|
23
23
|
optimizePackageImports: [
|
|
24
|
-
'modern-screenshot',
|
|
25
24
|
'emoji-mart',
|
|
26
25
|
'@emoji-mart/react',
|
|
27
26
|
'@emoji-mart/data',
|
|
28
27
|
'@icons-pack/react-simple-icons',
|
|
28
|
+
'@lobehub/ui',
|
|
29
29
|
'gpt-tokenizer',
|
|
30
30
|
'chroma-js',
|
|
31
31
|
],
|
|
@@ -46,7 +46,7 @@ const nextConfig = {
|
|
|
46
46
|
|
|
47
47
|
reactStrictMode: true,
|
|
48
48
|
|
|
49
|
-
transpilePackages: ['antd-style'
|
|
49
|
+
transpilePackages: ['antd-style'],
|
|
50
50
|
|
|
51
51
|
webpack(config) {
|
|
52
52
|
config.experiments = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.120.
|
|
3
|
+
"version": "0.120.3",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dev": "next dev -p 3010",
|
|
33
33
|
"docs": "lobe-i18n md && npm run workflow:docs",
|
|
34
34
|
"i18n": "npm run workflow:i18n && lobe-i18n",
|
|
35
|
-
"i18n
|
|
35
|
+
"i18n:docs": "lobe-i18n md && npm run workflow:docs",
|
|
36
36
|
"lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
|
|
37
37
|
"lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true",
|
|
38
38
|
"lint:md": "remark . --quiet --frail --output",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"test:coverage-local": "vitest run --coverage",
|
|
52
52
|
"test:update": "vitest -u",
|
|
53
53
|
"type-check": "tsc --noEmit",
|
|
54
|
-
"workflow:docs": "
|
|
55
|
-
"workflow:i18n": "
|
|
56
|
-
"workflow:readme": "
|
|
54
|
+
"workflow:docs": "tsx scripts/docsWorkflow/index.ts",
|
|
55
|
+
"workflow:i18n": "tsx scripts/i18nWorkflow/index.ts",
|
|
56
|
+
"workflow:readme": "tsx scripts/readmeWorkflow/index.ts"
|
|
57
57
|
},
|
|
58
58
|
"lint-staged": {
|
|
59
59
|
"*.md": [
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
"remark-parse": "^10",
|
|
180
180
|
"semantic-release": "^21",
|
|
181
181
|
"stylelint": "^15",
|
|
182
|
-
"
|
|
182
|
+
"tsx": "^4",
|
|
183
183
|
"typescript": "^5",
|
|
184
184
|
"unified": "^11",
|
|
185
185
|
"unist-util-visit": "^5",
|
|
@@ -6,7 +6,7 @@ export const ROOT = resolve(__dirname, '../..');
|
|
|
6
6
|
export const DOCS_DIR = resolve(ROOT, 'docs');
|
|
7
7
|
export const HOME_PATH = resolve(DOCS_DIR, 'Home.md');
|
|
8
8
|
export const SIDEBAR_PATH = resolve(DOCS_DIR, '_Sidebar.md');
|
|
9
|
-
export const docsFiles = globSync(resolve(DOCS_DIR, '**/*.md'), {
|
|
9
|
+
export const docsFiles = globSync(resolve(DOCS_DIR, '**/*.md').replaceAll('\\', '/'), {
|
|
10
10
|
ignore: ['Home.md', '_Sidebar.md', '_Footer.md'],
|
|
11
11
|
nodir: true,
|
|
12
12
|
}).filter((file) => !file.includes('.zh-CN.md'));
|
|
@@ -20,9 +20,15 @@ const run = async () => {
|
|
|
20
20
|
const links = {
|
|
21
21
|
cn: urlJoin(
|
|
22
22
|
WIKI_URL,
|
|
23
|
-
relative(DOCS_DIR, paths.cn)
|
|
23
|
+
relative(DOCS_DIR, paths.cn)
|
|
24
|
+
.replaceAll('\\', '/')
|
|
25
|
+
.split('/')[1]
|
|
26
|
+
.replace('.zh-CN.md', '.zh-CN'),
|
|
27
|
+
),
|
|
28
|
+
en: urlJoin(
|
|
29
|
+
WIKI_URL,
|
|
30
|
+
relative(DOCS_DIR, paths.en).replaceAll('\\', '/').split('/')[1].replace('.md', ''),
|
|
24
31
|
),
|
|
25
|
-
en: urlJoin(WIKI_URL, relative(DOCS_DIR, paths.en).split('/')[1].replace('.md', '')),
|
|
26
32
|
};
|
|
27
33
|
const titles = {
|
|
28
34
|
cn: await getTitle(paths.cn),
|
|
@@ -59,7 +65,7 @@ const run = async () => {
|
|
|
59
65
|
child.links.cn,
|
|
60
66
|
)}\n`;
|
|
61
67
|
sidebarContent += `- ${genMdLink(child.titles.en, child.links.en)} | ${genMdLink(
|
|
62
|
-
|
|
68
|
+
child.titles.cn,
|
|
63
69
|
child.links.cn,
|
|
64
70
|
)}\n`;
|
|
65
71
|
});
|
|
@@ -10,7 +10,7 @@ const genAgentTable = (data: DataItem[], lang: string) => {
|
|
|
10
10
|
const content = data.slice(0, 4).map((item) => [
|
|
11
11
|
[
|
|
12
12
|
genLink(
|
|
13
|
-
item.meta.title,
|
|
13
|
+
item.meta.title.replaceAll('|', ','),
|
|
14
14
|
qs.stringifyUrl({
|
|
15
15
|
query: { agent: item.identifier },
|
|
16
16
|
url: MARKET_URL,
|
|
@@ -18,7 +18,7 @@ const genAgentTable = (data: DataItem[], lang: string) => {
|
|
|
18
18
|
),
|
|
19
19
|
`<sup>By **${genLink(item.author, item.homepage)}** on **${(item as any).createAt}**</sup>`,
|
|
20
20
|
].join('<br/>'),
|
|
21
|
-
[item.meta.description, genTags(item.meta.tags)].join('<br/>'),
|
|
21
|
+
[item.meta.description.replaceAll('|', ','), genTags(item.meta.tags)].join('<br/>'),
|
|
22
22
|
]);
|
|
23
23
|
return markdownTable([
|
|
24
24
|
isCN ? ['最近新增', '助手说明'] : ['Recent Submits', 'Description'],
|
|
@@ -10,10 +10,10 @@ const genPluginTable = (data: DataItem[], lang: string) => {
|
|
|
10
10
|
.slice(0, 4)
|
|
11
11
|
.map((item) => [
|
|
12
12
|
[
|
|
13
|
-
genLink(item.meta.title, PLGUIN_URL),
|
|
13
|
+
genLink(item.meta.title.replaceAll('|', ','), PLGUIN_URL),
|
|
14
14
|
`<sup>By **${item.author}** on **${item.createdAt}**</sup>`,
|
|
15
15
|
].join('<br/>'),
|
|
16
|
-
[item.meta.description, genTags(item.meta.tags)].join('<br/>'),
|
|
16
|
+
[item.meta.description.replaceAll('|', ','), genTags(item.meta.tags)].join('<br/>'),
|
|
17
17
|
]);
|
|
18
18
|
return markdownTable([
|
|
19
19
|
isCN ? ['最近新增', '插件描述'] : ['Recent Submits', 'Description'],
|
|
@@ -94,10 +94,8 @@ const Actions = memo<ActionProps>(({ id, setOpen }) => {
|
|
|
94
94
|
domEvent.stopPropagation();
|
|
95
95
|
|
|
96
96
|
modal.confirm({
|
|
97
|
-
cancelText: t('cancel'),
|
|
98
97
|
centered: true,
|
|
99
98
|
okButtonProps: { danger: true },
|
|
100
|
-
okText: t('ok'),
|
|
101
99
|
onOk: () => {
|
|
102
100
|
removeSession(id);
|
|
103
101
|
},
|
|
@@ -49,10 +49,8 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
|
|
|
49
49
|
|
|
50
50
|
const handleReset = useCallback(() => {
|
|
51
51
|
modal.confirm({
|
|
52
|
-
cancelText: t('cancel', { ns: 'common' }),
|
|
53
52
|
centered: true,
|
|
54
53
|
okButtonProps: { danger: true },
|
|
55
|
-
okText: t('ok', { ns: 'common' }),
|
|
56
54
|
onOk: () => {
|
|
57
55
|
resetSettings();
|
|
58
56
|
form.setFieldsValue(DEFAULT_SETTINGS);
|
|
@@ -63,12 +61,10 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
|
|
|
63
61
|
|
|
64
62
|
const handleClear = useCallback(() => {
|
|
65
63
|
modal.confirm({
|
|
66
|
-
cancelText: t('cancel', { ns: 'common' }),
|
|
67
64
|
centered: true,
|
|
68
65
|
okButtonProps: {
|
|
69
66
|
danger: true,
|
|
70
67
|
},
|
|
71
|
-
okText: t('ok', { ns: 'common' }),
|
|
72
68
|
onOk: async () => {
|
|
73
69
|
await clearSessions();
|
|
74
70
|
await removeAllPlugins();
|
|
@@ -136,20 +132,23 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
|
|
|
136
132
|
<SliderWithInput
|
|
137
133
|
marks={{
|
|
138
134
|
12: {
|
|
139
|
-
label:
|
|
135
|
+
label: 'A',
|
|
140
136
|
style: {
|
|
137
|
+
fontSize: 12,
|
|
141
138
|
marginTop: 4,
|
|
142
139
|
},
|
|
143
140
|
},
|
|
144
141
|
14: {
|
|
145
142
|
label: t('settingTheme.fontSize.marks.normal'),
|
|
146
143
|
style: {
|
|
144
|
+
fontSize: 14,
|
|
147
145
|
marginTop: 4,
|
|
148
146
|
},
|
|
149
147
|
},
|
|
150
148
|
18: {
|
|
151
|
-
label:
|
|
149
|
+
label: 'A',
|
|
152
150
|
style: {
|
|
151
|
+
fontSize: 18,
|
|
153
152
|
marginTop: 4,
|
|
154
153
|
},
|
|
155
154
|
},
|
package/src/const/locale.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeLocale, supportLocales } from '@/locales/resources';
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_LANG = 'en-US';
|
|
4
4
|
export const LOBE_LOCALE_COOKIE = 'LOBE_LOCALE';
|
|
@@ -7,6 +7,6 @@ export const LOBE_LOCALE_COOKIE = 'LOBE_LOCALE';
|
|
|
7
7
|
* Check if the language is supported
|
|
8
8
|
* @param locale
|
|
9
9
|
*/
|
|
10
|
-
export const isLocaleNotSupport = (locale:
|
|
10
|
+
export const isLocaleNotSupport = (locale: string) => {
|
|
11
11
|
return normalizeLocale(locale) === DEFAULT_LANG || !supportLocales.includes(locale);
|
|
12
12
|
};
|
|
@@ -1,24 +1,39 @@
|
|
|
1
1
|
import { ConfigProvider } from 'antd';
|
|
2
|
-
import { PropsWithChildren, memo, useState } from 'react';
|
|
2
|
+
import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
3
3
|
import useSWR from 'swr';
|
|
4
4
|
|
|
5
5
|
import { createI18nNext } from '@/locales/create';
|
|
6
|
+
import { normalizeLocale } from '@/locales/resources';
|
|
6
7
|
import { useOnFinishHydrationGlobal } from '@/store/global';
|
|
7
8
|
import { isOnServerSide } from '@/utils/env';
|
|
8
9
|
import { switchLang } from '@/utils/switchLang';
|
|
9
10
|
|
|
11
|
+
const getAntdLocale = async (lang?: string) => {
|
|
12
|
+
let normalLang = normalizeLocale(lang);
|
|
13
|
+
|
|
14
|
+
// due to antd only have ar-EG locale, we need to convert ar to ar-EG
|
|
15
|
+
// refs: https://ant.design/docs/react/i18n
|
|
16
|
+
|
|
17
|
+
// And we don't want to handle it in `normalizeLocale` function
|
|
18
|
+
// because of other locale files are all `ar` not `ar-EG`
|
|
19
|
+
if (normalLang === 'ar') normalLang = 'ar-EG';
|
|
20
|
+
|
|
21
|
+
const { default: locale } = await import(`antd/locale/${normalLang.replace('-', '_')}.js`);
|
|
22
|
+
|
|
23
|
+
return locale;
|
|
24
|
+
};
|
|
25
|
+
|
|
10
26
|
interface LocaleLayoutProps extends PropsWithChildren {
|
|
11
|
-
|
|
27
|
+
defaultLang?: string;
|
|
12
28
|
}
|
|
13
29
|
|
|
14
|
-
const Locale = memo<LocaleLayoutProps>(({ children,
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
);
|
|
21
|
-
const [i18n] = useState(createI18nNext(lang));
|
|
30
|
+
const Locale = memo<LocaleLayoutProps>(({ children, defaultLang }) => {
|
|
31
|
+
const [i18n] = useState(createI18nNext(defaultLang));
|
|
32
|
+
const [lang, setLang] = useState(defaultLang);
|
|
33
|
+
|
|
34
|
+
const { data: locale } = useSWR(['antd-locale', lang], ([, key]) => getAntdLocale(key), {
|
|
35
|
+
revalidateOnFocus: false,
|
|
36
|
+
});
|
|
22
37
|
|
|
23
38
|
// if run on server side, init i18n instance everytime
|
|
24
39
|
if (isOnServerSide) {
|
|
@@ -38,6 +53,18 @@ const Locale = memo<LocaleLayoutProps>(({ children, lang }) => {
|
|
|
38
53
|
}
|
|
39
54
|
}, []);
|
|
40
55
|
|
|
56
|
+
// handle i18n instance language change
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const handleLang = (e: string) => {
|
|
59
|
+
setLang(e);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
i18n.instance.on('languageChanged', handleLang);
|
|
63
|
+
return () => {
|
|
64
|
+
i18n.instance.off('languageChanged', handleLang);
|
|
65
|
+
};
|
|
66
|
+
}, [i18n]);
|
|
67
|
+
|
|
41
68
|
return <ConfigProvider locale={locale}>{children}</ConfigProvider>;
|
|
42
69
|
});
|
|
43
70
|
|
|
@@ -48,7 +48,7 @@ interface GlobalLayoutProps extends AppThemeProps {
|
|
|
48
48
|
|
|
49
49
|
const GlobalLayout = ({ children, defaultLang, ...theme }: GlobalLayoutProps) => (
|
|
50
50
|
<AppTheme {...theme}>
|
|
51
|
-
<Locale
|
|
51
|
+
<Locale defaultLang={defaultLang}>
|
|
52
52
|
<StoreHydration />
|
|
53
53
|
<Container>{children}</Container>
|
|
54
54
|
<DebugUI />
|
package/src/locales/resources.ts
CHANGED
|
@@ -19,7 +19,9 @@ export const locales = [
|
|
|
19
19
|
export type DefaultResources = typeof resources;
|
|
20
20
|
export type Locales = (typeof locales)[number];
|
|
21
21
|
|
|
22
|
-
export const normalizeLocale = (locale
|
|
22
|
+
export const normalizeLocale = (locale?: string) => {
|
|
23
|
+
if (!locale) return 'en-US';
|
|
24
|
+
|
|
23
25
|
switch (locale) {
|
|
24
26
|
case 'zh-CN':
|
|
25
27
|
case 'zh': {
|
|
@@ -43,8 +45,8 @@ type LocaleOptions = {
|
|
|
43
45
|
|
|
44
46
|
export const localeOptions: LocaleOptions = [
|
|
45
47
|
{
|
|
46
|
-
label: '
|
|
47
|
-
value: '
|
|
48
|
+
label: 'English',
|
|
49
|
+
value: 'en-US',
|
|
48
50
|
},
|
|
49
51
|
{
|
|
50
52
|
label: '简体中文',
|
|
@@ -54,10 +56,6 @@ export const localeOptions: LocaleOptions = [
|
|
|
54
56
|
label: '繁體中文',
|
|
55
57
|
value: 'zh-TW',
|
|
56
58
|
},
|
|
57
|
-
{
|
|
58
|
-
label: 'English',
|
|
59
|
-
value: 'en-US',
|
|
60
|
-
},
|
|
61
59
|
{
|
|
62
60
|
label: '日本語',
|
|
63
61
|
value: 'ja-JP',
|
|
@@ -74,6 +72,10 @@ export const localeOptions: LocaleOptions = [
|
|
|
74
72
|
label: 'Español',
|
|
75
73
|
value: 'es-ES',
|
|
76
74
|
},
|
|
75
|
+
{
|
|
76
|
+
label: 'العربية',
|
|
77
|
+
value: 'ar',
|
|
78
|
+
},
|
|
77
79
|
{
|
|
78
80
|
label: 'Français',
|
|
79
81
|
value: 'fr-FR',
|