diff --git a/server/admin/app/ra/App.tsx b/server/admin/app/ra/App.tsx index 2358dac1..4b4302ef 100644 --- a/server/admin/app/ra/App.tsx +++ b/server/admin/app/ra/App.tsx @@ -15,7 +15,7 @@ import { Dashboard } from './dashboard'; import { Route } from 'react-router-dom'; import { TailchatNetwork } from './routes/network'; import { TailchatLayout } from './layout'; -import { i18nProvider } from './i18n'; +import { i18nProvider } from './i18n/index'; import { httpClient } from './request'; import { SocketIOAdmin } from './routes/socketio'; @@ -37,31 +37,22 @@ export const App = () => ( i18nProvider={i18nProvider} requireAuth={true} > - + diff --git a/server/admin/app/ra/authProvider.ts b/server/admin/app/ra/authProvider.ts index bb2b3be5..349a9045 100644 --- a/server/admin/app/ra/authProvider.ts +++ b/server/admin/app/ra/authProvider.ts @@ -18,7 +18,7 @@ export const authProvider: AuthProvider = { localStorage.setItem(authStorageKey, JSON.stringify(auth)); }) .catch(() => { - throw new Error('登录失败'); + throw new Error('Login Failed'); }); }, logout: () => { diff --git a/server/admin/app/ra/i18n.ts b/server/admin/app/ra/i18n.ts deleted file mode 100644 index a6cb3315..00000000 --- a/server/admin/app/ra/i18n.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { TranslationMessages } from 'react-admin'; -import _merge from 'lodash/merge'; -import defaultEnglishMessages from 'ra-language-english'; -import polyglotI18nProvider from 'ra-i18n-polyglot'; - -const chineseResources = { - resources: { - users: { - fields: { - id: '用户ID', - email: '邮箱', - avatar: '头像', - username: '用户名', - password: '密码', - nickname: '昵称', - discriminator: '标识符', - temporary: '是否游客', - type: '用户类型', - settings: '用户设置', - createdAt: '创建时间', - }, - }, - }, -}; - -const chineseMessages: TranslationMessages = _merge( - defaultEnglishMessages, - { - ra: { - action: { - add_filter: '增加检索', - add: '增加', - back: '回退', - bulk_actions: '选中%{smart_count}项', - cancel: '取消', - clear_input_value: '清空输入', - clone: '克隆', - confirm: '确认', - create: '新建', - create_item: '新建 %{item}', - delete: '删除', - edit: '编辑', - export: '导出', - list: '列表', - refresh: '刷新', - remove_filter: '移除检索', - remove: '删除', - save: '保存', - search: '检索', - select_all: '选中全部', - select_row: '选中这行', - show: '查看', - sort: '排序', - undo: '撤销', - unselect: '反选', - expand: '展开', - close: '关闭', - open_menu: '打开菜单', - close_menu: '关闭菜单', - update: '更新', - move_up: '上移', - move_down: '下移', - open: '打开', - toggle_theme: '切换主题', - }, - boolean: { - true: '是', - false: '否', - null: ' ', - }, - page: { - create: '新建 %{name}', - dashboard: '概览', - edit: '%{name} #%{id}', - error: '出现错误', - list: '%{name} 列表', - loading: '加载中', - not_found: '未发现', - show: '%{name} #%{id}', - empty: '无 %{name} ', - invite: '要增加吗?', - }, - input: { - file: { - upload_several: '将文件集合拖拽到这里, 或点击这里选择文件集合.', - upload_single: '将文件拖拽到这里, 或点击这里选择文件.', - }, - image: { - upload_several: - '将图片文件集合拖拽到这里, 或点击这里选择图片文件集合.', - upload_single: '将图片文件拖拽到这里, 或点击这里选择图片文件.', - }, - references: { - all_missing: '未找到参考数据.', - many_missing: '至少有一条参考数据不再可用.', - single_missing: '关联的参考数据不再可用.', - }, - password: { - toggle_visible: '隐藏密码', - toggle_hidden: '显示密码', - }, - }, - message: { - about: '关于', - are_you_sure: '您确定操作?', - bulk_delete_title: '删除 %{name} |||| 删除 %{smart_count}项 %{name} ', - bulk_delete_content: - '您确定要删除 %{name}? |||| 您确定要删除 %{smart_count} 项?', - bulk_update_content: - '你确定要更新 %{name}? |||| 你确定想更新 %{smart_count} 项?', - bulk_update_title: - '更新 %{name} |||| 您确定更新 %{smart_count} %{name}', - delete_content: '您确定要删除该条目?', - delete_title: '删除 %{name} #%{id}', - details: '详情', - error: '客户端错误导致请求未完成.', - invalid_form: '表单输入无效. 请检查错误提示', - loading: '正在加载页面, 请稍候', - no: '否', - not_found: '您输入了错误的URL或者错误的链接.', - yes: '是', - unsaved_changes: '修改未保存. 放弃修改吗?', - }, - navigation: { - no_results: '结果为空', - no_more_results: '页码 %{page} 超出边界. 试试上一页.', - page_out_of_boundaries: '页码 %{page} 超出边界', - page_out_from_end: '已到最末页', - page_out_from_begin: '已到最前页', - page_range_info: '%{offsetBegin}-%{offsetEnd} / %{total}', - partial_page_range_info: - '%{offsetBegin}-%{offsetEnd} of more than %{offsetEnd}', - current_page: '页码 %{page}', - page: '跳转到 %{page}', - prev: '向前', - first: '第一页', - last: '最后一页', - next: '向后', - previous: '先前第一页', - page_rows_per_page: '每页行数:', - skip_nav: '跳到内容', - }, - sort: { - sort_by: '按 %{field} %{order}', - ASC: '升序', - DESC: '降序', - }, - auth: { - auth_check_error: '请登录以继续', - user_menu: '设置', - username: '用户名', - password: '密码', - sign_in: '登录', - sign_in_error: '验证失败, 请重试', - logout: '退出', - }, - notification: { - updated: '条目已更新 |||| %{smart_count} 项条目已更新', - created: '条目已新建', - deleted: '条目已删除 |||| %{smart_count} 项条目已删除', - bad_item: '不正确的条目', - item_doesnt_exist: '条目不存在', - http_error: '与服务通信出错', - canceled: '取消动作', - data_provider_error: 'dataProvider错误. 请检查console的详细信息.', - i18n_error: '无法加载指定语言包', - logged_out: '会话失效, 请重连.', - not_authorized: '您无权访问此资源.', - }, - validation: { - required: '必填', - minLength: '必须不少于 %{min} 个字符', - maxLength: '必须不多于 %{max} 个字符', - minValue: '必须不小于 %{min}', - maxValue: '必须不大于 %{max}', - number: '必须为数字', - email: '必须是有效的邮箱', - oneOf: '必须为: %{options}其中一项', - regex: '必须符合指定的格式 (regexp): %{pattern}', - }, - saved_queries: { - label: '保存查询', - query_name: '搜索名称', - new_label: '保存当前的查询...', - new_dialog_title: '将当前查询另存为', - remove_label: '删除保存的查询', - remove_label_with_name: '删除查询 "%{name}"', - remove_dialog_title: '是否删除保存的查询?', - remove_message: '确实要从保存的查询列表中删除该项吗?', - help: '筛选列表并保存此查询以备将来使用', - }, - }, - }, - chineseResources -); - -const englishMessages = _merge(defaultEnglishMessages, chineseResources); - -export const i18nProvider = polyglotI18nProvider((locale: string) => { - if (locale === 'ch') { - return chineseMessages; - } else { - return englishMessages; - } -}, 'ch'); diff --git a/server/admin/app/ra/i18n/builtin.ts b/server/admin/app/ra/i18n/builtin.ts new file mode 100644 index 00000000..9186f746 --- /dev/null +++ b/server/admin/app/ra/i18n/builtin.ts @@ -0,0 +1,164 @@ +export const defaultChineseMessages = { + ra: { + action: { + add_filter: '增加检索', + add: '增加', + back: '回退', + bulk_actions: '选中%{smart_count}项', + cancel: '取消', + clear_input_value: '清空输入', + clone: '克隆', + confirm: '确认', + create: '新建', + create_item: '新建 %{item}', + delete: '删除', + edit: '编辑', + export: '导出', + list: '列表', + refresh: '刷新', + remove_filter: '移除检索', + remove: '删除', + save: '保存', + search: '检索', + select_all: '选中全部', + select_row: '选中这行', + show: '查看', + sort: '排序', + undo: '撤销', + unselect: '反选', + expand: '展开', + close: '关闭', + open_menu: '打开菜单', + close_menu: '关闭菜单', + update: '更新', + move_up: '上移', + move_down: '下移', + open: '打开', + toggle_theme: '切换主题', + }, + boolean: { + true: '是', + false: '否', + null: ' ', + }, + page: { + create: '新建 %{name}', + dashboard: '概览', + edit: '%{name} #%{id}', + error: '出现错误', + list: '%{name} 列表', + loading: '加载中', + not_found: '未发现', + show: '%{name} #%{id}', + empty: '无 %{name} ', + invite: '要增加吗?', + }, + input: { + file: { + upload_several: '将文件集合拖拽到这里, 或点击这里选择文件集合.', + upload_single: '将文件拖拽到这里, 或点击这里选择文件.', + }, + image: { + upload_several: '将图片文件集合拖拽到这里, 或点击这里选择图片文件集合.', + upload_single: '将图片文件拖拽到这里, 或点击这里选择图片文件.', + }, + references: { + all_missing: '未找到参考数据.', + many_missing: '至少有一条参考数据不再可用.', + single_missing: '关联的参考数据不再可用.', + }, + password: { + toggle_visible: '隐藏密码', + toggle_hidden: '显示密码', + }, + }, + message: { + about: '关于', + are_you_sure: '您确定操作?', + bulk_delete_title: '删除 %{name} |||| 删除 %{smart_count}项 %{name} ', + bulk_delete_content: + '您确定要删除 %{name}? |||| 您确定要删除 %{smart_count} 项?', + bulk_update_content: + '你确定要更新 %{name}? |||| 你确定想更新 %{smart_count} 项?', + bulk_update_title: '更新 %{name} |||| 您确定更新 %{smart_count} %{name}', + delete_content: '您确定要删除该条目?', + delete_title: '删除 %{name} #%{id}', + details: '详情', + error: '客户端错误导致请求未完成.', + invalid_form: '表单输入无效. 请检查错误提示', + loading: '正在加载页面, 请稍候', + no: '否', + not_found: '您输入了错误的URL或者错误的链接.', + yes: '是', + unsaved_changes: '修改未保存. 放弃修改吗?', + }, + navigation: { + no_results: '结果为空', + no_more_results: '页码 %{page} 超出边界. 试试上一页.', + page_out_of_boundaries: '页码 %{page} 超出边界', + page_out_from_end: '已到最末页', + page_out_from_begin: '已到最前页', + page_range_info: '%{offsetBegin}-%{offsetEnd} / %{total}', + partial_page_range_info: + '%{offsetBegin}-%{offsetEnd} of more than %{offsetEnd}', + current_page: '页码 %{page}', + page: '跳转到 %{page}', + prev: '向前', + first: '第一页', + last: '最后一页', + next: '向后', + previous: '先前第一页', + page_rows_per_page: '每页行数:', + skip_nav: '跳到内容', + }, + sort: { + sort_by: '按 %{field} %{order}', + ASC: '升序', + DESC: '降序', + }, + auth: { + auth_check_error: '请登录以继续', + user_menu: '设置', + username: '用户名', + password: '密码', + sign_in: '登录', + sign_in_error: '验证失败, 请重试', + logout: '退出', + }, + notification: { + updated: '条目已更新 |||| %{smart_count} 项条目已更新', + created: '条目已新建', + deleted: '条目已删除 |||| %{smart_count} 项条目已删除', + bad_item: '不正确的条目', + item_doesnt_exist: '条目不存在', + http_error: '与服务通信出错', + canceled: '取消动作', + data_provider_error: 'dataProvider错误. 请检查console的详细信息.', + i18n_error: '无法加载指定语言包', + logged_out: '会话失效, 请重连.', + not_authorized: '您无权访问此资源.', + }, + validation: { + required: '必填', + minLength: '必须不少于 %{min} 个字符', + maxLength: '必须不多于 %{max} 个字符', + minValue: '必须不小于 %{min}', + maxValue: '必须不大于 %{max}', + number: '必须为数字', + email: '必须是有效的邮箱', + oneOf: '必须为: %{options}其中一项', + regex: '必须符合指定的格式 (regexp): %{pattern}', + }, + saved_queries: { + label: '保存查询', + query_name: '搜索名称', + new_label: '保存当前的查询...', + new_dialog_title: '将当前查询另存为', + remove_label: '删除保存的查询', + remove_label_with_name: '删除查询 "%{name}"', + remove_dialog_title: '是否删除保存的查询?', + remove_message: '确实要从保存的查询列表中删除该项吗?', + help: '筛选列表并保存此查询以备将来使用', + }, + }, +}; diff --git a/server/admin/app/ra/i18n/custom.ts b/server/admin/app/ra/i18n/custom.ts new file mode 100644 index 00000000..0c42f4a4 --- /dev/null +++ b/server/admin/app/ra/i18n/custom.ts @@ -0,0 +1,62 @@ +export const englishCustom = { + custom: { + common: { + summary: 'Summary', + panel: 'Panel', + name: 'Name', + permission: 'Permission', + }, + users: { + search: 'Search nickname or email', + resetPassword: 'Reset Password', + resetPasswordTip: + 'After resetting the password, the password becomes: 123456789, please change the password in time', + }, + messages: { + search: 'Search Message Content', + }, + groups: { + noAvatar: 'No Avatar', + 'panels.name': 'Panel Name', + 'panels.type': 'Panel Type', + 'panels.provider': 'Panel Provider', + 'panels.pluginPanelName': 'Panel Name', + 'panels.meta': 'Panel Meta', + 'panels.parentId': 'Panel Parent', + textPanel: 'Text Panel', + groupPanel: 'Panel Group', + pluginPanel: 'Plugin Panel', + }, + }, +}; + +export const chineseCustom = { + custom: { + common: { + summary: '概述', + panel: '面板', + name: '名称', + permission: '权限', + }, + users: { + search: '搜索昵称或邮箱', + resetPassword: '重置密码', + resetPasswordTip: '重置密码后密码变为: 123456789, 请及时修改密码', + }, + messages: { + search: '搜索消息内容', + }, + groups: { + noAvatar: '无头像', + 'panels.name': '面板名', + 'panels.type': '面板类型', + 'panels.provider': '面板供应插件', + 'panels.pluginPanelName': '插件面板名', + 'panels.meta': '面板元信息', + 'panels.parentId': '面板父级', + textPanel: '文本频道', + groupPanel: '面板分组', + pluginPanel: '插件面板', + }, + }, +}; diff --git a/server/admin/app/ra/i18n/index.ts b/server/admin/app/ra/i18n/index.ts new file mode 100644 index 00000000..0f6cbba4 --- /dev/null +++ b/server/admin/app/ra/i18n/index.ts @@ -0,0 +1,37 @@ +import { TranslationMessages } from 'react-admin'; +import _merge from 'lodash/merge'; +import defaultEnglishMessages from 'ra-language-english'; +import polyglotI18nProvider from 'ra-i18n-polyglot'; +import { chineseResources, englishResources } from './resources'; +import { chineseCustom, englishCustom } from './custom'; +import { defaultChineseMessages } from './builtin'; + +const chineseMessages: TranslationMessages = _merge( + {}, + defaultEnglishMessages, + defaultChineseMessages, + chineseResources, + chineseCustom +); + +const englishMessages = _merge( + {}, + defaultEnglishMessages, + englishResources, + englishCustom +); + +export const i18nProvider = polyglotI18nProvider( + (locale: string) => { + if (locale === 'ch') { + return chineseMessages; + } else { + return englishMessages; + } + }, + 'en', + [ + { locale: 'en', name: 'English' }, + { locale: 'ch', name: '简体中文' }, + ] +); diff --git a/server/admin/app/ra/i18n/resources.ts b/server/admin/app/ra/i18n/resources.ts new file mode 100644 index 00000000..000f6cc4 --- /dev/null +++ b/server/admin/app/ra/i18n/resources.ts @@ -0,0 +1,123 @@ +export const englishResources = { + resources: { + users: { + name: 'User', + fields: { + id: 'ID', + email: 'Email', + avatar: 'Avatar', + username: 'Username', + password: 'Password', + nickname: 'Nick Name', + discriminator: 'Discriminator', + temporary: 'is Template User', + type: 'User Type', + settings: 'User Settings', + createdAt: 'Create Time', + }, + }, + messages: { + name: 'Messages', + fields: { + content: 'Content', + author: 'Author', + groupId: 'Group ID', + converseId: 'Converse ID', + hasRecall: 'Recall', + reactions: 'Reactions', + createdAt: 'Create Time', + }, + }, + groups: { + name: 'Group', + fields: { + id: 'Group ID', + name: 'Group Name', + avatar: 'Avatar', + owner: 'Owner', + members: 'Member List', + 'members.length': 'Member count', + 'panels.length': 'Panel count', + roles: 'Roles', + config: 'Config', + panels: 'Group Panels', + fallbackPermissions: 'Default Permission', + createdAt: 'Create Time', + updatedAt: 'Update Time', + }, + }, + file: { + name: 'File', + fields: { + objectName: 'Object Name', + url: 'Path', + size: 'Size', + 'metaData.content-type': 'Type', + userId: 'Storage User', + createdAt: 'Create Time', + }, + }, + }, +}; + +export const chineseResources = { + resources: { + users: { + name: '用户管理', + fields: { + id: '用户ID', + email: '邮箱', + avatar: '头像', + username: '用户名', + password: '密码', + nickname: '昵称', + discriminator: '标识符', + temporary: '是否游客', + type: '用户类型', + settings: '用户设置', + createdAt: '创建时间', + }, + }, + messages: { + name: '消息管理', + fields: { + content: '内容', + author: '作者', + groupId: '群组ID', + converseId: '会话ID', + hasRecall: '撤回', + reactions: '消息反应', + createdAt: '创建时间', + }, + }, + groups: { + name: '群组管理', + fields: { + id: '群组ID', + name: '群组名称', + avatar: '头像', + owner: '管理员', + members: '成员列表', + 'members.length': '成员数量', + 'panels.length': '面板数量', + roles: '角色', + config: '配置信息', + panels: '群组面板', + fallbackPermissions: '默认权限', + createdAt: '创建时间', + updatedAt: '更新时间', + }, + }, + file: { + name: '文件管理', + fields: { + objectName: '对象存储名', + url: '文件路径', + size: '文件大小', + 'metaData.content-type': '文件类型', + userId: '存储用户', + createdAt: '创建时间', + }, + }, + }, +}; diff --git a/server/admin/app/ra/resources/chat.tsx b/server/admin/app/ra/resources/chat.tsx index 5616a413..e14eb138 100644 --- a/server/admin/app/ra/resources/chat.tsx +++ b/server/admin/app/ra/resources/chat.tsx @@ -7,29 +7,34 @@ import { ReferenceField, TextField, SearchInput, + useTranslate, } from 'react-admin'; -export const MessageList: React.FC = () => ( - , - ]} - > - - - - - - - - - - - -); +export const MessageList: React.FC = () => { + const translate = useTranslate(); + + return ( + , + ]} + > + + + + + + + + + + + + ); +}; MessageList.displayName = 'MessageList'; diff --git a/server/admin/app/ra/resources/file.tsx b/server/admin/app/ra/resources/file.tsx index b414d9a8..5c1e4238 100644 --- a/server/admin/app/ra/resources/file.tsx +++ b/server/admin/app/ra/resources/file.tsx @@ -12,16 +12,16 @@ import { FilesizeField } from '../components/FilesizeField'; export const FileList: React.FC = () => ( - - + + - + - + () - + ); diff --git a/server/admin/app/ra/resources/group.tsx b/server/admin/app/ra/resources/group.tsx index f836be36..72d8e14f 100644 --- a/server/admin/app/ra/resources/group.tsx +++ b/server/admin/app/ra/resources/group.tsx @@ -9,13 +9,10 @@ import { SingleFieldList, ChipField, Show, - SimpleShowLayout, - NumberField, - ReferenceField, - BooleanField, SelectField, TabbedShowLayout, ImageField, + useTranslate, } from 'react-admin'; import React from 'react'; import { Box } from '@mui/material'; @@ -28,23 +25,18 @@ const PostListActionToolbar = ({ children, ...props }) => ( export const GroupList: React.FC = () => ( ]}> - - - - - - + + + + + + - - + + @@ -53,64 +45,96 @@ export const GroupList: React.FC = () => ( ); GroupList.displayName = 'GroupList'; -export const GroupShow: React.FC = () => ( - - - - - - - +export const GroupShow: React.FC = () => { + const translate = useTranslate(); - - - - - + return ( + + + + + + + - {/* 面板 */} - - - - - - - - - - - - - + + + + + - {/* 身份组 */} - - - - - - - - + {/* 面板 */} + + + + + + + + + + + + + - {/* 成员列表 */} - - - - - - - - - - -); + {/* 身份组 */} + + + + + + + + + + {/* 成员列表 */} + + + + + + + + + + + ); +}; GroupShow.displayName = 'GroupShow'; diff --git a/server/admin/app/ra/resources/user.tsx b/server/admin/app/ra/resources/user.tsx index dd300591..57c11d7a 100644 --- a/server/admin/app/ra/resources/user.tsx +++ b/server/admin/app/ra/resources/user.tsx @@ -13,6 +13,7 @@ import { TopToolbar, useUpdate, useShowContext, + useTranslate, } from 'react-admin'; import React from 'react'; import { Box } from '@mui/material'; @@ -23,41 +24,46 @@ const PostListActionToolbar = ({ children, ...props }) => ( {children} ); -export const UserList: React.FC = () => ( - , - ]} - > - - - - - - - - - - - - - - - -); +export const UserList: React.FC = () => { + const translate = useTranslate(); + + return ( + , + ]} + > + + + + + + + + + + + + + + + + ); +}; UserList.displayName = 'UserList'; const UserShowActions: React.FC = () => { const [update] = useUpdate(); const { record, refetch, resource } = useShowContext(); + const translate = useTranslate(); return ( @@ -65,8 +71,8 @@ const UserShowActions: React.FC = () => { { await update(resource, { id: record.id, diff --git a/server/admin/app/server/broker.ts b/server/admin/app/server/broker.ts index 82c2bc98..0c7247a0 100644 --- a/server/admin/app/server/broker.ts +++ b/server/admin/app/server/broker.ts @@ -10,5 +10,5 @@ export const broker = new TcBroker({ }); broker.start().then(() => { - console.log('已链接上Tailchat网络, TRANSPORTER: ', transporter); + console.log('Linked to Tailchat network, TRANSPORTER: ', transporter); }); diff --git a/server/admin/app/server/index.ts b/server/admin/app/server/index.ts index 02bd63cf..15e2744e 100644 --- a/server/admin/app/server/index.ts +++ b/server/admin/app/server/index.ts @@ -10,9 +10,9 @@ import { apiRouter } from './router/api'; // 链接数据库 mongoose.connect(process.env.MONGO_URL!, (error: any) => { if (!error) { - return console.info('数据库已连接成功'); + return console.info('Datebase connected'); } - console.error('数据库连接失败', error); + console.error('Datebase connect error', error); }); const BUILD_DIR = path.join(process.cwd(), 'build'); diff --git a/website/docs/deployment/docker-compose.mdx b/website/docs/deployment/docker-compose.mdx index 9386c5ee..f0298d0a 100644 --- a/website/docs/deployment/docker-compose.mdx +++ b/website/docs/deployment/docker-compose.mdx @@ -101,6 +101,7 @@ After the compilation is complete, you can view the compiled image through `dock + ```bash npx tailchat-cli docker init ``` @@ -109,10 +110,11 @@ Executing this command will ask you some configuration-related questions in an i ![](./assets/docker-init.png) - + + > A configuration file needs to be downloaded before starting to tell `docker-compose` how to start the image > Download configuration files and configure environment variables from the repository: > - [docker-compose.yml](https://raw.githubusercontent.com/msgbyte/tailchat/master/docker-compose.yml) @@ -131,9 +133,11 @@ Modify the configuration of the `docker-compose.env` file, the following fields - `API_URL` is an externally accessible url address, used for file service access, it can be a domain name or an ip **If the sent picture cannot be displayed normally, this variable is not set** - `SECRET` server-side encryption key, used to generate Token. The default is `tailchat` + + Need to modify the configuration before starting Modify the configuration of the `docker-compose.env` file, the following fields are recommended to be modified: