diff --git a/docs/data/languages.json b/docs/data/languages.json
index cc3952d0e..65e53ea0d 100644
--- a/docs/data/languages.json
+++ b/docs/data/languages.json
@@ -1 +1 @@
-{"defaults":{"language":{"display":"English","locale":"en"},"namespace":"portal"},"namespaces":["portal"],"languages":[{"display":"English","locale":"en","namespaces":["portal"],"fallbacks":["en"]},{"display":"Arabic","locale":"ar","namespaces":["portal"],"fallbacks":["en"]},{"display":"Arabic (Saudi Arabia)","locale":"ar-SA","namespaces":["portal"],"fallbacks":["ar","en"]},{"display":"Czech","locale":"cs","namespaces":["portal"],"fallbacks":["en"]},{"display":"Czech (Czechia)","locale":"cs-CZ","namespaces":["portal"],"fallbacks":["cs","en"]},{"display":"Danish","locale":"da","namespaces":["portal"],"fallbacks":["en"]},{"display":"Danish (Denmark)","locale":"da-DK","namespaces":["portal"],"fallbacks":["da","en"]},{"display":"German","locale":"de","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek","locale":"el","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek (Greece)","locale":"el-GR","namespaces":["portal"],"fallbacks":["el","en"]},{"display":"Spanish","locale":"es","namespaces":["portal"],"fallbacks":["en"]},{"display":"Finnish","locale":"fi","namespaces":["portal"],"fallbacks":["en"]},{"display":"French","locale":"fr","namespaces":["portal"],"fallbacks":["en"]},{"display":"Italian","locale":"it","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese","locale":"ja","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese (Japan)","locale":"ja-JP","namespaces":["portal"],"fallbacks":["ja","en"]},{"display":"Norwegian Bokmål","locale":"nb","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål (Norway)","locale":"nb-NO","namespaces":["portal"],"fallbacks":["nb","en"]},{"display":"Dutch","locale":"nl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål","locale":"no","namespaces":["portal"],"fallbacks":["en"]},{"display":"Polish","locale":"pl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Portuguese","locale":"pt","namespaces":["portal"],"fallbacks":["en"]},{"display":"Brazilian Portuguese","locale":"pt-BR","namespaces":["portal"],"fallbacks":["en"]},{"display":"Romanian","locale":"ro","namespaces":["portal"],"fallbacks":["en"]},{"display":"Russian","locale":"ru","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish","locale":"sv","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish (Sweden)","locale":"sv-SE","namespaces":["portal"],"fallbacks":["sv","en"]},{"display":"Ukrainian","locale":"uk","namespaces":["portal"],"fallbacks":["en"]},{"display":"Ukrainian (Ukraine)","locale":"uk-UA","namespaces":["portal"],"fallbacks":["uk","en"]},{"display":"Chinese","locale":"zh","namespaces":["portal"],"fallbacks":["en"]},{"display":"Chinese (China)","locale":"zh-CN","namespaces":["portal"],"fallbacks":["zh","en"]},{"display":"Chinese (Taiwan)","locale":"zh-TW","namespaces":["portal"],"fallbacks":["en"]}]}
\ No newline at end of file
+{"defaults":{"language":{"display":"English","locale":"en"},"namespace":"portal"},"namespaces":["portal","settings"],"languages":[{"display":"English","locale":"en","namespaces":["portal","settings"],"fallbacks":["en"]},{"display":"Arabic","locale":"ar","namespaces":["portal"],"fallbacks":["en"]},{"display":"Arabic (Saudi Arabia)","locale":"ar-SA","namespaces":["portal"],"fallbacks":["ar","en"]},{"display":"Czech","locale":"cs","namespaces":["portal"],"fallbacks":["en"]},{"display":"Czech (Czechia)","locale":"cs-CZ","namespaces":["portal"],"fallbacks":["cs","en"]},{"display":"Danish","locale":"da","namespaces":["portal"],"fallbacks":["en"]},{"display":"Danish (Denmark)","locale":"da-DK","namespaces":["portal"],"fallbacks":["da","en"]},{"display":"German","locale":"de","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek","locale":"el","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek (Greece)","locale":"el-GR","namespaces":["portal"],"fallbacks":["el","en"]},{"display":"Spanish","locale":"es","namespaces":["portal"],"fallbacks":["en"]},{"display":"Finnish","locale":"fi","namespaces":["portal"],"fallbacks":["en"]},{"display":"French","locale":"fr","namespaces":["portal"],"fallbacks":["en"]},{"display":"Italian","locale":"it","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese","locale":"ja","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese (Japan)","locale":"ja-JP","namespaces":["portal"],"fallbacks":["ja","en"]},{"display":"Norwegian Bokmål","locale":"nb","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål (Norway)","locale":"nb-NO","namespaces":["portal"],"fallbacks":["nb","en"]},{"display":"Dutch","locale":"nl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål","locale":"no","namespaces":["portal"],"fallbacks":["en"]},{"display":"Polish","locale":"pl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Portuguese","locale":"pt","namespaces":["portal"],"fallbacks":["en"]},{"display":"Brazilian Portuguese","locale":"pt-BR","namespaces":["portal"],"fallbacks":["en"]},{"display":"Romanian","locale":"ro","namespaces":["portal"],"fallbacks":["en"]},{"display":"Russian","locale":"ru","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish","locale":"sv","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish (Sweden)","locale":"sv-SE","namespaces":["portal"],"fallbacks":["sv","en"]},{"display":"Ukrainian","locale":"uk","namespaces":["portal"],"fallbacks":["en"]},{"display":"Ukrainian (Ukraine)","locale":"uk-UA","namespaces":["portal"],"fallbacks":["uk","en"]},{"display":"Chinese","locale":"zh","namespaces":["portal"],"fallbacks":["en"]},{"display":"Chinese (China)","locale":"zh-CN","namespaces":["portal"],"fallbacks":["zh","en"]},{"display":"Chinese (Taiwan)","locale":"zh-TW","namespaces":["portal"],"fallbacks":["en"]}]}
\ No newline at end of file
diff --git a/internal/server/locales/en/settings.json b/internal/server/locales/en/settings.json
new file mode 100644
index 000000000..e9d784812
--- /dev/null
+++ b/internal/server/locales/en/settings.json
@@ -0,0 +1,28 @@
+{
+ "Actions": "Actions",
+ "Add": "Add",
+ "Add new Security Key": "Add new Security Key",
+ "Attestation Type": "Attestation Type",
+ "Authenticator Attestation GUID": "Authenticator Attestation GUID",
+ "Cancel": "Cancel",
+ "Clone Warning": "Clone Warning",
+ "Created": "Created",
+ "Delete": "Delete",
+ "Details": "Details",
+ "Edit": "Edit",
+ "Enabled": "Enabled",
+ "Last Used": "Last Used",
+ "Manage your security keys": "Manage your security keys",
+ "Name": "Name",
+ "No": "No",
+ "Provide the details for the new security key": "Provide the details for the new security key",
+ "Relying Party ID": "Relying Party ID",
+ "Security Keys": "Security Keys",
+ "Settings": "Settings",
+ "Show Details": "Show Details",
+ "Transports": "Transports",
+ "Usage Count": "Usage Count",
+ "Webauthn Credential Identifier": "Credential Identifier: {{id}}",
+ "Webauthn Public Key": "Public Key: {{key}}",
+ "Yes": "Yes"
+}
diff --git a/web/src/i18n/index.ts b/web/src/i18n/index.ts
index 63cfd5edf..605477c37 100644
--- a/web/src/i18n/index.ts
+++ b/web/src/i18n/index.ts
@@ -31,7 +31,7 @@ i18n.use(Backend)
loadPath: basePath + "/locales/{{lng}}/{{ns}}.json",
},
load: "all",
- ns: ["portal"],
+ ns: ["portal", "settings"],
defaultNS: "portal",
fallbackLng: {
default: ["en"],
diff --git a/web/src/views/Settings/AddSecurityDialog.tsx b/web/src/views/Settings/AddSecurityDialog.tsx
index 3cda17d6e..659e53bef 100644
--- a/web/src/views/Settings/AddSecurityDialog.tsx
+++ b/web/src/views/Settings/AddSecurityDialog.tsx
@@ -10,10 +10,13 @@ import {
DialogTitle,
TextField,
} from "@mui/material";
+import { useTranslation } from "react-i18next";
interface Props extends DialogProps {}
export default function AddSecurityKeyDialog(props: Props) {
+ const { t: translate } = useTranslation("settings");
+
const handleAddClick = () => {
if (props.onClose) {
props.onClose({}, "backdropClick");
@@ -28,22 +31,22 @@ export default function AddSecurityKeyDialog(props: Props) {
return (
);
diff --git a/web/src/views/Settings/SettingsView.tsx b/web/src/views/Settings/SettingsView.tsx
index 0617bb380..853e898ff 100644
--- a/web/src/views/Settings/SettingsView.tsx
+++ b/web/src/views/Settings/SettingsView.tsx
@@ -31,6 +31,7 @@ import {
Tooltip,
Typography,
} from "@mui/material";
+import { useTranslation } from "react-i18next";
import { WebauthnDevice } from "@root/models/Webauthn";
import { getWebauthnDevices } from "@root/services/UserWebauthnDevices";
@@ -42,6 +43,8 @@ interface Props {}
const drawerWidth = 240;
export default function SettingsView(props: Props) {
+ const { t: translate } = useTranslation("settings");
+
const [webauthnDevices, setWebauthnDevices] = useState();
const [addKeyOpen, setAddKeyOpen] = useState(false);
const [webauthnShowDetails, setWebauthnShowDetails] = useState(-1);
@@ -73,7 +76,7 @@ export default function SettingsView(props: Props) {
theme.zIndex.drawer + 1 }}>
- Settings
+ {translate("Settings")}
-
+
@@ -101,12 +104,12 @@ export default function SettingsView(props: Props) {
- Manage your security keys
+ {translate("Manage your security keys")}
@@ -116,9 +119,9 @@ export default function SettingsView(props: Props) {
- Name
- Enabled
- Actions
+ {translate("Name")}
+ {translate("Enabled")}
+ {translate("Actions")}
@@ -131,7 +134,10 @@ export default function SettingsView(props: Props) {
key={x.kid.toString()}
>
-
+
-
+
-
+
@@ -196,7 +205,7 @@ export default function SettingsView(props: Props) {
gutterBottom
component="div"
>
- Details
+ {translate("Details")}
@@ -218,7 +227,14 @@ export default function SettingsView(props: Props) {
lg={12}
xl={12}
>
- Key ID: {x.kid}
+
+ {translate(
+ "Webauthn Credential Identifier",
+ {
+ id: x.kid.toString(),
+ },
+ )}
+
Public Key: {x.public_key}
+ {translate("Webauthn Public Key", {
+ key: x.public_key.toString(),
+ })}
- Relying Party ID
+
+ {translate("Relying Party ID")}
+
{x.rpid}
- Authenticator Attestation GUID
+ {translate(
+ "Authenticator Attestation GUID",
+ )}
{x.aaguid}
- Attestation Type
+
+ {translate("Attestation Type")}
+
{x.attestation_type}
- Transports
+
+ {translate("Transports")}
+
{x.transports.length === 0
? "N/A"
@@ -265,30 +292,40 @@ export default function SettingsView(props: Props) {
- Clone Warning
- {x.clone_warning ? "Yes" : "No"}
+ {translate("Clone Warning")}
+
+
+ {x.clone_warning
+ ? translate("Yes")
+ : translate("No")}
- Created
+
+ {translate("Created")}
+
{x.created_at.toString()}
- Last Used
+
+ {translate("Last Used")}
+
{x.last_used_at === undefined
- ? "Never"
+ ? translate("Never")
: x.last_used_at.toString()}
- Usage Count
+
+ {translate("Usage Count")}
+
{x.sign_count === 0
- ? "Never"
+ ? translate("Never")
: x.sign_count}