diff --git a/public/locales/en-US/application.json b/public/locales/en-US/application.json index d2b9c0a..06c40ac 100644 --- a/public/locales/en-US/application.json +++ b/public/locales/en-US/application.json @@ -348,5 +348,58 @@ "transferring": "Finished, transfer pending in queue", "deleteRecord": "Delete record", "createdAt": "Created at: " + }, + "setting": { + "avatarUpdated": "The avatar has been updated and will take effect after refreshing.", + "nickChanged": "Nickname changed and will take effect after refreshing.", + "settingSaved": "Setting saved.", + "themeColorChanged": "Theme color changed.", + "profile": "Profile", + "avatar": "Avatar", + "uid": "UID", + "nickname": "Nickname", + "group": "Group", + "regTime": "Sign in date", + "privacyAndSecurity": "Privacy and security", + "profilePage": "Public profile", + "accountPassword": "Password", + "2fa": "2FA authentication", + "enabled": "Enabled", + "disabled": "Disabled", + "appearance": "Appearance", + "themeColor": "Theme color", + "darkMode": "Dark mode", + "syncWithSystem": "Sync with system", + "fileList": "File list", + "timeZone": "Timezone", + "webdavServer": "Server", + "userName": "Username", + "manageAccount": "Manage accounts", + "uploadImage": "Upload from file", + "useGravatar": "Use Gravatar ", + "changeNick": "Change nickname", + "originalPassword": "Original password", + "enable2FA": "Enable 2FA authentication", + "disable2FA": "Disable 2FA authentication", + "2faDescription": "Please use any 2FA mobile app or password management software that supports 2FA to scan the QR code on the left to add this site. After scanning, please fill in the 6-digit verification code given by the 2FA app to enable 2FA.", + "inputCurrent2FACode": "Enter current 2FA verification code.", + "timeZoneCode": "IANA timezone code", + "authenticatorRemoved": "Authenticator removed.", + "authenticatorAdded": "Authenticator added.", + "browserNotSupported": "Not supported by current browser or environment.", + "removedAuthenticator": "Remove authenticator", + "removedAuthenticatorConfirm": "Are you sure to remove this authenticator?", + "addNewAuthenticator": "Add a authenticator", + "hardwareAuthenticator": "Hardware authenticator", + "copied": "Copied to clipboard.", + "pleaseManuallyCopy": "Current browser does not support, please copy manually.", + "webdavAccounts": "WebDAV Accounts", + "webdavHint": "WebDAV server: {{url}}; Username: {{name}} ; The password is the password of the created account below.", + "annotation": "Annotation", + "rootFolder": "Relative root folder", + "createdAt": "Created at", + "action": "Action", + "listEmpty": "No records.", + "createNewAccount": "Create new account" } } \ No newline at end of file diff --git a/public/locales/zh-CN/application.json b/public/locales/zh-CN/application.json index 2008436..76cc356 100644 --- a/public/locales/zh-CN/application.json +++ b/public/locales/zh-CN/application.json @@ -11,7 +11,7 @@ "usePassword": "使用密码登录", "forgetPassword": "忘记密码", "2FA": "二步验证", - "input2FACode": "请输入六位二步验证代码", + "input2FACode": "请输入 6 位二步验证代码", "passwordNotMatch": "两次密码输入不一致", "findMyPassword": "找回密码", "passwordReset": "密码已重设", @@ -89,9 +89,9 @@ "newFolder": "创建文件夹", "newFile": "创建文件", "showFullPath": "显示路径", - "listView": "列表展示", - "gridViewSmall": "小图标展示", - "gridViewLarge": "大图标展示", + "listView": "列表", + "gridViewSmall": "小图标", + "gridViewLarge": "大图标", "paginationSize": "分页大小", "paginationOption": "{{option}} / 页", "noPagination": "不分页", @@ -338,5 +338,58 @@ "transferring": "已完成,转存排队中", "deleteRecord": "删除记录", "createdAt": "创建日期:" + }, + "setting": { + "avatarUpdated": "头像已更新,刷新后生效", + "nickChanged": "昵称已更改,刷新后生效", + "settingSaved": "设置已保存", + "themeColorChanged": "主题配色已更换", + "profile": "个人资料", + "avatar": "头像", + "uid": "UID", + "nickname": "昵称", + "group": "用户组", + "regTime": "注册时间", + "privacyAndSecurity": "安全隐私", + "profilePage": "个人主页", + "accountPassword": "登录密码", + "2fa": "二步验证", + "enabled": "已开启", + "disabled": "未开启", + "appearance": "个性化", + "themeColor": "主题配色", + "darkMode": "黑暗模式", + "syncWithSystem": "跟随系统", + "fileList": "文件列表", + "timeZone": "时区", + "webdavServer": "连接地址", + "userName": "用户名", + "manageAccount": "账号管理", + "uploadImage": "从文件上传", + "useGravatar": "使用 Gravatar 头像 ", + "changeNick": "修改昵称", + "originalPassword": "原密码", + "enable2FA": "启用二步验证", + "disable2FA": "关闭二步验证", + "2faDescription": "请使用任意二步验证APP或者支持二步验证的密码管理软件扫描左侧二维码添加本站。扫描完成后请填写二步验证APP给出的6位验证码以开启二步验证。", + "inputCurrent2FACode": "请验证当前二步验证代码。", + "timeZoneCode": "IANA 时区名称标识", + "authenticatorRemoved": "凭证已删除", + "authenticatorAdded": "验证器已添加", + "browserNotSupported": "当前浏览器或环境不支持", + "removedAuthenticator": "删除凭证", + "removedAuthenticatorConfirm": "确定要吊销这个凭证吗?", + "addNewAuthenticator": "添加新验证器", + "hardwareAuthenticator": "外部认证器", + "copied": "已复制到剪切板", + "pleaseManuallyCopy": "当前浏览器不支持,请手动复制", + "webdavAccounts": "WebDAV 账号管理", + "webdavHint": "WebDAV的地址为:{{url}};登录用户名统一为:{{name}} ;密码为所创建账号的密码。", + "annotation": "备注名", + "rootFolder": "相对根目录", + "createdAt": "创建日期", + "action": "操作", + "listEmpty": "没有记录", + "createNewAccount": "创建新账号" } } \ No newline at end of file diff --git a/src/App.js b/src/App.js index b83265c..26bee65 100644 --- a/src/App.js +++ b/src/App.js @@ -69,6 +69,11 @@ export default function App() { textTransform: "none", }, }, + MuiTab: { + root: { + textTransform: "none", + }, + }, }, }); changeThemeColor( @@ -232,11 +237,13 @@ export default function App() { - + - + diff --git a/src/component/Modals/CreateWebDAVAccount.js b/src/component/Modals/CreateWebDAVAccount.js index 8d64402..a82d136 100644 --- a/src/component/Modals/CreateWebDAVAccount.js +++ b/src/component/Modals/CreateWebDAVAccount.js @@ -1,12 +1,13 @@ import React, { useState } from "react"; -import { makeStyles } from "@material-ui/core"; -import { Dialog } from "@material-ui/core"; +import { Dialog, makeStyles } from "@material-ui/core"; import DialogTitle from "@material-ui/core/DialogTitle"; import DialogActions from "@material-ui/core/DialogActions"; import Button from "@material-ui/core/Button"; import TextField from "@material-ui/core/TextField"; import { FolderOpenOutlined, LabelOutlined } from "@material-ui/icons"; import PathSelector from "../FileManager/PathSelector"; +import { useTranslation } from "react-i18next"; + const useStyles = makeStyles((theme) => ({ formGroup: { display: "flex", @@ -32,6 +33,7 @@ const useStyles = makeStyles((theme) => ({ })); export default function CreateWebDAVAccount(props) { + const { t } = useTranslation(); const [value, setValue] = useState({ name: "", path: "/", @@ -77,7 +79,9 @@ export default function CreateWebDAVAccount(props) { onClose={() => setPathSelectDialog(false)} aria-labelledby="form-dialog-title" > - 选择目录 + + {t("navbar.addTagDialog.selectFolder")} + @@ -108,7 +112,7 @@ export default function CreateWebDAVAccount(props) { className={classes.input} value={value.name} onChange={handleInputChange("name")} - label="备注名" + label={t("setting.annotation")} />
@@ -120,7 +124,7 @@ export default function CreateWebDAVAccount(props) { value={value.path} onChange={handleInputChange("path")} className={classes.input} - label="相对根目录" + label={t("setting.rootFolder")} />
- + diff --git a/src/component/Modals/TimeZone.js b/src/component/Modals/TimeZone.js index dbb5a13..2459f85 100644 --- a/src/component/Modals/TimeZone.js +++ b/src/component/Modals/TimeZone.js @@ -1,20 +1,15 @@ -import React, { useState, useCallback, useEffect } from "react"; -import { FormLabel, makeStyles } from "@material-ui/core"; +import React, { useCallback, useState } from "react"; import { Button, + CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, - DialogContentText, - CircularProgress, + makeStyles, } from "@material-ui/core"; -import PathSelector from "../FileManager/PathSelector"; import { useDispatch } from "react-redux"; -import API from "../../middleware/Api"; import TextField from "@material-ui/core/TextField"; -import Select from "@material-ui/core/Select"; -import MenuItem from "@material-ui/core/MenuItem"; import { refreshTimeZone, timeZone, @@ -22,11 +17,13 @@ import { } from "../../utils/datetime"; import FormControl from "@material-ui/core/FormControl"; import Auth from "../../middleware/Auth"; -import { setModalsLoading, toggleSnackbar } from "../../redux/explorer"; +import { toggleSnackbar } from "../../redux/explorer"; +import { useTranslation } from "react-i18next"; const useStyles = makeStyles((theme) => ({})); export default function TimeZoneDialog(props) { + const { t } = useTranslation(); const [timeZoneValue, setTimeZoneValue] = useState(timeZone); const dispatch = useDispatch(); const ToggleSnackbar = useCallback( @@ -53,12 +50,14 @@ export default function TimeZoneDialog(props) { onClose={props.onClose} aria-labelledby="form-dialog-title" > - 更改时区 + + {t("setting.timeZone")} + setTimeZoneValue(e.target.value)} /> @@ -66,14 +65,16 @@ export default function TimeZoneDialog(props) { - +
- 外部认证器 + {t("setting.hardwareAuthenticator")} @@ -194,7 +213,9 @@ export default function Authn(props) { - + ({ layout: { @@ -94,10 +95,22 @@ const styles = (theme) => ({ }, infoText: { marginRight: "17px", + [theme.breakpoints.down("xs")]: { + maxWidth: 100, + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden", + }, }, infoTextWithIcon: { marginRight: "17px", marginTop: "1px", + [theme.breakpoints.down("xs")]: { + maxWidth: 100, + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden", + }, }, rightIconWithText: { marginTop: "0px", @@ -285,7 +298,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "头像已更新,刷新后生效", + this.props.t("setting.avatarUpdated"), "success" ); this.setState({ @@ -316,7 +329,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "昵称已更改,刷新后生效", + this.props.t("nickChanged"), "success" ); this.setState({ @@ -352,7 +365,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "头像已更新,刷新后生效", + this.props.t("setting.avatarUpdated"), "success" ); this.setState({ @@ -380,7 +393,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "设置已保存", + this.props.t("setting.settingSaved"), "success" ); this.setState({ @@ -405,7 +418,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "两次密码输入不一致", + this.props.t("login.passwordNotMatch"), "warning" ); return; @@ -421,7 +434,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "密码已更新", + this.props.t("login.passwordReset"), "success" ); this.setState({ @@ -453,7 +466,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "主题配色已更换", + this.props.t("setting.themeColorChanged"), "success" ); this.props.applyThemes(this.state.chosenTheme); @@ -552,7 +565,7 @@ class UserSettingCompoment extends Component { this.props.toggleSnackbar( "top", "right", - "设定已保存", + this.props.t("setting.settingSaved"), "success" ); this.setState({ @@ -591,7 +604,7 @@ class UserSettingCompoment extends Component { }; render() { - const { classes } = this.props; + const { classes, t } = this.props; const user = Auth.GetUser(); const dark = Auth.GetPreference("theme_mode"); @@ -602,7 +615,7 @@ class UserSettingCompoment extends Component { className={classes.sectionTitle} variant="subtitle2" > - 个人资料 + {t("setting.profile")} @@ -621,7 +634,7 @@ class UserSettingCompoment extends Component { } /> - + @@ -631,7 +644,7 @@ class UserSettingCompoment extends Component { - + - + @@ -676,7 +689,7 @@ class UserSettingCompoment extends Component { - + - + - + - 安全隐私 + {t("setting.privacyAndSecurity")} @@ -733,7 +746,9 @@ class UserSettingCompoment extends Component { - + - + - + {!this.state.settings.two_factor - ? "未开启" - : "已开启"} + ? t("setting.disabled") + : t("setting.enabled")} - 个性化 + {t("setting.appearance")} @@ -830,13 +847,15 @@ class UserSettingCompoment extends Component { - + -
-
+
+
@@ -847,7 +866,7 @@ class UserSettingCompoment extends Component { - + {dark && (dark === "dark" - ? "偏好开启" - : "偏好关闭")} - {dark === null && "跟随系统"} + ? t("setting.enabled") + : t("setting.disabled"))} + {dark === null && + t("setting.syncWithSystem")} - + {this.props.viewMethod === "icon" && - "大图标"} + t("fileManager.gridViewLarge")} {this.props.viewMethod === "list" && - "列表"} + t("fileManager.listView")} {this.props.viewMethod === - "smallIcon" && "小图标"} + "smallIcon" && + t("fileManager.gridViewSmall")} - + - + - + - + -
+
this.setState({ changeTimeZone: false })} @@ -1070,7 +1097,7 @@ class UserSettingCompoment extends Component { open={this.state.avatarModal} onClose={this.handleClose} > - 修改头像 + {t("setting.avatar")} - + - 修改昵称 + {t("setting.changeNick")} @@ -1146,12 +1173,12 @@ class UserSettingCompoment extends Component { open={this.state.changePassword} onClose={this.handleClose} > - 修改登录密码 + {t("login.resetPassword")}
- {this.state.settings.two_factor ? "关闭" : "启用"} - 二步验证 + {this.state.settings.two_factor + ? t("setting.disable2FA") + : t("setting.enable2FA")}
@@ -1224,17 +1252,17 @@ class UserSettingCompoment extends Component {
{!this.state.settings.two_factor && ( - 请使用任意二步验证APP或者支持二步验证的密码管理软件扫描左侧二维码添加本站。扫描完成后请填写二步验证APP给出的6位验证码以开启二步验证。 + {t("setting.2faDescription")} )} {this.state.settings.two_factor && ( - 请验证当前二步验证代码。 + {t("setting.inputCurrent2FACode")} )}
@@ -1267,7 +1296,7 @@ class UserSettingCompoment extends Component { open={this.state.changeTheme} onClose={this.handleClose} > - 更改主题配色 + {t("setting.themeColor")} @@ -1306,7 +1335,7 @@ class UserSettingCompoment extends Component { open={this.state.showWebDavUrl} onClose={this.handleClose} > - WebDAV连接地址 + {t("setting.webdavServer")} @@ -1326,7 +1355,7 @@ class UserSettingCompoment extends Component { open={this.state.showWebDavUserName} onClose={this.handleClose} > - WebDAV用户名 + {t("setting.userName")} @@ -1350,6 +1379,6 @@ class UserSettingCompoment extends Component { const UserSetting = connect( mapStateToProps, mapDispatchToProps -)(withStyles(styles)(withRouter(UserSettingCompoment))); +)(withStyles(styles)(withRouter(withTranslation()(UserSettingCompoment)))); export default UserSetting; diff --git a/src/component/Setting/WebDAV.js b/src/component/Setting/WebDAV.js index fe44c60..c76547e 100644 --- a/src/component/Setting/WebDAV.js +++ b/src/component/Setting/WebDAV.js @@ -21,6 +21,7 @@ import TimeAgo from "timeago-react"; import Link from "@material-ui/core/Link"; import { toggleSnackbar } from "../../redux/explorer"; import Nothing from "../Placeholder/Nothing"; +import { useTranslation } from "react-i18next"; const useStyles = makeStyles((theme) => ({ layout: { @@ -53,6 +54,7 @@ const useStyles = makeStyles((theme) => ({ })); export default function WebDAV() { + const { t } = useTranslation(); const [tab, setTab] = useState(0); const [create, setCreate] = useState(false); const [accounts, setAccounts] = useState([]); @@ -67,12 +69,12 @@ export default function WebDAV() { const copyToClipboard = (text) => { if (navigator.clipboard) { navigator.clipboard.writeText(text); - ToggleSnackbar("top", "center", "已复制到剪切板", "success"); + ToggleSnackbar("top", "center", t("setting.copied"), "success"); } else { ToggleSnackbar( "top", "center", - "当前浏览器不支持,请手动复制", + t("setting.pleaseManuallyCopy"), "warning" ); } @@ -141,7 +143,7 @@ export default function WebDAV() { onClose={() => setCreate(false)} /> - WebDAV + {t("navbar.connect")} setTab(newValue)} aria-label="disabled tabs example" > - +
{tab === 0 && (
- WebDAV的地址为: - {window.location.origin + "/dav"} - ;登录用户名统一为:{user.user_name}{" "} - ;密码为所创建账号的密码。 + {t("setting.webdavHint", { + url: window.location.origin + "/dav", + name: user.user_name, + })} - 备注名 - 密码 - - 根目录 + + {t("setting.annotation")} + + + {t("login.password")} - 创建日期 + {t("setting.rootFolder")} - 操作 + {t("setting.createdAt")} + + + {t("setting.action")} @@ -202,7 +208,9 @@ export default function WebDAV() { } href={"javascript:void"} > - 复制 + {t("copy", { + ns: "common", + })} @@ -229,7 +237,7 @@ export default function WebDAV() {
{accounts.length === 0 && ( - + )}
)} diff --git a/src/utils/datetime.js b/src/utils/datetime.js index 1fa60ea..c751362 100644 --- a/src/utils/datetime.js +++ b/src/utils/datetime.js @@ -7,13 +7,23 @@ import i18next from "../i18n"; dayjs.extend(utc); dayjs.extend(timezone); -const defaultTimeZone = "Asia/Shanghai"; +let userTimezone = ""; +try { + userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; +} catch (e) { + console.log(e); +} + +if (!userTimezone) { + userTimezone = "Asia/Shanghai"; +} + const preferTimeZone = Auth.GetPreference("timeZone"); -export let timeZone = preferTimeZone ? preferTimeZone : defaultTimeZone; +export let timeZone = preferTimeZone ? preferTimeZone : userTimezone; export function refreshTimeZone() { timeZone = Auth.GetPreference("timeZone"); - timeZone = timeZone ? timeZone : defaultTimeZone; + timeZone = timeZone ? timeZone : userTimezone; } export function formatLocalTime(time) {