mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix: 使用 client 加载 i18n 以解决 nextjs 集成问题 (#10)
* 🐛 fix: 尝试使用原始 i18n 加载方式 * 🏷️ chore: fix type * 🔥 chore: clean file * ♻️ refactor: 清理项目,优化路径依赖关系 * 🚨 chore: fix lint
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
const i18n = require('./.i18nrc');
|
||||
|
||||
/** @type {import('next-i18next').UserConfig} */
|
||||
module.exports = {
|
||||
//debug: process.env.NODE_ENV === 'development',
|
||||
fallbackLng: {
|
||||
default: ['zh_CN'],
|
||||
zh_TW: ['zh_CN'],
|
||||
},
|
||||
i18n: {
|
||||
defaultLocale: i18n.entryLocale,
|
||||
locales: [i18n.entryLocale, ...i18n.outputLocales],
|
||||
},
|
||||
localePath:
|
||||
typeof window === 'undefined' ? require('node:path').resolve('./', i18n.output) : '/locales',
|
||||
react: { useSuspense: false },
|
||||
reloadOnPrerender: process.env.NODE_ENV === 'development',
|
||||
strictMode: true,
|
||||
};
|
||||
@@ -1,10 +1,7 @@
|
||||
import i18nConfig from './next-i18next.config.js';
|
||||
|
||||
const API_END_PORT_URL = process.env.API_END_PORT_URL || '';
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
i18n: i18nConfig.i18n,
|
||||
reactStrictMode: true,
|
||||
pageExtensions: ['page.tsx', 'api.ts'],
|
||||
transpilePackages: ['@lobehub/ui', 'antd-style'],
|
||||
|
||||
@@ -77,8 +77,7 @@
|
||||
"lodash-es": "^4",
|
||||
"lucide-react": "latest",
|
||||
"nanoid": "^4",
|
||||
"next": "^13",
|
||||
"next-i18next": "^14",
|
||||
"next": "13.4.7",
|
||||
"openai-edge": "^1",
|
||||
"polished": "^4",
|
||||
"react": "^18",
|
||||
@@ -110,15 +109,18 @@
|
||||
"commitlint": "^17",
|
||||
"eslint": "^8",
|
||||
"husky": "^8",
|
||||
"i18next-browser-languagedetector": "^7",
|
||||
"i18next-resources-for-ts": "^1",
|
||||
"jsdom": "^22",
|
||||
"lint-staged": "^13",
|
||||
"next-pwa": "^5",
|
||||
"node-fetch": "^3",
|
||||
"postcss-styled-syntax": "^0.4",
|
||||
"prettier": "^2",
|
||||
"remark": "^14",
|
||||
"remark-cli": "^11",
|
||||
"semantic-release": "^21",
|
||||
"semantic-release-config-gitmoji": "^1",
|
||||
"stylelint": "^15",
|
||||
"typescript": "^5",
|
||||
"vitest": "latest"
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"advanceSettings": "Advanced Settings",
|
||||
"agentAvatar": "Avatar",
|
||||
"agentDescription": "Description",
|
||||
"agentDescriptionPlaceholder": "Please enter a description",
|
||||
"agentModel": "Model",
|
||||
"agentName": "Name",
|
||||
"agentNamePlaceholder": "Please enter a name",
|
||||
"agentProfile": "Agent Profile",
|
||||
"agentPrompt": "AI Prompt",
|
||||
"agentPromptPlaceholder": "Please enter AI prompt",
|
||||
"agentTag": "Tag",
|
||||
"agentTagPlaceholder": "Please enter a tag",
|
||||
"archive": "Archive",
|
||||
"autoGenerate": "Auto Generate",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"confirmRemoveSessionItemAlert": "You are about to remove this agent. Once removed, it cannot be recovered. Please confirm your action.",
|
||||
"defaultAgent": "Default Agent",
|
||||
"edit": "Edit",
|
||||
"editAgentProfile": "Edit Agent Profile",
|
||||
"export": "Export",
|
||||
"gpt-3.5-turbo": "GPT 3.5 Turbo",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 Turbo (16K)",
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4-32k": "GPT 4 (32K)",
|
||||
"modelConfig": "Model Configuration",
|
||||
"modelTemperature": "Temperature",
|
||||
"newAgent": "New Agent",
|
||||
"noDescription": "No description",
|
||||
"ok": "OK",
|
||||
"profile": "Profile",
|
||||
"reset": "Reset",
|
||||
"searchAgentPlaceholder": "Search agents and conversations...",
|
||||
"sessionSetting": "Session Setting",
|
||||
"setting": "Setting",
|
||||
"share": "Share",
|
||||
"updateAgent": "Update Agent",
|
||||
"updatePrompt": "Update Prompt"
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
{
|
||||
"danger": {
|
||||
"reset": {
|
||||
"title": "Reset All Settings",
|
||||
"desc": "Reset all settings to default values",
|
||||
"action": "Reset Now",
|
||||
"confirm": "Confirm reset all settings?",
|
||||
"currentVersion": "Current Version"
|
||||
},
|
||||
"clear": {
|
||||
"title": "Clear All Data",
|
||||
"desc": "Clear all chat and settings data",
|
||||
"action": "Clear Now",
|
||||
"confirm": "Confirm clear all chat and settings data?"
|
||||
}
|
||||
},
|
||||
"header": "Settings",
|
||||
"settingChat": {
|
||||
"title": "Chat Settings",
|
||||
"inputTemplate": {
|
||||
"title": "User Input Template",
|
||||
"desc": "The latest user message will be filled into this template"
|
||||
},
|
||||
"compressThreshold": {
|
||||
"title": "History Message Length Compression Threshold",
|
||||
"desc": "When the uncompressed history message exceeds this value, it will be compressed"
|
||||
},
|
||||
"historyCount": {
|
||||
"title": "Number of History Messages",
|
||||
"desc": "Number of history messages carried in each request"
|
||||
},
|
||||
"maxTokens": {
|
||||
"title": "Max Tokens per Response",
|
||||
"desc": "The maximum number of tokens used for each interaction"
|
||||
},
|
||||
"sendKey": {
|
||||
"title": "Send Key"
|
||||
}
|
||||
},
|
||||
"settingModel": {
|
||||
"title": "Model Settings",
|
||||
"model": {
|
||||
"title": "Model"
|
||||
},
|
||||
"temperature": {
|
||||
"title": "Randomness (temperature)",
|
||||
"desc": "The higher the value, the more random the response"
|
||||
},
|
||||
"topP": {
|
||||
"title": "Nucleus Sampling (top_p)",
|
||||
"desc": "Similar to randomness, but do not change it together with randomness"
|
||||
},
|
||||
"presencePenalty": {
|
||||
"title": "Topic Freshness (presence_penalty)",
|
||||
"desc": "The higher the value, the more likely it is to expand to new topics"
|
||||
},
|
||||
"frequencyPenalty": {
|
||||
"title": "Frequency Penalty (frequency_penalty)",
|
||||
"desc": "The higher the value, the more likely it is to reduce repeated words"
|
||||
}
|
||||
},
|
||||
"settingOpenAI": {
|
||||
"title": "OpenAI Settings",
|
||||
"token": {
|
||||
"title": "API Key",
|
||||
"desc": "Use your own key to bypass password access restrictions",
|
||||
"placeholder": "OpenAI API Key"
|
||||
},
|
||||
"endpoint": {
|
||||
"title": "API Endpoint",
|
||||
"desc": "In addition to the default address, it must include http(s)://"
|
||||
}
|
||||
},
|
||||
"settingSystem": {
|
||||
"title": "System Settings",
|
||||
"accessCode": {
|
||||
"title": "Access Code",
|
||||
"desc": "Encryption access has been enabled by the administrator",
|
||||
"placeholder": "Please enter the access code"
|
||||
}
|
||||
},
|
||||
"settingTheme": {
|
||||
"title": "Theme Settings",
|
||||
"avatar": {
|
||||
"title": "Avatar",
|
||||
"desc": "Supports URL / Base64 / Emoji"
|
||||
},
|
||||
"fontSize": {
|
||||
"title": "Font Size",
|
||||
"desc": "Font size of chat content"
|
||||
},
|
||||
"lang": {
|
||||
"name": "Language Settings",
|
||||
"all": "All Languages"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"advanceSettings": "高级设置",
|
||||
"agentAvatar": "头像",
|
||||
"agentDescription": "描述",
|
||||
"agentDescriptionPlaceholder": "请输入描述",
|
||||
"agentModel": "模型",
|
||||
"agentName": "名称",
|
||||
"agentNamePlaceholder": "请输入名称",
|
||||
"agentProfile": "助手信息",
|
||||
"agentPrompt": "提示词",
|
||||
"agentPromptPlaceholder": "请输入 AI 提示词",
|
||||
"agentTag": "标签",
|
||||
"agentTagPlaceholder": "请输入标签",
|
||||
"archive": "归档",
|
||||
"autoGenerate": "自动补全",
|
||||
"cancel": "取消",
|
||||
"close": "关闭",
|
||||
"confirmRemoveSessionItemAlert": "即将删除该助手,删除后该将无法找回,请确认你的操作",
|
||||
"defaultAgent": "默认助手",
|
||||
"edit": "编辑",
|
||||
"editAgentProfile": "编辑助手信息",
|
||||
"export": "导出",
|
||||
"gpt-3.5-turbo": "GPT 3.5",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 (16K)",
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4-32k": "GPT 4 (32K)",
|
||||
"modelConfig": "模型配置",
|
||||
"modelTemperature": "发散度",
|
||||
"newAgent": "新建助手",
|
||||
"noDescription": "暂无描述",
|
||||
"ok": "确定",
|
||||
"profile": "身份卡",
|
||||
"reset": "重置",
|
||||
"searchAgentPlaceholder": "搜索助手和对话...",
|
||||
"sessionSetting": "会话设置",
|
||||
"setting": "设置",
|
||||
"share": "分享",
|
||||
"updateAgent": "更新助理信息",
|
||||
"updatePrompt": "更新提示词"
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
{
|
||||
"danger": {
|
||||
"reset": {
|
||||
"title": "重置所有设置",
|
||||
"desc": "重置所有设置项回默认值",
|
||||
"action": "立即重置",
|
||||
"confirm": "确认重置所有设置?",
|
||||
"currentVersion": "当前版本"
|
||||
},
|
||||
"clear": {
|
||||
"title": "清除所有数据",
|
||||
"desc": "清除所有聊天、设置数据",
|
||||
"action": "立即清除",
|
||||
"confirm": "确认清除所有聊天、设置数据?"
|
||||
}
|
||||
},
|
||||
|
||||
"header": "设置",
|
||||
"settingChat": {
|
||||
"title": "聊天设置",
|
||||
"inputTemplate": {
|
||||
"title": "用户输入预处理",
|
||||
"desc": "用户最新的一条消息会填充到此模板"
|
||||
},
|
||||
"compressThreshold": {
|
||||
"title": "历史消息长度压缩阈值",
|
||||
"desc": "当未压缩的历史消息超过该值时,将进行压缩"
|
||||
},
|
||||
"historyCount": {
|
||||
"title": "附带历史消息数",
|
||||
"desc": "每次请求携带的历史消息数"
|
||||
},
|
||||
"maxTokens": {
|
||||
"title": "单次回复限制 (max_tokens)",
|
||||
"desc": "单次交互所用的最大 Token 数"
|
||||
},
|
||||
"sendKey": {
|
||||
"title": "发送键"
|
||||
}
|
||||
},
|
||||
"settingModel": {
|
||||
"title": "模型设置",
|
||||
"model": {
|
||||
"title": "模型"
|
||||
},
|
||||
"temperature": {
|
||||
"title": "随机性 (temperature)",
|
||||
"desc": "值越大,回复越随机"
|
||||
},
|
||||
"topP": {
|
||||
"title": "核采样 (top_p)",
|
||||
"desc": "与随机性类似,但不要和随机性一起更改"
|
||||
},
|
||||
"presencePenalty": {
|
||||
"title": "话题新鲜度 (presence_penalty)",
|
||||
"desc": "值越大,越有可能扩展到新话题"
|
||||
},
|
||||
"frequencyPenalty": {
|
||||
"title": "频率惩罚度 (frequency_penalty)",
|
||||
"desc": "值越大,越有可能降低重复字词"
|
||||
}
|
||||
},
|
||||
"settingOpenAI": {
|
||||
"title": "OpenAI 设置",
|
||||
"token": {
|
||||
"title": "API Key",
|
||||
"desc": "使用自己的 Key 可绕过密码访问限制",
|
||||
"placeholder": "OpenAI API Key"
|
||||
},
|
||||
"endpoint": {
|
||||
"title": "接口地址",
|
||||
"desc": "除默认地址外,必须包含 http(s)://"
|
||||
}
|
||||
},
|
||||
"settingSystem": {
|
||||
"title": "系统设置",
|
||||
"accessCode": {
|
||||
"title": "访问密码",
|
||||
"desc": "管理员已开启加密访问",
|
||||
"placeholder": "请输入访问密码"
|
||||
}
|
||||
},
|
||||
"settingTheme": {
|
||||
"title": "主题设置",
|
||||
"avatar": {
|
||||
"title": "头像",
|
||||
"desc": "支持 URL / Base64 / Emoji 表情符号"
|
||||
},
|
||||
"fontSize": {
|
||||
"title": "字体大小",
|
||||
"desc": "聊天内容的字体大小"
|
||||
},
|
||||
"lang": {
|
||||
"name": "语言设置",
|
||||
"all": "所有语言"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,17 @@ import { useSessionStore } from '@/store/session';
|
||||
import { useSettings } from '@/store/settings';
|
||||
import { GlobalStyle } from '@/styles';
|
||||
|
||||
import i18n from '../locales';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Layout = ({ children }: PropsWithChildren) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
// 用一种比较奇怪的方式import 了 18n
|
||||
i18n.finally(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ConfigProvider locale={Zh_CN}>
|
||||
<App className={styles.bg}>{children}</App>
|
||||
|
||||
50
src/locales/create.ts
Normal file
50
src/locales/create.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import i18n from 'i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import { commonLocaleSet } from './namespaces';
|
||||
|
||||
interface LocalSet {
|
||||
['en-US']: Record<string, any>;
|
||||
['zh-CN']: Record<string, any>;
|
||||
}
|
||||
|
||||
interface I18NOptions {
|
||||
localSet: LocalSet;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
export const createI18nNext = (options: I18NOptions) => {
|
||||
// 将语言包合并
|
||||
const resources = {
|
||||
'en-US': {
|
||||
common: commonLocaleSet['en-US'],
|
||||
[options.namespace]: options.localSet['en-US'],
|
||||
},
|
||||
'zh-CN': {
|
||||
common: commonLocaleSet['zh-CN'],
|
||||
[options.namespace]: options.localSet['zh-CN'],
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
i18n
|
||||
// detect user language
|
||||
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
.use(LanguageDetector)
|
||||
// pass the i18n instance to react-i18next.
|
||||
.use(initReactI18next)
|
||||
// init i18next
|
||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
.init({
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
defaultNS: [options.namespace, 'common'],
|
||||
fallbackLng: 'zh-CN',
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
ns: [options.namespace, 'common'],
|
||||
resources,
|
||||
})
|
||||
);
|
||||
};
|
||||
41
src/locales/en_US/common.ts
Normal file
41
src/locales/en_US/common.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
export default {
|
||||
'advanceSettings': 'Advanced Settings',
|
||||
'agentAvatar': 'Avatar',
|
||||
'agentDescription': 'Description',
|
||||
'agentDescriptionPlaceholder': 'Please enter a description',
|
||||
'agentModel': 'Model',
|
||||
'agentName': 'Name',
|
||||
'agentNamePlaceholder': 'Please enter a name',
|
||||
'agentProfile': 'Agent Profile',
|
||||
'agentPrompt': 'AI Prompt',
|
||||
'agentPromptPlaceholder': 'Please enter AI prompt',
|
||||
'agentTag': 'Tag',
|
||||
'agentTagPlaceholder': 'Please enter a tag',
|
||||
'archive': 'Archive',
|
||||
'autoGenerate': 'Auto Generate',
|
||||
'cancel': 'Cancel',
|
||||
'close': 'Close',
|
||||
'confirmRemoveSessionItemAlert':
|
||||
'You are about to remove this agent. Once removed, it cannot be recovered. Please confirm your action.',
|
||||
'defaultAgent': 'Default Agent',
|
||||
'edit': 'Edit',
|
||||
'editAgentProfile': 'Edit Agent Profile',
|
||||
'export': 'Export',
|
||||
'gpt-3.5-turbo': 'GPT 3.5 Turbo',
|
||||
'gpt-3.5-turbo-16k': 'GPT 3.5 Turbo (16K)',
|
||||
'gpt-4': 'GPT 4',
|
||||
'gpt-4-32k': 'GPT 4 (32K)',
|
||||
'modelConfig': 'Model Configuration',
|
||||
'modelTemperature': 'Temperature',
|
||||
'newAgent': 'New Agent',
|
||||
'noDescription': 'No description',
|
||||
'ok': 'OK',
|
||||
'profile': 'Profile',
|
||||
'reset': 'Reset',
|
||||
'searchAgentPlaceholder': 'Search agents and conversations...',
|
||||
'sessionSetting': 'Session Setting',
|
||||
'setting': 'Setting',
|
||||
'share': 'Share',
|
||||
'updateAgent': 'Update Agent',
|
||||
'updatePrompt': 'Update Prompt',
|
||||
};
|
||||
97
src/locales/en_US/setting.ts
Normal file
97
src/locales/en_US/setting.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
export default {
|
||||
danger: {
|
||||
clear: {
|
||||
action: 'Clear Now',
|
||||
confirm: 'Confirm clear all chat and settings data?',
|
||||
desc: 'Clear all chat and settings data',
|
||||
title: 'Clear All Data',
|
||||
},
|
||||
reset: {
|
||||
action: 'Reset Now',
|
||||
confirm: 'Confirm reset all settings?',
|
||||
currentVersion: 'Current Version',
|
||||
desc: 'Reset all settings to default values',
|
||||
title: 'Reset All Settings',
|
||||
},
|
||||
},
|
||||
header: 'Settings',
|
||||
settingChat: {
|
||||
compressThreshold: {
|
||||
desc: 'When the uncompressed history message exceeds this value, it will be compressed',
|
||||
title: 'History Message Length Compression Threshold',
|
||||
},
|
||||
historyCount: {
|
||||
desc: 'Number of history messages carried in each request',
|
||||
title: 'Number of History Messages',
|
||||
},
|
||||
inputTemplate: {
|
||||
desc: 'The latest user message will be filled into this template',
|
||||
title: 'User Input Template',
|
||||
},
|
||||
maxTokens: {
|
||||
desc: 'The maximum number of tokens used for each interaction',
|
||||
title: 'Max Tokens per Response',
|
||||
},
|
||||
sendKey: {
|
||||
title: 'Send Key',
|
||||
},
|
||||
title: 'Chat Settings',
|
||||
},
|
||||
settingModel: {
|
||||
frequencyPenalty: {
|
||||
desc: 'The higher the value, the more likely it is to reduce repeated words',
|
||||
title: 'Frequency Penalty (frequency_penalty)',
|
||||
},
|
||||
model: {
|
||||
title: 'Model',
|
||||
},
|
||||
presencePenalty: {
|
||||
desc: 'The higher the value, the more likely it is to expand to new topics',
|
||||
title: 'Topic Freshness (presence_penalty)',
|
||||
},
|
||||
temperature: {
|
||||
desc: 'The higher the value, the more random the response',
|
||||
title: 'Randomness (temperature)',
|
||||
},
|
||||
title: 'Model Settings',
|
||||
topP: {
|
||||
desc: 'Similar to randomness, but do not change it together with randomness',
|
||||
title: 'Nucleus Sampling (top_p)',
|
||||
},
|
||||
},
|
||||
settingOpenAI: {
|
||||
endpoint: {
|
||||
desc: 'In addition to the default address, it must include http(s)://',
|
||||
title: 'API Endpoint',
|
||||
},
|
||||
title: 'OpenAI Settings',
|
||||
token: {
|
||||
desc: 'Use your own key to bypass password access restrictions',
|
||||
placeholder: 'OpenAI API Key',
|
||||
title: 'API Key',
|
||||
},
|
||||
},
|
||||
settingSystem: {
|
||||
accessCode: {
|
||||
desc: 'Encryption access has been enabled by the administrator',
|
||||
placeholder: 'Please enter the access code',
|
||||
title: 'Access Code',
|
||||
},
|
||||
title: 'System Settings',
|
||||
},
|
||||
settingTheme: {
|
||||
avatar: {
|
||||
desc: 'Supports URL / Base64 / Emoji',
|
||||
title: 'Avatar',
|
||||
},
|
||||
fontSize: {
|
||||
desc: 'Font size of chat content',
|
||||
title: 'Font Size',
|
||||
},
|
||||
lang: {
|
||||
all: 'All Languages',
|
||||
name: 'Language Settings',
|
||||
},
|
||||
title: 'Theme Settings',
|
||||
},
|
||||
};
|
||||
6
src/locales/index.ts
Normal file
6
src/locales/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createI18nNext } from './create';
|
||||
import { commonLocaleSet } from './namespaces';
|
||||
|
||||
const initI18n = createI18nNext({ localSet: commonLocaleSet, namespace: 'common' });
|
||||
|
||||
export default initI18n;
|
||||
16
src/locales/namespaces.ts
Normal file
16
src/locales/namespaces.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import enCommon from './en_US/common';
|
||||
import settingEN from './en_US/setting';
|
||||
import zhCommon from './zh_CN/common';
|
||||
import settingZH from './zh_CN/setting';
|
||||
|
||||
export const commonLocaleSet = {
|
||||
'en-US': enCommon,
|
||||
'zh-CN': zhCommon,
|
||||
};
|
||||
|
||||
export const settingsLocaleSet = {
|
||||
'en-US': settingEN,
|
||||
'zh-CN': settingZH,
|
||||
};
|
||||
|
||||
export type Language = 'zh-CN' | 'en-US';
|
||||
40
src/locales/zh_CN/common.ts
Normal file
40
src/locales/zh_CN/common.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
export default {
|
||||
'advanceSettings': '高级设置',
|
||||
'agentAvatar': '头像',
|
||||
'agentDescription': '描述',
|
||||
'agentDescriptionPlaceholder': '请输入描述',
|
||||
'agentModel': '模型',
|
||||
'agentName': '名称',
|
||||
'agentNamePlaceholder': '请输入名称',
|
||||
'agentProfile': '助手信息',
|
||||
'agentPrompt': '提示词',
|
||||
'agentPromptPlaceholder': '请输入 AI 提示词',
|
||||
'agentTag': '标签',
|
||||
'agentTagPlaceholder': '请输入标签',
|
||||
'archive': '归档',
|
||||
'autoGenerate': '自动补全',
|
||||
'cancel': '取消',
|
||||
'close': '关闭',
|
||||
'confirmRemoveSessionItemAlert': '即将删除该助手,删除后该将无法找回,请确认你的操作',
|
||||
'defaultAgent': '默认助手',
|
||||
'edit': '编辑',
|
||||
'editAgentProfile': '编辑助手信息',
|
||||
'export': '导出',
|
||||
'gpt-3.5-turbo': 'GPT 3.5',
|
||||
'gpt-3.5-turbo-16k': 'GPT 3.5 (16K)',
|
||||
'gpt-4': 'GPT 4',
|
||||
'gpt-4-32k': 'GPT 4 (32K)',
|
||||
'modelConfig': '模型配置',
|
||||
'modelTemperature': '发散度',
|
||||
'newAgent': '新建助手',
|
||||
'noDescription': '暂无描述',
|
||||
'ok': '确定',
|
||||
'profile': '身份卡',
|
||||
'reset': '重置',
|
||||
'searchAgentPlaceholder': '搜索助手和对话...',
|
||||
'sessionSetting': '会话设置',
|
||||
'setting': '设置',
|
||||
'share': '分享',
|
||||
'updateAgent': '更新助理信息',
|
||||
'updatePrompt': '更新提示词',
|
||||
};
|
||||
97
src/locales/zh_CN/setting.ts
Normal file
97
src/locales/zh_CN/setting.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
export default {
|
||||
danger: {
|
||||
clear: {
|
||||
action: '立即清除',
|
||||
confirm: '确认清除所有聊天、设置数据?',
|
||||
desc: '清除所有聊天、设置数据',
|
||||
title: '清除所有数据',
|
||||
},
|
||||
reset: {
|
||||
action: '立即重置',
|
||||
confirm: '确认重置所有设置?',
|
||||
currentVersion: '当前版本',
|
||||
desc: '重置所有设置项回默认值',
|
||||
title: '重置所有设置',
|
||||
},
|
||||
},
|
||||
header: '设置',
|
||||
settingChat: {
|
||||
compressThreshold: {
|
||||
desc: '当未压缩的历史消息超过该值时,将进行压缩',
|
||||
title: '历史消息长度压缩阈值',
|
||||
},
|
||||
historyCount: {
|
||||
desc: '每次请求携带的历史消息数',
|
||||
title: '附带历史消息数',
|
||||
},
|
||||
inputTemplate: {
|
||||
desc: '用户最新的一条消息会填充到此模板',
|
||||
title: '用户输入预处理',
|
||||
},
|
||||
maxTokens: {
|
||||
desc: '单次交互所用的最大 Token 数',
|
||||
title: '单次回复限制 (max_tokens)',
|
||||
},
|
||||
sendKey: {
|
||||
title: '发送键',
|
||||
},
|
||||
title: '聊天设置',
|
||||
},
|
||||
settingModel: {
|
||||
frequencyPenalty: {
|
||||
desc: '值越大,越有可能降低重复字词',
|
||||
title: '频率惩罚度 (frequency_penalty)',
|
||||
},
|
||||
model: {
|
||||
title: '模型',
|
||||
},
|
||||
presencePenalty: {
|
||||
desc: '值越大,越有可能扩展到新话题',
|
||||
title: '话题新鲜度 (presence_penalty)',
|
||||
},
|
||||
temperature: {
|
||||
desc: '值越大,回复越随机',
|
||||
title: '随机性 (temperature)',
|
||||
},
|
||||
title: '模型设置',
|
||||
topP: {
|
||||
desc: '与随机性类似,但不要和随机性一起更改',
|
||||
title: '核采样 (top_p)',
|
||||
},
|
||||
},
|
||||
settingOpenAI: {
|
||||
endpoint: {
|
||||
desc: '除默认地址外,必须包含 http(s)://',
|
||||
title: '接口地址',
|
||||
},
|
||||
title: 'OpenAI 设置',
|
||||
token: {
|
||||
desc: '使用自己的 Key 可绕过密码访问限制',
|
||||
placeholder: 'OpenAI API Key',
|
||||
title: 'API Key',
|
||||
},
|
||||
},
|
||||
settingSystem: {
|
||||
accessCode: {
|
||||
desc: '管理员已开启加密访问',
|
||||
placeholder: '请输入访问密码',
|
||||
title: '访问密码',
|
||||
},
|
||||
title: '系统设置',
|
||||
},
|
||||
settingTheme: {
|
||||
avatar: {
|
||||
desc: '支持 URL / Base64 / Emoji 表情符号',
|
||||
title: '头像',
|
||||
},
|
||||
fontSize: {
|
||||
desc: '聊天内容的字体大小',
|
||||
title: '字体大小',
|
||||
},
|
||||
lang: {
|
||||
all: '所有语言',
|
||||
name: '语言设置',
|
||||
},
|
||||
title: '主题设置',
|
||||
},
|
||||
};
|
||||
@@ -1,15 +1,13 @@
|
||||
import { Analytics } from '@vercel/analytics/react';
|
||||
import { appWithTranslation } from 'next-i18next';
|
||||
import type { AppProps } from 'next/app';
|
||||
|
||||
import i18nConfig from '@/../next-i18next.config';
|
||||
import Layout from '@/layout';
|
||||
|
||||
export default appWithTranslation(({ Component, pageProps }: AppProps) => {
|
||||
export default ({ Component, pageProps }: AppProps) => {
|
||||
return (
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
<Analytics />
|
||||
</Layout>
|
||||
);
|
||||
}, i18nConfig);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { StyleProvider, extractStaticStyle } from 'antd-style';
|
||||
import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document';
|
||||
|
||||
import i18nextConfig from '../../next-i18next.config.js';
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getStaticProps(ctx: DocumentContext) {
|
||||
const page = await ctx.renderPage({
|
||||
@@ -30,9 +28,8 @@ class MyDocument extends Document {
|
||||
}
|
||||
|
||||
render() {
|
||||
const currentLocale = this.props.__NEXT_DATA__.locale ?? i18nextConfig.i18n.defaultLocale;
|
||||
return (
|
||||
<Html lang={currentLocale}>
|
||||
<Html>
|
||||
<Head>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/apple-touch-icon.png"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { MessageSquarePlus } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Link from 'next/link';
|
||||
import { memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ActionIcon, Avatar, List } from '@lobehub/ui';
|
||||
import { Popconfirm } from 'antd';
|
||||
import { X } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FC, memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ActionIcon, DraggablePanel, DraggablePanelContainer } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { LucideEdit, LucideX } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Router from 'next/router';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ActionIcon, Avatar, ChatHeader } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ArchiveIcon, MoreVerticalIcon, Share2Icon } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TextArea } from '@lobehub/ui';
|
||||
import { Collapse, InputNumber, Segmented, Slider } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ActionIcon, Avatar, Input } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { LucideSparkles } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { ChatHeader } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { makeI18nProps } from '@/utils/makeI18nProps';
|
||||
|
||||
import ChatLayout from '../../layout';
|
||||
import AgentConfig from './AgentConfig';
|
||||
import AgentMeta from './AgentMeta';
|
||||
@@ -61,6 +59,4 @@ const EditPage = memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
export { getStaticPaths } from '@/utils/makeI18nProps';
|
||||
export const getStaticProps = makeI18nProps(['common']);
|
||||
export default EditPage;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Head from 'next/head';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { sessionSelectors, useSessionStore } from '@/store/session';
|
||||
import { makeI18nProps } from '@/utils/makeI18nProps';
|
||||
|
||||
import Layout from '../layout';
|
||||
import Config from './Config';
|
||||
@@ -13,16 +11,11 @@ import Conversation from './Conversation';
|
||||
import Header from './Header';
|
||||
|
||||
const Chat = memo(() => {
|
||||
const { i18n } = useTranslation('common', { bindI18n: 'languageChanged loaded' });
|
||||
const [title] = useSessionStore((s) => {
|
||||
const context = sessionSelectors.currentSession(s);
|
||||
return [context?.meta.title];
|
||||
}, isEqual);
|
||||
|
||||
useEffect(() => {
|
||||
i18n.reloadResources(i18n.resolvedLanguage, ['common']);
|
||||
}, []);
|
||||
|
||||
const pageTitle = title ? `${title} - LobeChat` : 'LobeChat';
|
||||
|
||||
return (
|
||||
@@ -44,7 +37,4 @@ const Chat = memo(() => {
|
||||
</Layout>
|
||||
);
|
||||
});
|
||||
|
||||
export { getStaticPaths } from '@/utils/makeI18nProps';
|
||||
export const getStaticProps = makeI18nProps(['common']);
|
||||
export default Chat;
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
import { makeI18nProps } from '@/utils/makeI18nProps';
|
||||
|
||||
export { default } from './[id]/index.page';
|
||||
|
||||
export const getStaticProps = makeI18nProps(['common']);
|
||||
|
||||
@@ -3,13 +3,21 @@ import { PropsWithChildren, memo, useEffect } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { createI18nNext } from '@/locales/create';
|
||||
import { commonLocaleSet } from '@/locales/namespaces';
|
||||
import { useSessionStore } from '@/store/session';
|
||||
import { useSettings } from '@/store/settings';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import { Sessions } from './SessionList';
|
||||
|
||||
const initI18n = createI18nNext({ localSet: commonLocaleSet, namespace: 'common' });
|
||||
|
||||
const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
||||
useEffect(() => {
|
||||
initI18n.finally();
|
||||
}, []);
|
||||
|
||||
const [activeSession] = useSessionStore((s) => {
|
||||
return [s.activeSession];
|
||||
}, shallow);
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default, getStaticProps } from './chat/index.page';
|
||||
export { default } from './chat/index.page';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChatHeader } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Head from 'next/head';
|
||||
import { memo } from 'react';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { createI18nNext } from '@/locales/create';
|
||||
import { settingsLocaleSet } from '@/locales/namespaces';
|
||||
import { Sessions } from '@/pages/chat/SessionList';
|
||||
import { makeI18nProps } from '@/utils/makeI18nProps';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import Header from './Header';
|
||||
import SettingForm from './SettingForm';
|
||||
|
||||
const initI18n = createI18nNext({ localSet: settingsLocaleSet, namespace: 'setting' });
|
||||
|
||||
const SettingLayout = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const pageTitle = `${t('header')} - LobeChat`;
|
||||
|
||||
useEffect(() => {
|
||||
initI18n.finally();
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -32,6 +39,4 @@ const SettingLayout = memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
export const getStaticProps = makeI18nProps(['common', 'setting']);
|
||||
|
||||
export default SettingLayout;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import common from '@/../public/locales/zh_CN/common.json';
|
||||
import setting from '@/../public/locales/zh_CN/setting.json';
|
||||
|
||||
const resources = {
|
||||
common,
|
||||
setting,
|
||||
} as const;
|
||||
|
||||
export default resources;
|
||||
|
||||
export type NS = keyof typeof resources;
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { GetServerSideProps, GetStaticPaths, GetStaticProps } from 'next';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
|
||||
import i18nextConfig from '@/../next-i18next.config';
|
||||
import { NS } from '@/types/resources';
|
||||
|
||||
const isServerReq = (req: any) => !req?.url?.startsWith('/_next');
|
||||
|
||||
export const getI18nProps = async (ctx: any, ns: NS[] = ['common']) => {
|
||||
const locale = ctx?.params?.locale || ctx?.locale || i18nextConfig.i18n.defaultLocale;
|
||||
const req = ctx?.params?.req || ctx?.req;
|
||||
return isServerReq(req) ? await serverSideTranslations(locale, ns) : {};
|
||||
};
|
||||
|
||||
export const makeI18nProps =
|
||||
(ns: NS[] = []): GetStaticProps | GetServerSideProps =>
|
||||
async (ctx: any) => {
|
||||
const req = ctx?.params?.req || ctx?.req;
|
||||
return {
|
||||
props: isServerReq(req) ? await getI18nProps(ctx, ns) : {},
|
||||
revalidate: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
// We don't want to specify all possible countries as we get those from the headers
|
||||
return {
|
||||
fallback: 'blocking',
|
||||
paths: [],
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user