diff --git a/public/locales/en-US/application.json b/public/locales/en-US/application.json index 8aeb147..7447ce7 100644 --- a/public/locales/en-US/application.json +++ b/public/locales/en-US/application.json @@ -31,6 +31,57 @@ "sinUpTitle": "Sign up to {{title}}", "activateTitle": "Activate your account", "activateDescription": "An activation email has been sent to your email address, please visit the link in the email to complete your sign-up.", - "continue": "Next" + "continue": "Next", + "logout": "Sign out", + "loggedOut": "You are signed out now." + }, + "navbar": { + "myFiles": "My Files", + "myShare": "Shared", + "remoteDownload": "Remote Download", + "connect": "Connect", + "taskQueue": "Task Queue", + "setting": "Settings", + "videos": "Videos", + "photos": "Photos", + "music": "Music", + "documents": "Documents", + "addATag": "Add a tag...", + "addTagDialog": { + "selectFolder": "Select a Folder", + "fileSelector": "File Selector", + "folderLink": "Folder Shortcut", + "tagName": "Tag name", + "matchPattern": "Match pattern(s) of file name", + "matchPatternDescription": "You can use <0>* as a wildcard. For example, <1>*.png means match png format images. Multi-line rules will operate in an \"or\" relationship with each other.", + "icon": "Icon:", + "color": "Color:", + "folderPath": "Path to the folder" + }, + "storage": "Storage", + "storageDetail": "{{used}} of {{total}} used", + "notLoginIn": "Not sign in", + "visitor": "Anonymous", + "objectsSelected": "{{num}} objects selected", + "searchPlaceholder": "Search...", + "searchInFiles": "Search <0>{{name}} in my files", + "searchInFolders": "Search <0>{{name}} under current folder", + "searchInShares": "Search <0>{{name}} in other users’ shares", + "backToHomepage": "Back to homepage", + "toDarkMode": "Switch to dark theme", + "toLightMode": "Switch to light theme", + "myProfile": "My profile", + "dashboard": "Dashboard", + "exceedQuota": "Your used capacity has exceeded the quota, please delete the extra files." + }, + "fileManager": { + "open": "Open", + "download": "Download", + "batchDownload": "Download in batch", + "share": "Share", + "rename": "Rename", + "move": "Move", + "delete": "Remove", + "moreActions": "More actions..." } } \ No newline at end of file diff --git a/public/locales/en-US/common.json b/public/locales/en-US/common.json index cf859a1..e01c334 100644 --- a/public/locales/en-US/common.json +++ b/public/locales/en-US/common.json @@ -5,6 +5,9 @@ "newVersionRefresh": "A new version of the current page is available and ready to be refreshed.", "errorDetails": "Error details", "renderError": "There is an error in the page rendering, please try refreshing this page.", + "ok": "OK", + "cancel": "Cancel", + "select": "Select", "errors": { "40020": "Wrong password or email address.", "40017": "This account has been blocked.", diff --git a/public/locales/zh-CN/application.json b/public/locales/zh-CN/application.json index a54da09..bf88732 100644 --- a/public/locales/zh-CN/application.json +++ b/public/locales/zh-CN/application.json @@ -31,6 +31,57 @@ "sinUpTitle": "注册 {{title}}", "activateTitle": "邮件激活", "activateDescription": "一封激活邮件已经发送至您的邮箱,请访问邮件中的链接以继续完成注册。", - "continue": "下一步" + "continue": "下一步", + "logout": "退出登录", + "loggedOut": "您已退出登录" + }, + "navbar": { + "myFiles": "我的文件", + "myShare": "我的分享", + "remoteDownload": "离线下载", + "connect": "连接", + "taskQueue": "任务队列", + "setting": "个人设置", + "videos": "视频", + "photos": "图片", + "music": "音乐", + "documents": "文档", + "addATag": "添加标签...", + "addTagDialog": { + "selectFolder": "选择目录", + "fileSelector": "文件分类", + "folderLink": "目录快捷方式", + "tagName": "标签名", + "matchPattern": "文件名匹配规则", + "matchPatternDescription": "你可以使用 <0>* 作为通配符。比如 <1>*.png 表示匹配 png 格式图像。多行规则间会以 “或” 的关系进行运算。", + "icon": "图标:", + "color": "颜色:", + "folderPath": "目录路径" + }, + "storage": "存储空间", + "storageDetail": "已使用 {{used}}, 共 {{total}}", + "notLoginIn": "未登录", + "visitor": "游客", + "objectsSelected": "{{num}} 个对象", + "searchPlaceholder": "搜索...", + "searchInFiles": "在我的文件中搜索 <0>{{name}}", + "searchInFolders": "在当前目录中搜索 <0>{{name}}", + "searchInShares": "在全站分享中搜索 <0>{{name}}", + "backToHomepage": "返回主页", + "toDarkMode": "切换到深色模式", + "toLightMode": "切换到浅色模式", + "myProfile": "个人主页", + "dashboard": "管理面板", + "exceedQuota": "您的已用容量已超过容量配额,请尽快删除多余文件" + }, + "fileManager": { + "open": "打开", + "download": "下载", + "batchDownload": "打包下载", + "share": "分享", + "rename": "重命名", + "move": "移动", + "delete": "删除", + "moreActions": "更多操作" } } \ No newline at end of file diff --git a/public/locales/zh-CN/common.json b/public/locales/zh-CN/common.json index 2f45cbc..e1b5967 100644 --- a/public/locales/zh-CN/common.json +++ b/public/locales/zh-CN/common.json @@ -5,6 +5,9 @@ "newVersionRefresh": "当前页面有新版本可用,准备刷新。", "errorDetails": "错误详情", "renderError": "页面渲染出现错误,请尝试刷新此页面。", + "ok": "确定", + "cancel": "取消", + "select": "选择", "errors": { "40020": "用户邮箱或密码错误", "40017": "该账号已被封禁", diff --git a/src/component/Modals/AddTag.js b/src/component/Modals/AddTag.js index e61c6e0..d454219 100644 --- a/src/component/Modals/AddTag.js +++ b/src/component/Modals/AddTag.js @@ -1,12 +1,13 @@ -import React, { useState, useCallback } from "react"; -import { makeStyles, useTheme } from "@material-ui/core"; +import React, { useCallback, useState } from "react"; import { Button, + CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, - CircularProgress, + makeStyles, + useTheme, } from "@material-ui/core"; import PathSelector from "../FileManager/PathSelector"; import { useDispatch } from "react-redux"; @@ -35,6 +36,7 @@ import { Triangle, } from "mdi-material-ui"; import { toggleSnackbar } from "../../redux/explorer"; +import { Trans, useTranslation } from "react-i18next"; const useStyles = makeStyles((theme) => ({ contentFix: { @@ -93,6 +95,7 @@ const icons = { export default function AddTag(props) { const theme = useTheme(); + const { t } = useTranslation(); const [value, setValue] = React.useState(0); const [loading, setLoading] = React.useState(false); @@ -229,7 +232,9 @@ export default function AddTag(props) { onClose={() => setPathSelectDialog(false)} aria-labelledby="form-dialog-title" > - 选择目录 + + {t("navbar.addTagDialog.selectFolder")} + @@ -257,14 +262,14 @@ export default function AddTag(props) { variant="fullWidth" aria-label="full width tabs example" > - - + + {value === 0 && ( - 你可以使用*作为通配符。比如 - *.png - 表示匹配png格式图像。多行规则间会以“或”的关系进行运算。 + + {[, ]} + - 图标: + + {t("navbar.addTagDialog.icon")} +
- 颜色: + + {t("navbar.addTagDialog.color")} +
- 选择 + {t("common:select")}
)} - +
@@ -605,7 +618,9 @@ class NavbarCompoment extends Component { {this.props.selected.length > 1 && !pathHelper.isMobile() && ( - {this.props.selected.length} 个对象 + {t("navbar.objectsSelected", { + num: this.props.selected.length, + })} )} {this.props.selected.length === 0 && } @@ -628,7 +643,11 @@ class NavbarCompoment extends Component { ) } > - + @@ -651,7 +670,11 @@ class NavbarCompoment extends Component { this.props.withFile } > - + @@ -671,7 +694,11 @@ class NavbarCompoment extends Component { this.props.withFolder } > - + - + @@ -702,7 +733,9 @@ class NavbarCompoment extends Component { )} {!this.props.isMultiple && !isSharePage && ( - + @@ -724,7 +757,11 @@ class NavbarCompoment extends Component { !pathHelper.isMobile() } > - + @@ -743,7 +780,11 @@ class NavbarCompoment extends Component { .length !== 0 } > - + @@ -763,7 +804,11 @@ class NavbarCompoment extends Component { pathHelper.isMobile() } > - + @@ -786,7 +831,7 @@ class NavbarCompoment extends Component { !(!this.props.isMultiple && this.props.withFile) && this.props.audioPreviewPlayingName != null && ( - 在我的文件中搜索{" "} - - {this.state.input} - + , + ]} + /> } /> @@ -216,10 +223,18 @@ class SearchBarCompoment extends Component { }} primary={ - 在当前目录中搜索{" "} - - {this.state.input} - + , + ]} + /> } /> @@ -234,10 +249,15 @@ class SearchBarCompoment extends Component { classes={{ primary: classes.primary }} primary={ - 在全站分享中搜索{" "} - - {this.state.input} - + , + ]} + /> } /> @@ -258,6 +278,6 @@ SearchBarCompoment.propTypes = { const SearchBar = connect( mapStateToProps, mapDispatchToProps -)(withStyles(styles)(withRouter(SearchBarCompoment))); +)(withStyles(styles)(withRouter(withTranslation()(SearchBarCompoment)))); export default SearchBar; diff --git a/src/component/Navbar/StorageBar.js b/src/component/Navbar/StorageBar.js index 4775321..92d769a 100644 --- a/src/component/Navbar/StorageBar.js +++ b/src/component/Navbar/StorageBar.js @@ -15,6 +15,8 @@ import { import ButtonBase from "@material-ui/core/ButtonBase"; import { withRouter } from "react-router"; import { toggleSnackbar } from "../../redux/explorer"; +import { Link as RouterLink } from "react-router-dom"; +import { withTranslation } from "react-i18next"; const mapStateToProps = (state) => { return { @@ -108,7 +110,7 @@ class StorageBarCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "您的已用容量已超过容量配额,请尽快删除多余文件或购买容量", + this.props.t("navbar.exceedQuota"), "warning" ); } else { @@ -125,7 +127,7 @@ class StorageBarCompoment extends Component { }; render() { - const { classes } = this.props; + const { classes, t } = this.props; return (
this.setState({ showExpand: true })} @@ -137,7 +139,9 @@ class StorageBarCompoment extends Component {
- 存储空间{" "} + + {t("navbar.storage")} +
{ return { @@ -91,7 +92,7 @@ class UserAvatarCompoment extends Component { }; render() { - const { classes } = this.props; + const { classes, t } = this.props; const loginCheck = Auth.Check(this.props.isLogin); const user = Auth.GetUser(this.props.isLogin); const isAdminPage = pathHelper.isAdminPage( @@ -113,7 +114,7 @@ class UserAvatarCompoment extends Component { {loginCheck && ( <> )} {isAdminPage && ( - + { return { @@ -105,7 +110,7 @@ class UserAvatarPopoverCompoment extends Component { }; render() { - const { classes } = this.props; + const { classes, t } = this.props; const user = Auth.GetUser(); const isAdminPage = pathHelper.isAdminPage( this.props.location.pathname @@ -134,16 +139,18 @@ class UserAvatarPopoverCompoment extends Component { - 登录 + {t("login.signIn")} {this.props.registerEnabled && ( this.props.history.push("/signup")} + onClick={() => + this.props.history.push("/signup") + } > - 注册 + {t("login.signUp")} )}
@@ -198,7 +205,7 @@ class UserAvatarPopoverCompoment extends Component { - 个人主页 + {t("navbar.myProfile")} )} {user.group.id === 1 && ( @@ -214,7 +221,7 @@ class UserAvatarPopoverCompoment extends Component { - 管理面板 + {t("navbar.dashboard")} )} @@ -227,7 +234,7 @@ class UserAvatarPopoverCompoment extends Component { - 退出登录 + {t("login.logout")}
@@ -244,6 +251,10 @@ UserAvatarPopoverCompoment.propTypes = { const UserAvatarPopover = connect( mapStateToProps, mapDispatchToProps -)(withStyles(styles)(withRouter(UserAvatarPopoverCompoment))); +)( + withStyles(styles)( + withRouter(withTranslation()(UserAvatarPopoverCompoment)) + ) +); export default UserAvatarPopover; diff --git a/src/component/Navbar/UserInfo.js b/src/component/Navbar/UserInfo.js index c142b0e..2daeffb 100644 --- a/src/component/Navbar/UserInfo.js +++ b/src/component/Navbar/UserInfo.js @@ -6,6 +6,7 @@ import Auth from "../../middleware/Auth"; import DarkModeSwitcher from "./DarkModeSwitcher"; import Avatar from "@material-ui/core/Avatar"; import { setUserPopover } from "../../redux/explorer"; +import { withTranslation } from "react-i18next"; const mapStateToProps = (state) => { return { @@ -70,7 +71,6 @@ const styles = (theme) => ({ }, nickName: { color: "#fff", - marginLeft: "10px", marginTop: "15px", fontSize: "17px", }, @@ -80,7 +80,6 @@ const styles = (theme) => ({ alignItems: "flex-start", }, groupName: { - marginLeft: "10px", color: "#ffffff", opacity: "0.54", }, @@ -95,7 +94,7 @@ class UserInfoCompoment extends Component { }; render() { - const { classes } = this.props; + const { classes, t } = this.props; const isLogin = Auth.Check(this.props.isLogin); const user = Auth.GetUser(this.props.isLogin); @@ -125,7 +124,7 @@ class UserInfoCompoment extends Component { component="h2" noWrap > - {isLogin ? user.nickname : "未登录"} + {isLogin ? user.nickname : t("navbar.notLoginIn")} - {isLogin ? user.group.name : "游客"} + {isLogin ? user.group.name : t("navbar.visitor")}
@@ -148,6 +147,6 @@ UserInfoCompoment.propTypes = { const UserInfo = connect( mapStateToProps, mapDispatchToProps -)(withStyles(styles)(UserInfoCompoment)); +)(withStyles(styles)(withTranslation()(UserInfoCompoment))); export default UserInfo; diff --git a/src/i18n.ts b/src/i18n.ts index 47ed3be..28b27a9 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -18,4 +18,8 @@ i18n.use(Backend) }, }); +i18n.on("languageChanged", (lng) => { + document.documentElement.setAttribute("lang", lng); +}); + export default i18n;