Skip to content

Commit 0298062

Browse files
Vonngclaude
andcommitted
feat(i18n): localize identity, configuration, and event destinations
Users, groups, policies, and access keys: forms, selectors, credential prompts, and confirmations. Selectable tables now pass onSelectAll, so the select column shows a select-all checkbox instead of the vendor's untranslatable "Select" header text. Configuration and event destinations: descriptor-driven forms translate at the rendering seam, keeping the module-level field definitions pure; protocol names and config keys stay untranslated. Example placeholders read 例如: in Chinese. Also restores proper bullet-list layout in the site replication help box. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 14b1c2d commit 0298062

70 files changed

Lines changed: 1409 additions & 986 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

web-app/src/screens/Console/Account/Account.tsx

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
4545
import { api } from "api";
4646
import { errorToHandler } from "api/errors";
4747
import HelpMenu from "../HelpMenu";
48-
import { ACCOUNT_TABLE_COLUMNS } from "./AccountUtils";
48+
import { accountTableColumns } from "./AccountUtils";
49+
import { interpolate, useLocalizedLink, useT } from "i18n";
4950
import { useAppDispatch } from "store";
5051
import { ServiceAccounts } from "api/consoleApi";
5152
import {
@@ -68,6 +69,8 @@ const DeleteServiceAccount = withSuspense(
6869
const Account = () => {
6970
const dispatch = useAppDispatch();
7071
const navigate = useNavigate();
72+
const t = useT();
73+
const localize = useLocalizedLink();
7174

7275
const features = useSelector(selFeatures);
7376

@@ -107,7 +110,7 @@ const Account = () => {
107110
.catch((res) => {
108111
dispatch(
109112
setErrorSnackMessage(
110-
errorToHandler(res?.error || "Error retrieving access keys"),
113+
errorToHandler(res?.error || t("Error retrieving access keys")),
111114
),
112115
);
113116
setLoading(false);
@@ -131,7 +134,7 @@ const Account = () => {
131134
const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
132135
setDeleteMultipleOpen(false);
133136
if (refresh) {
134-
dispatch(setSnackBarMessage(`Access keys deleted successfully.`));
137+
dispatch(setSnackBarMessage(t("Access keys deleted successfully.")));
135138
setSelectedSAs([]);
136139
setLoading(true);
137140
}
@@ -213,13 +216,13 @@ const Account = () => {
213216
open={changePasswordModalOpen}
214217
closeModal={() => setChangePasswordModalOpen(false)}
215218
/>
216-
<PageHeaderWrapper label="Access Keys" actions={<HelpMenu />} />
219+
<PageHeaderWrapper label={t("Access Keys")} actions={<HelpMenu />} />
217220

218221
<PageLayout>
219222
<Grid container>
220223
<Grid item xs={12} sx={{ ...actionsTray.actionsTray }}>
221224
<SearchBox
222-
placeholder={"Search Access Keys"}
225+
placeholder={t("Search Access Keys")}
223226
onChange={setFilter}
224227
sx={{ marginRight: "auto", maxWidth: 380 }}
225228
value={filter}
@@ -231,13 +234,13 @@ const Account = () => {
231234
gap: 5,
232235
}}
233236
>
234-
<TooltipWrapper tooltip={"Delete Selected"}>
237+
<TooltipWrapper tooltip={t("Delete Selected")}>
235238
<Button
236239
id={"delete-selected-accounts"}
237240
onClick={() => {
238241
setDeleteMultipleOpen(true);
239242
}}
240-
label={"Delete Selected"}
243+
label={t("Delete Selected")}
241244
icon={<DeleteIcon />}
242245
disabled={selectedSAs.length === 0}
243246
variant={"secondary"}
@@ -252,7 +255,7 @@ const Account = () => {
252255
<Button
253256
id={"change-password"}
254257
onClick={() => setChangePasswordModalOpen(true)}
255-
label={`Change Password`}
258+
label={t("Change Password")}
256259
icon={<PasswordKeyIcon />}
257260
variant={"regular"}
258261
disabled={userIDP}
@@ -269,7 +272,7 @@ const Account = () => {
269272
onClick={() => {
270273
navigate(`${IAM_PAGES.ACCOUNT_ADD}`);
271274
}}
272-
label={`Create access key`}
275+
label={t("Create access key")}
273276
icon={<AddIcon />}
274277
variant={"callAction"}
275278
/>
@@ -280,9 +283,17 @@ const Account = () => {
280283
<Grid item xs={12}>
281284
<DataTable
282285
itemActions={tableActions}
283-
entityName={"Access Keys"}
284-
columns={ACCOUNT_TABLE_COLUMNS}
286+
entityName={t("Access Keys")}
287+
customEmptyMessage={t("There are no Access Keys yet.")}
288+
columns={accountTableColumns(t)}
285289
onSelect={(e) => selectSAs(e, setSelectedSAs, selectedSAs)}
290+
onSelectAll={() =>
291+
setSelectedSAs(
292+
selectedSAs.length === filteredRecords.length
293+
? []
294+
: filteredRecords.map((r) => `${r.accessKey}`),
295+
)
296+
}
286297
selectedItems={selectedSAs}
287298
isLoading={loading}
288299
records={filteredRecords}
@@ -291,28 +302,28 @@ const Account = () => {
291302
</Grid>
292303
<Grid item xs={12} sx={{ marginTop: 15 }}>
293304
<HelpBox
294-
title={"Learn more about ACCESS KEYS"}
305+
title={t("Learn more about ACCESS KEYS")}
295306
iconComponent={<AccountIcon />}
296307
help={
297308
<Fragment>
298-
SILO access keys are child identities of an authenticated SILO
299-
user, including externally managed identities. Each access key
300-
inherits its privileges based on the policies attached to it’s
301-
parent user or those groups in which the parent user has
302-
membership. Access Keys also support an optional inline policy
303-
which further restricts access to a subset of actions and
304-
resources available to the parent user.
309+
{t(
310+
"SILO access keys are child identities of an authenticated SILO user, including externally managed identities. Each access key inherits its privileges based on the policies attached to it’s parent user or those groups in which the parent user has membership. Access Keys also support an optional inline policy which further restricts access to a subset of actions and resources available to the parent user.",
311+
)}
305312
<br />
306313
<br />
307-
You can learn more at the{" "}
308-
<a
309-
href="https://silo.pgsty.com/administration/identity-access-management/minio-user-management/#access-keys"
310-
target="_blank"
311-
rel="noopener"
312-
>
313-
documentation
314-
</a>
315-
.
314+
{interpolate(t("You can learn more at the {link}."), {
315+
link: (
316+
<a
317+
href={localize(
318+
"https://silo.pgsty.com/administration/identity-access-management/minio-user-management/#access-keys",
319+
)}
320+
target="_blank"
321+
rel="noopener"
322+
>
323+
{t("documentation")}
324+
</a>
325+
),
326+
})}
316327
</Fragment>
317328
}
318329
/>

web-app/src/screens/Console/Account/AccountUtils.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import React from "react";
1818
import { DateTime } from "luxon";
1919

20-
export const ACCOUNT_TABLE_COLUMNS = [
21-
{ label: "Access Key", elementKey: "accessKey" },
20+
// Built per render so the labels follow the active language; callers pass the
21+
// t() they already hold.
22+
export const accountTableColumns = (t: (text: string) => string) => [
23+
{ label: t("Access Key"), elementKey: "accessKey" },
2224
{
23-
label: "Expiry",
25+
label: t("Expiry"),
2426
elementKey: "expiration",
2527
renderFunction: (expTime: string) => {
2628
if (expTime !== "1970-01-01T00:00:00Z") {
@@ -30,21 +32,21 @@ export const ACCOUNT_TABLE_COLUMNS = [
3032

3133
return <span title={fmtDate}>{fmtDate}</span>;
3234
} else {
33-
return <span>no-expiry</span>;
35+
return <span>{t("no-expiry")}</span>;
3436
}
3537
},
3638
},
3739
{
38-
label: "Status",
40+
label: t("Status"),
3941
elementKey: "accountStatus",
4042
renderFunction: (status: string) => {
4143
if (status === "off") {
42-
return "Disabled";
44+
return t("Disabled");
4345
} else {
44-
return "Enabled";
46+
return t("Enabled");
4547
}
4648
},
4749
},
48-
{ label: "Name", elementKey: "name" },
49-
{ label: "Description", elementKey: "description" },
50+
{ label: t("Name"), elementKey: "name" },
51+
{ label: t("Description"), elementKey: "description" },
5052
];

web-app/src/screens/Console/Account/AddServiceAccountHelpBox.tsx

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
PasswordKeyIcon,
2222
ServiceAccountIcon,
2323
} from "mds";
24+
import { useT } from "i18n";
2425

2526
const FeatureItem = ({
2627
icon,
@@ -49,6 +50,7 @@ const FeatureItem = ({
4950
);
5051
};
5152
const AddServiceAccountHelpBox = () => {
53+
const t = useT();
5254
return (
5355
<Box
5456
sx={{
@@ -78,51 +80,50 @@ const AddServiceAccountHelpBox = () => {
7880
}}
7981
>
8082
<HelpIconFilled />
81-
<div>Learn more about Access Keys</div>
83+
<div>{t("Learn more about Access Keys")}</div>
8284
</Box>
8385
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
8486
<Box sx={{ paddingBottom: "20px" }}>
8587
<FeatureItem
8688
icon={<ServiceAccountIcon />}
87-
description={`Create Access Keys`}
89+
description={t("Create Access Keys")}
8890
/>
8991
<Box sx={{ paddingTop: "20px" }}>
90-
Access Keys inherit the policies explicitly attached to the parent
91-
user, and the policies attached to each group in which the parent
92-
user has membership.
92+
{t(
93+
"Access Keys inherit the policies explicitly attached to the parent user, and the policies attached to each group in which the parent user has membership.",
94+
)}
9395
</Box>
9496
</Box>
9597
<Box sx={{ paddingBottom: "20px" }}>
9698
<FeatureItem
9799
icon={<PasswordKeyIcon />}
98-
description={`Assign Custom Credentials`}
100+
description={t("Assign Custom Credentials")}
99101
/>
100102
<Box sx={{ paddingTop: "10px" }}>
101-
Randomized access credentials are recommended, and provided by
102-
default. You may use your own custom Access Key and Secret Key by
103-
replacing the default values. After creation of any Access Key, you
104-
will be given the opportunity to view and download the account
105-
credentials.
103+
{t(
104+
"Randomized access credentials are recommended, and provided by default. You may use your own custom Access Key and Secret Key by replacing the default values. After creation of any Access Key, you will be given the opportunity to view and download the account credentials.",
105+
)}
106106
</Box>
107107
<Box sx={{ paddingTop: "10px" }}>
108-
Access Keys support programmatic access by applications. You cannot
109-
use a Access Key to log into the SILO Console.
108+
{t(
109+
"Access Keys support programmatic access by applications. You cannot use a Access Key to log into the SILO Console.",
110+
)}
110111
</Box>
111112
</Box>
112113
<Box sx={{ paddingBottom: "20px" }}>
113114
<FeatureItem
114115
icon={<IAMPoliciesIcon />}
115-
description={`Assign Access Policies`}
116+
description={t("Assign Access Policies")}
116117
/>
117118
<Box sx={{ paddingTop: "10px" }}>
118-
You can specify an optional JSON-formatted IAM policy to further
119-
restrict Access Key access to a subset of the actions and resources
120-
explicitly allowed for the parent user. Additional access beyond
121-
that of the parent user cannot be implemented through these
122-
policies.
119+
{t(
120+
"You can specify an optional JSON-formatted IAM policy to further restrict Access Key access to a subset of the actions and resources explicitly allowed for the parent user. Additional access beyond that of the parent user cannot be implemented through these policies.",
121+
)}
123122
</Box>
124123
<Box sx={{ paddingTop: "10px" }}>
125-
You cannot modify the optional Access Key IAM policy after saving.
124+
{t(
125+
"You cannot modify the optional Access Key IAM policy after saving.",
126+
)}
126127
</Box>
127128
</Box>
128129
</Box>

0 commit comments

Comments
 (0)