diff --git a/web/src/components/core/InstallButton.jsx b/web/src/components/core/InstallButton.jsx index 9735bb21ca..6deaf4759d 100644 --- a/web/src/components/core/InstallButton.jsx +++ b/web/src/components/core/InstallButton.jsx @@ -41,10 +41,10 @@ const InstallConfirmationPopup = ({ hasIssues, onAccept, onClose }) => { }; // TRANSLATORS: the installer reports some errors, - // the part in curly brackets {} is a clickable link + // the text in square brackets [] is a clickable link const [msgStart, msgLink, msgEnd] = _("There are some reported issues. \ -Please, check {the list of issues} \ -before proceeding with the installation.").split(/[{}]/); +Please, check [the list of issues] \ +before proceeding with the installation.").split(/[[\]]/); return (

diff --git a/web/src/components/core/Popup.jsx b/web/src/components/core/Popup.jsx index 05b3c75200..e51cdacf6c 100644 --- a/web/src/components/core/Popup.jsx +++ b/web/src/components/core/Popup.jsx @@ -21,6 +21,8 @@ import React, { useLayoutEffect } from "react"; import { Button, Modal } from "@patternfly/react-core"; + +import { _ } from "~/i18n"; import { partition } from "~/utils"; /** @@ -85,7 +87,7 @@ const PrimaryAction = ({ children, ...props }) => ( * @param {React.ReactNode} [props.children="confirm"] - content of the action * @param {object} [props] - {@link Action} props */ -const Confirm = ({ children = "Confirm", ...props }) => ( +const Confirm = ({ children = _("Confirm"), ...props }) => ( { children } ); @@ -123,7 +125,7 @@ const SecondaryAction = ({ children, ...props }) => ( * @param {React.ReactNode} [props.children="Cancel"] - content of the action * @param {object} [props] - {@link Action} props */ -const Cancel = ({ children = "Cancel", ...props }) => ( +const Cancel = ({ children = _("Cancel"), ...props }) => ( { children } ); diff --git a/web/src/components/storage/DASDFormatProgress.jsx b/web/src/components/storage/DASDFormatProgress.jsx index e7f7311e44..a97f34b3a8 100644 --- a/web/src/components/storage/DASDFormatProgress.jsx +++ b/web/src/components/storage/DASDFormatProgress.jsx @@ -21,6 +21,8 @@ import React, { useEffect, useState } from "react"; import { Progress, Skeleton } from '@patternfly/react-core'; + +import { _ } from "~/i18n"; import { If, Popup } from "~/components/core"; import { useInstallerClient } from "~/context/installer"; @@ -56,7 +58,7 @@ export default function DASDFormatProgress({ job, devices, isOpen = true }) { const WaitingProgress = () => (

-
Waiting for progress report
+
{_("Waiting for progress report")}
@@ -64,7 +66,7 @@ export default function DASDFormatProgress({ job, devices, isOpen = true }) { return ( diff --git a/web/src/components/storage/DASDPage.jsx b/web/src/components/storage/DASDPage.jsx index eceafb7045..5c40e0d723 100644 --- a/web/src/components/storage/DASDPage.jsx +++ b/web/src/components/storage/DASDPage.jsx @@ -22,6 +22,8 @@ import React, { useEffect, useReducer } from "react"; import { useNavigate } from "react-router-dom"; import { Button } from "@patternfly/react-core"; + +import { _ } from "~/i18n"; import { MainActions } from "~/components/layout"; import { If, Page } from "~/components/core"; import { DASDFormatProgress, DASDTable } from "~/components/storage"; @@ -179,9 +181,10 @@ export default function DASDPage() { }, [client.dasd]); return ( - + // TRANSLATORS: DASD = Direct Access Storage Device, IBM mainframe storage technology + - + diff --git a/web/src/components/storage/DASDTable.jsx b/web/src/components/storage/DASDTable.jsx index 82fff46735..528f80e42a 100644 --- a/web/src/components/storage/DASDTable.jsx +++ b/web/src/components/storage/DASDTable.jsx @@ -29,6 +29,7 @@ import { import { TableComposable, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; import { sort } from 'fast-sort'; +import { _ } from "~/i18n"; import { Icon } from "~/components/layout"; import { If, SectionSkeleton } from "~/components/core"; import { hex } from "~/utils"; @@ -51,20 +52,20 @@ const columnData = (device, column) => { } if (typeof data === "boolean") { - return data ? "Yes" : "No"; + return data ? _("Yes") : _("No"); } return data; }; const columns = [ - { id: "channelId", sortId: "hexId", label: "Channel ID" }, - { id: "status", label: "Status" }, - { id: "name", label: "Device" }, - { id: "type", label: "Type" }, - { id: "diag", label: "Diag" }, - { id: "formatted", label: "Formatted" }, - { id: "partitionInfo", label: "Partition Info" } + { id: "channelId", sortId: "hexId", label: _("Channel ID") }, + { id: "status", label: _("Status") }, + { id: "name", label: _("Device") }, + { id: "type", label: _("Type") }, + { id: "diag", label: _("Diag") }, + { id: "formatted", label: _("Formatted") }, + { id: "partitionInfo", label: _("Partition Info") } ]; const Actions = ({ devices, isDisabled }) => { @@ -97,17 +98,23 @@ const Actions = ({ devices, isDisabled }) => { isOpen={isOpen} onSelect={onSelect} dropdownItems={[ - Activate, - Deactivate, + // TRANSLATORS: drop down menu action, activate the device + {_("Activate")}, + // TRANSLATORS: drop down menu action, deactivate the device + {_("Deactivate")}, , - Set DIAG On, - Set DIAG Off, + // TRANSLATORS: drop down menu action, enable DIAG access method + {_("Set DIAG On")}, + // TRANSLATORS: drop down menu action, disable DIAG access method + {_("Set DIAG Off")}, , - Format + // TRANSLATORS: drop down menu action, format the disk + {_("Format")} ]} toggle={ - Perform an action + {/* TRANSLATORS: drop down menu label */} + {_("Perform an action")} } /> @@ -185,15 +192,15 @@ export default function DASDTable({ state, dispatch }) { { state.minChannel !== "" && diff --git a/web/src/components/storage/ProposalActionsSection.jsx b/web/src/components/storage/ProposalActionsSection.jsx index ed3ccd386b..a30723752b 100644 --- a/web/src/components/storage/ProposalActionsSection.jsx +++ b/web/src/components/storage/ProposalActionsSection.jsx @@ -27,7 +27,9 @@ import { Skeleton, Text } from "@patternfly/react-core"; +import format from "format-util"; +import { _, n_ } from "~/i18n"; import { If, Section } from "~/components/core"; import { partition } from "~/utils"; @@ -64,13 +66,16 @@ const ProposalActions = ({ actions = [] }) => { if (actions.length === 0) return null; const [generalActions, subvolActions] = partition(actions, a => !a.subvol); - const userAction = isExpanded ? "Hide" : "Show"; - const toggleText = `${userAction} ${subvolActions.length} subvolumes actions`; + const toggleText = isExpanded + // TRANSLATORS: show/hide toggle action, this is a clickable link + ? format(n_("Hide %d subvolume action", "Hide %d subvolume actions", subvolActions.length), subvolActions.length) + // TRANSLATORS: show/hide toggle action, this is a clickable link + : format(n_("Show %d subvolume action", "Show %d subvolume actions", subvolActions.length), subvolActions.length); return ( <> - Actions to create the file systems and to ensure the system boots. + {_("Actions to create the file systems and to ensure the system boots.")} {subvolActions.length > 0 && ( @@ -116,7 +121,9 @@ export default function ProposalActionsSection({ actions = [], errors = [], isLo if (isLoading) errors = []; return ( -
+ // TRANSLATORS: section title, list of planned actions for the selected device, + // e.g. "delete partition A", "create partition B with filesystem C", ... +
} diff --git a/web/src/components/storage/ProposalActionsSection.test.jsx b/web/src/components/storage/ProposalActionsSection.test.jsx index 1f425aef7c..846e6fda20 100644 --- a/web/src/components/storage/ProposalActionsSection.test.jsx +++ b/web/src/components/storage/ProposalActionsSection.test.jsx @@ -114,14 +114,14 @@ describe("when there are actions", () => { ); - const link = screen.getByText(/Show.*subvolumes actions/); + const link = screen.getByText(/Show.*subvolume actions/); expect(screen.getAllByRole("list").length).toEqual(1); await user.click(link); waitForElementToBeRemoved(link); - screen.getByText(/Hide.*subvolumes actions/); + screen.getByText(/Hide.*subvolume actions/); // For now, we know that there are two lists and the subvolume list is the second one. // The test could be simplified once we have aria-descriptions for the lists. diff --git a/web/src/components/storage/ProposalPage.jsx b/web/src/components/storage/ProposalPage.jsx index 44ddf89368..673e54ed60 100644 --- a/web/src/components/storage/ProposalPage.jsx +++ b/web/src/components/storage/ProposalPage.jsx @@ -22,6 +22,7 @@ import React, { useCallback, useReducer, useEffect } from "react"; import { Alert } from "@patternfly/react-core"; +import { _ } from "~/i18n"; import { useInstallerClient } from "~/context/installer"; import { toValidationError, useCancellablePromise } from "~/utils"; import { Icon } from "~/components/layout"; @@ -146,7 +147,7 @@ export default function ProposalPage() { } - title="Devices will not be modified until installation starts." + title={_("Devices will not be modified until installation starts.")} /> + // TRANSLATORS: page title + diff --git a/web/src/components/storage/ProposalPageOptions.jsx b/web/src/components/storage/ProposalPageOptions.jsx index 0ea11fc01b..447315c4fe 100644 --- a/web/src/components/storage/ProposalPageOptions.jsx +++ b/web/src/components/storage/ProposalPageOptions.jsx @@ -21,6 +21,8 @@ import React, { useEffect, useState } from "react"; import { useHref } from "react-router-dom"; + +import { _ } from "~/i18n"; import { useInstallerClient } from "~/context/installer"; import { If, PageOptions } from "~/components/core"; @@ -35,7 +37,7 @@ const DASDLink = () => { DASD @@ -53,7 +55,7 @@ const ZFCPLink = () => { zFCP @@ -71,7 +73,7 @@ const ISCSILink = () => { iSCSI diff --git a/web/src/components/storage/ProposalSettingsSection.jsx b/web/src/components/storage/ProposalSettingsSection.jsx index ed2506d236..072888928b 100644 --- a/web/src/components/storage/ProposalSettingsSection.jsx +++ b/web/src/components/storage/ProposalSettingsSection.jsx @@ -26,6 +26,7 @@ import { Tooltip } from "@patternfly/react-core"; +import { _ } from "~/i18n"; import { If, PasswordAndConfirmationInput, Section, Popup } from "~/components/core"; import { DeviceSelector, ProposalVolumes } from "~/components/storage"; import { Icon } from "~/components/layout"; @@ -108,7 +109,7 @@ const InstallationDeviceField = ({ current, devices, isLoading, onChange }) => { }; const DeviceContent = ({ device }) => { - const text = device || "No device selected yet"; + const text = device || _("No device selected yet"); return ; }; @@ -117,23 +118,23 @@ const InstallationDeviceField = ({ current, devices, isLoading, onChange }) => { return ; } - const description = "Select in which device to install the system. All the file systems will " + - "be created on the selected device."; + const description = _("Select the device for installing the system. All the \ +file systems will be created on the selected device."); return ( <>
- Installation device + {_("Installation device")}
No devices found} + then={
{_("No devices found")}
} else={ { type="submit" isDisabled={devices.length === 0} > - Accept + {_("Accept")} @@ -183,7 +184,7 @@ const LVMField = ({ selected: selectedProp, isLoading, onChange }) => { return ( { return ( - @@ -302,14 +303,14 @@ const EncryptionPasswordField = ({ selected: selectedProp, password: passwordPro
{ selected && }
- + - Accept + {_("Accept")} @@ -370,7 +371,7 @@ export default function ProposalSettingsSection({ const encryption = settings.encryptionPassword !== undefined && settings.encryptionPassword.length > 0; return ( -
+
{ return ( <> - These limits are affected by: + {/* TRANSLATORS: header for a list of items */} + {_("These limits are affected by:")} {volume.snapshotsAffectSizes && - The configuration of snapshots} + // TRANSLATORS: list item, this affects the computed partition size limits + {_("The configuration of snapshots")}} {volume.sizeRelevantVolumes && volume.sizeRelevantVolumes.length > 0 && - Presence of other volumes ({volume.sizeRelevantVolumes.join(", ")})} + // TRANSLATORS: list item, this affects the computed partition size limits + // %s is replaced by a list of the volumes (like "/home, /boot") + {format(_("Presence of other volumes (%s)"), volume.sizeRelevantVolumes.join(", "))}} ); @@ -109,30 +115,33 @@ const GeneralActions = ({ templates, onAdd, onReset }) => { key="reset" onClick={onReset} > - Reset to defaults + {/* TRANSLATORS: dropdown menu label */} + {_("Reset to defaults")} , - Add file system + {/* TRANSLATORS: dropdown menu label */} + {_("Add file system")} ]} toggle={ - Actions + {/* TRANSLATORS: dropdown label */} + {_("Actions")} } /> - + - Accept + {_("Accept")} @@ -173,12 +182,14 @@ const VolumeRow = ({ columns, volume, isLoading, onEdit, onDelete }) => { let size = minSize; if (minSize && maxSize && minSize !== maxSize) size = `${minSize} - ${maxSize}`; - if (maxSize === undefined) size = `At least ${minSize}`; + // TRANSLATORS: minimum device size, %s is replaced by size string, e.g. "17.5 GiB" + if (maxSize === undefined) size = format(_("At least %s"), minSize); return (
{size} - auto} /> + {/* TRANSLATORS: device flag, the partition size is automatically computed */} + {_("auto")}} />
); }; @@ -187,15 +198,18 @@ const VolumeRow = ({ columns, volume, isLoading, onEdit, onDelete }) => { const isLv = volume.deviceType === "lvm_lv"; const hasSnapshots = volume.fsType === "Btrfs" && volume.snapshots; - const text = `${volume.fsType} ${isLv ? "logical volume" : "partition"}`; + // TRANSLATORS: the filesystem uses a logical volume (LVM) + const text = `${volume.fsType} ${isLv ? _("logical volume") : _("partition")}`; const lockIcon = ; const snapshotsIcon = ; return (
{text} - encrypted} /> - with snapshots} /> + {/* TRANSLATORS: filesystem flag, it uses an encryption */} + {_("encrypted")}} /> + {/* TRANSLATORS: filesystem flag, it allows creating snapshots */} + {_("with snapshots")}} />
); }; @@ -204,12 +218,12 @@ const VolumeRow = ({ columns, volume, isLoading, onEdit, onDelete }) => { const actions = () => { const actions = { delete: { - title: "Delete", + title: _("Delete"), onClick: () => onDelete(volume), className: "danger-action" }, edit: { - title: "Edit", + title: _("Edit"), onClick: () => onEdit(volume) } }; @@ -250,7 +264,7 @@ const VolumeRow = ({ columns, volume, isLoading, onEdit, onDelete }) => { - + { onSubmit={acceptForm} /> - Accept + {_("Accept")} @@ -281,10 +295,10 @@ const VolumeRow = ({ columns, volume, isLoading, onEdit, onDelete }) => { */ const VolumesTable = ({ volumes, isLoading, onVolumesChange }) => { const columns = { - mountPoint: "At", - details: "Details", - size: "Size", - actions: "Actions" + mountPoint: _("Mount point"), + details: _("Details"), + size: _("Size"), + actions: _("Actions") }; const VolumesContent = ({ volumes, isLoading, onVolumesChange }) => { @@ -318,7 +332,7 @@ const VolumesTable = ({ volumes, isLoading, onVolumesChange }) => { }; return ( - + {columns.mountPoint} @@ -374,7 +388,7 @@ export default function ProposalVolumes({ - File systems to create in your system + {_("File systems to create in your system")} { return ( - { units.map(unit => ) } + {/* the unit values are marked for translation in the utils.js file */} + { units.map(unit => ) } ); }; @@ -89,16 +92,25 @@ const SizeAuto = ({ volume }) => { const conditions = []; if (volume.snapshotsAffectSizes) - conditions.push("the configuration of snapshots"); + // TRANSLATORS: item which affects the final computed partition size + conditions.push(_("the configuration of snapshots")); if (volume.sizeRelevantVolumes && volume.sizeRelevantVolumes.length > 0) - conditions.push(`the presence of the file system for ${volume.sizeRelevantVolumes.join(", ")}.`); + // TRANSLATORS: item which affects the final computed partition size + // %s is replaced by a list of mount points like "/home, /boot" + conditions.push(format(_("the presence of the file system for %s"), + // TRANSLATORS: conjunction for merging two list items + volume.sizeRelevantVolumes.join(_(", ")))); - const conditionsText = `The final size depends on ${conditions.join(" and ")}`; + // TRANSLATORS: the %s is replaced by the items which affect the computed size + const conditionsText = format(_("The final size depends on %s."), + // TRANSLATORS: conjunction for merging two texts + conditions.join(_(" and "))); return ( <> -

Automatically calculated size according to the selected product. {conditionsText}

+ {/* TRANSLATORS: the partition size is automatically computed */} +

{_("Automatically calculated size according to the selected product.")}{" "}{conditionsText}

); }; @@ -118,7 +130,7 @@ const SizeManual = ({ errors, formData, onChange }) => { return (

- Exact size for the file system. + {_("Exact size for the file system.")}

{ onChange({ size })} validated={errors.size && 'error'} /> onChange({ sizeUnit })} @@ -163,13 +182,14 @@ const SizeRange = ({ errors, formData, onChange }) => { return (

- Limits for the file system size. The final size will be a value between the given minimum - and maximum sizes. If no maximum is given, then the file system will be as big as possible. + {_("Limits for the file system size. The final size will be a value between the given minimum \ +and maximum. If no maximum is given then the file system will be as big as possible.")}

{ onChange({ minSize })} validated={errors.minSize && 'error'} /> onChange({ minSizeUnit })} @@ -194,7 +215,8 @@ const SizeRange = ({ errors, formData, onChange }) => { { id="maxSize" name="maxSize" validated={errors.maxSize && 'error'} - aria-label="Maximum desired size" + // TRANSLATORS: the maximum partition size + aria-label={_("Maximum desired size")} value={formData.maxSize} onChange={(maxSize) => onChange({ maxSize })} /> onChange({ maxSizeUnit })} @@ -224,6 +247,16 @@ const SizeRange = ({ errors, formData, onChange }) => { ); }; +// constants need to be marked for translation with N_() and translated with _() later +const SIZE_OPTION_LABELS = Object.freeze({ + // TRANSLATORS: radio button label, fully automatically computed partition size, no user input + auto: N_("Auto"), + // TRANSLATORS: radio button label, exact partition size requested by user + fixed: N_("Fixed"), + // TRANSLATORS: radio button label, automatically computed partition size within the user provided min and max limits + range: N_("Range") +}); + /** * Widget for rendering the volume size options * @component @@ -254,7 +287,7 @@ const SizeOptions = ({ errors, formData, volume, onChange }) => { - + - + - + diff --git a/web/src/components/storage/VolumeForm.test.jsx b/web/src/components/storage/VolumeForm.test.jsx index f72db4b80f..522a57239c 100644 --- a/web/src/components/storage/VolumeForm.test.jsx +++ b/web/src/components/storage/VolumeForm.test.jsx @@ -107,14 +107,14 @@ it("renders controls for setting the desired size", () => { it("uses the default size unit when min size unit is missing", () => { plainRender(); - const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Max size unit" }); + const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Unit for the maximum size" }); expect(maxSizeUnitSelector).toHaveValue(DEFAULT_SIZE_UNIT); }); it("uses the min size unit as max size unit when it is missing", () => { plainRender(); - const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Max size unit" }); + const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Unit for the maximum size" }); expect(maxSizeUnitSelector).toHaveValue("TiB"); }); @@ -144,10 +144,10 @@ it("calls the onSubmit callback with resulting volume when the form is submitted await user.click(rangeSize); const minSizeInput = screen.getByRole("textbox", { name: "Minimum desired size" }); - const minSizeUnitSelector = screen.getByRole("combobox", { name: "Min size unit" }); + const minSizeUnitSelector = screen.getByRole("combobox", { name: "Unit for the minimum size" }); const minSizeGiBUnit = within(minSizeUnitSelector).getByRole("option", { name: "GiB" }); const maxSizeInput = screen.getByRole("textbox", { name: "Maximum desired size" }); - const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Max size unit" }); + const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Unit for the maximum size" }); const maxSizeGiBUnit = within(maxSizeUnitSelector).getByRole("option", { name: "GiB" }); await user.clear(minSizeInput); @@ -216,10 +216,10 @@ describe("size validations", () => { await user.click(rangeSize); const minSizeInput = screen.getByRole("textbox", { name: "Minimum desired size" }); - const minSizeUnitSelector = screen.getByRole("combobox", { name: "Min size unit" }); + const minSizeUnitSelector = screen.getByRole("combobox", { name: "Unit for the minimum size" }); const minSizeMiBUnit = within(minSizeUnitSelector).getByRole("option", { name: "MiB" }); const maxSizeInput = screen.getByRole("textbox", { name: "Maximum desired size" }); - const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Max size unit" }); + const maxSizeUnitSelector = screen.getByRole("combobox", { name: "Unit for the maximum size" }); const maxSizeGiBUnit = within(maxSizeUnitSelector).getByRole("option", { name: "GiB" }); const maxSizeMiBUnit = within(maxSizeUnitSelector).getByRole("option", { name: "MiB" }); diff --git a/web/src/components/storage/ZFCPDiskForm.jsx b/web/src/components/storage/ZFCPDiskForm.jsx index ca4bfed716..38fa508187 100644 --- a/web/src/components/storage/ZFCPDiskForm.jsx +++ b/web/src/components/storage/ZFCPDiskForm.jsx @@ -26,6 +26,8 @@ import { Alert, Form, FormGroup, FormSelect, FormSelectOption } from "@patternfly/react-core"; + +import { _ } from "~/i18n"; import { If } from "~/components/core"; import { noop } from "~/utils"; @@ -107,13 +109,13 @@ export default function ZFCPDiskForm({ id, luns = [], onSubmit = noop, onLoading -

The zFCP disk was not activated.

+ +

{_("The zFCP disk was not activated.")}

} />
- + )} - + {/* TRANSLATORS: abbrev. World Wide Port Name */} + )} - + {/* TRANSLATORS: abbrev. Logical Unit Number */} + { const { cancellablePromise } = useCancellablePromise(); const columns = [ - { id: "channel", label: "Channel ID" }, - { id: "status", label: "Status" }, - { id: "lunScan", label: "Auto LUNs Scan" } + { id: "channel", label: _("Channel ID") }, + { id: "status", label: _("Status") }, + { id: "lunScan", label: _("Auto LUNs Scan") } ]; const columnValue = (controller, column) => { @@ -311,11 +313,11 @@ const ControllersTable = ({ client, manager }) => { value = controller.channel; break; case "status": - value = controller.active ? "Activated" : "Deactivated"; + value = controller.active ? _("Activated") : _("Deactivated"); break; case "lunScan": if (controller.active) - value = controller.lunScan ? "Yes" : "No"; + value = controller.lunScan ? _("Yes") : _("No"); else value = "-"; break; @@ -329,7 +331,7 @@ const ControllersTable = ({ client, manager }) => { return [ { - label: "Activate", + label: _("Activate"), run: async () => await cancellablePromise(client.activateController(controller)) } ]; @@ -357,10 +359,10 @@ const DisksTable = ({ client, manager }) => { const { cancellablePromise } = useCancellablePromise(); const columns = [ - { id: "name", label: "Name" }, - { id: "channel", label: "Channel ID" }, - { id: "wwpn", label: "WWPN" }, - { id: "lun", label: "LUN" } + { id: "name", label: _("Name") }, + { id: "channel", label: _("Channel ID") }, + { id: "wwpn", label: _("WWPN") }, + { id: "lun", label: _("LUN") } ]; const columnValue = (disk, column) => disk[column.id]; @@ -371,7 +373,7 @@ const DisksTable = ({ client, manager }) => { return [ { - label: "Deactivate", + label: _("Deactivate"), run: async () => await cancellablePromise( client.deactivateDisk(controller, disk.wwpn, disk.lun) ) @@ -414,31 +416,30 @@ const ControllersSection = ({ client, manager, load = noop, isLoading = false }) const EmptyState = () => { return (
-
No zFCP controllers found
-
Please, try to read the zFCP devices again.
- +
{_("No zFCP controllers found")}
+
{_("Please, try to read the zFCP devices again.")}
+ {/* TRANSLATORS: button label */} +
); }; const Content = () => { const LUNScanInfo = () => { + const msg = allowLUNScan + // TRANSLATORS: the text in the square brackets [] will be displayed in bold + ? _("Automatic LUN scan is [enabled]. Activating a controller which is \ +running in NPIV mode will automatically configures all its LUNs.") + // TRANSLATORS: the text in the square brackets [] will be displayed in bold + : _("Automatic LUN scan is [disabled]. LUNs have to be manually \ +configured after activating a controller."); + + const [msgStart, msgBold, msgEnd] = msg.split(/[[\]]/); + return ( - - Automatic LUN scan is enabled. Activating a controller which is running in NPIV - mode will automatically configures all its LUNs. -

- } - else={ -

- Automatic LUN scan is disabled. LUNs have to be manually configured after - activating a controller. -

- } - /> +

+ {msgStart}{msgBold}{msgEnd} +

); }; @@ -496,7 +497,7 @@ const DiskPopup = ({ client, manager, onClose = noop }) => { const formId = "ZFCPDiskForm"; return ( - + { type="submit" isDisabled={isAcceptDisabled} > - Accept + {_("Accept")} @@ -535,22 +536,23 @@ const DisksSection = ({ client, manager, isLoading = false }) => { const EmptyState = () => { const NoActiveControllers = () => { return ( -
Please, try to activate a zFCP controller.
+
{_("Please, try to activate a zFCP controller.")}
); }; const NoActiveDisks = () => { return ( <> -
Please, try to activate a zFCP disk.
- +
{_("Please, try to activate a zFCP disk.")}
+ {/* TRANSLATORS: button label */} + ); }; return (
-
No zFCP disks found
+
{_("No zFCP disks found")}
} @@ -568,7 +570,8 @@ const DisksSection = ({ client, manager, isLoading = false }) => { - + {/* TRANSLATORS: button label */} + @@ -579,7 +582,8 @@ const DisksSection = ({ client, manager, isLoading = false }) => { }; return ( -
+ // TRANSLATORS: section title +
} @@ -726,9 +730,10 @@ export default function ZFCPPage() { }, [client.zfcp, cancellablePromise, getLUNs]); return ( - + // TRANSLATORS: page title + - +