From b256ac1e1fa0148eca99efa826a5640b63398059 Mon Sep 17 00:00:00 2001 From: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:19:00 -0600 Subject: [PATCH] Added localizations 3rd batch (#2413) * Added localizations 3rd batch * Fix unit tests --- .../AddCollectionPanel/AddCollectionPanel.tsx | 175 +++++----- .../AddCollectionPanelUtility.tsx | 49 +-- .../AddCollectionPanel.test.tsx.snap | 7 +- .../AddDatabasePanel/AddDatabasePanel.tsx | 29 +- .../AddGlobalSecondaryIndexPanel.tsx | 18 +- .../CassandraAddCollectionPane.tsx | 38 +-- .../ChangePartitionKeyPane.tsx | 51 ++- .../CopyNotebookPane/CopyNotebookPane.tsx | 8 +- .../CopyNotebookPaneComponent.tsx | 8 +- .../DeleteCollectionConfirmationPane.tsx | 26 +- ...teCollectionConfirmationPane.test.tsx.snap | 4 +- .../Panes/DeleteDatabaseConfirmationPanel.tsx | 30 +- .../ExecuteSprocParamsPane.tsx | 24 +- .../ExecuteSprocParamsPane/InputParameter.tsx | 6 +- .../GraphStylingPanel/GraphStylingPanel.tsx | 4 +- .../Panes/LoadQueryPane/LoadQueryPane.tsx | 14 +- .../Panes/NewVertexPanel/NewVertexPanel.tsx | 4 +- .../Panes/PanelInfoErrorComponent.tsx | 12 +- .../PublishNotebookPane.tsx | 10 +- .../PublishNotebookPaneComponent.tsx | 44 +-- .../Panes/SaveQueryPane/SaveQueryPane.tsx | 28 +- .../Panes/SettingsPane/SettingsPane.tsx | 239 +++++++------- .../__snapshots__/SettingsPane.test.tsx.snap | 7 +- .../TableColumnSelectionPane.tsx | 16 +- .../Panes/Tables/AddTableEntityPanel.tsx | 12 +- .../Panes/Tables/EditTableEntityPanel.tsx | 10 +- .../TableQuerySelectPanel.tsx | 8 +- .../Panes/UploadItemsPane/UploadItemsPane.tsx | 20 +- ...eteDatabaseConfirmationPanel.test.tsx.snap | 4 +- src/Localization/en/Resources.json | 311 ++++++++++++++++++ 30 files changed, 801 insertions(+), 415 deletions(-) diff --git a/src/Explorer/Panes/AddCollectionPanel/AddCollectionPanel.tsx b/src/Explorer/Panes/AddCollectionPanel/AddCollectionPanel.tsx index 205b3eb94..59d0f56e5 100644 --- a/src/Explorer/Panes/AddCollectionPanel/AddCollectionPanel.tsx +++ b/src/Explorer/Panes/AddCollectionPanel/AddCollectionPanel.tsx @@ -42,6 +42,8 @@ import { } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility"; import { useSidePanel } from "hooks/useSidePanel"; import { useTeachingBubble } from "hooks/useTeachingBubble"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { DEFAULT_FABRIC_NATIVE_CONTAINER_THROUGHPUT, isFabricNative } from "Platform/Fabric/FabricUtil"; import React from "react"; import { CollectionCreation } from "Shared/Constants"; @@ -177,7 +179,7 @@ export class AddCollectionPanel extends React.Component )} @@ -274,17 +276,17 @@ export class AddCollectionPanel extends React.Component @@ -304,7 +306,7 @@ export class AddCollectionPanel extends React.Component - Create new + {t(Keys.panes.addCollection.createNew)} - Use existing + {t(Keys.panes.addCollection.useExisting)} )} @@ -347,7 +349,9 @@ export class AddCollectionPanel extends React.Component @@ -424,14 +428,18 @@ export class AddCollectionPanel extends React.Component @@ -445,10 +453,10 @@ export class AddCollectionPanel extends React.Component) => this.setState({ collectionId: event.target.value }) @@ -462,7 +470,7 @@ export class AddCollectionPanel extends React.Component - Indexing + {t(Keys.panes.addCollection.indexing)} @@ -470,32 +478,32 @@ export class AddCollectionPanel extends React.Component - Automatic + {t(Keys.panes.addCollection.automatic)} - Off + {t(Keys.panes.addCollection.off)} {this.getFreeTierIndexingText()}{" "} - Learn more + {t(Keys.common.learnMore)} @@ -508,21 +516,17 @@ export class AddCollectionPanel extends React.Component - Sharding + {t(Keys.panes.addCollection.sharding)} @@ -531,7 +535,7 @@ export class AddCollectionPanel extends React.Component - Unsharded (20GB limit) + {t(Keys.panes.addCollection.unshardedLabel)} - Sharded + {t(Keys.panes.addCollection.sharded)} )} @@ -679,15 +683,14 @@ export class AddCollectionPanel extends React.Component= Constants.BackendDefaults.maxNumMultiHashPartition} onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })} > - Add hierarchical partition key + {t(Keys.panes.addCollection.addPartitionKey)} {this.state.subPartitionKeys.length > 0 && ( - This feature allows you to - partition your data with up to three levels of keys for better data distribution. Requires .NET - V3, Java V4 SDK, or preview JavaScript V3 SDK.{" "} + {" "} + {t(Keys.panes.addCollection.hierarchicalPartitionKeyInfo)}{" "} - Learn more + {t(Keys.common.learnMore)} )} @@ -700,7 +703,9 @@ export class AddCollectionPanel extends React.Component @@ -769,8 +770,8 @@ export class AddCollectionPanel extends React.Component this.setState({ uniqueKeys: [...this.state.uniqueKeys, ""] })} > - Add unique key + {t(Keys.panes.addCollection.addUniqueKey)} )} @@ -823,7 +824,7 @@ export class AddCollectionPanel extends React.Component - On + {t(Keys.panes.addCollection.on)} - Off + {t(Keys.panes.addCollection.off)} {!isSynapseLinkEnabled() && ( - Azure Synapse Link is required for creating an analytical store{" "} - {getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account.
+ {t(Keys.panes.addCollection.analyticalStoreSynapseLinkRequired, { + collectionName: getCollectionName().toLocaleLowerCase(), + })}{" "} +
- Learn more + {t(Keys.common.learnMore)}
this.props.explorer.openEnableSynapseLinkDialog()} style={{ height: 27, width: 80 }} styles={{ label: { fontSize: 12 } }} @@ -878,7 +881,7 @@ export class AddCollectionPanel extends React.Component { scrollToSection("collapsibleVectorPolicySectionContent"); @@ -906,7 +909,7 @@ export class AddCollectionPanel extends React.Component { scrollToSection("collapsibleFullTextPolicySectionContent"); @@ -935,7 +938,7 @@ export class AddCollectionPanel extends React.Component { TelemetryProcessor.traceOpen(Action.ExpandAddCollectionPaneAdvancedSection); @@ -948,23 +951,23 @@ export class AddCollectionPanel extends React.Component - Indexing + {t(Keys.panes.addCollection.indexing)}
- To ensure compatibility with older SDKs, the - created container will use a legacy partitioning scheme that supports partition key values of size - only up to 101 bytes. If this is enabled, you will not be able to use hierarchical partition keys.{" "} + {t(Keys.panes.addCollection.legacySdkInfo)}{" "} - Learn more + {t(Keys.common.learnMore)} @@ -1018,7 +1019,7 @@ export class AddCollectionPanel extends React.Component {!this.props.isCopyJobFlow && ( - + )} {this.state.isExecuting && ( @@ -1044,7 +1045,7 @@ export class AddCollectionPanel extends React.Component )} @@ -1150,8 +1151,8 @@ export class AddCollectionPanel extends React.Component CollectionCreation.DefaultCollectionRUs100K && !this.isCostAcknowledged) { const errorMessage = this.isNewDatabaseAutoscale - ? "Please acknowledge the estimated monthly spend." - : "Please acknowledge the estimated daily spend."; + ? t(Keys.panes.addCollection.acknowledgeSpendErrorMonthly) + : t(Keys.panes.addCollection.acknowledgeSpendErrorDaily); this.setState({ errorMessage }); return false; } if (throughput > CollectionCreation.MaxRUPerPartition && !this.state.isSharded) { - this.setState({ errorMessage: "Unsharded collections support up to 10,000 RUs" }); + this.setState({ errorMessage: t(Keys.panes.addCollection.unshardedMaxRuError) }); return false; } @@ -1270,12 +1271,12 @@ export class AddCollectionPanel extends React.Component - Unique keys + {t(Keys.panes.addCollectionUtility.uniqueKeysLabel)} @@ -99,12 +103,11 @@ export function shouldShowAnalyticalStoreOptions(): boolean { } export function AnalyticalStoreHeader(): JSX.Element { - const tooltipContent = - "Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads."; + const tooltipContent = t(Keys.panes.addCollectionUtility.analyticalStoreTooltip); return ( - Analytical Store + {t(Keys.panes.addCollectionUtility.analyticalStoreLabel)} @@ -116,14 +119,13 @@ export function AnalyticalStoreHeader(): JSX.Element { export function AnalyticalStorageContent(): JSX.Element { return ( - Enable analytical store capability to perform near real-time analytics on your operational data, without impacting - the performance of transactional workloads.{" "} + {t(Keys.panes.addCollectionUtility.analyticalStoreDescription)}{" "} - Learn more + {t(Keys.common.learnMore)} ); @@ -155,10 +157,9 @@ export function scrollToSection(id: string): void { export function ContainerVectorPolicyTooltipContent(): JSX.Element { return ( - Describe any properties in your data that contain vectors, so that they can be made available for similarity - queries.{" "} + {t(Keys.panes.addCollectionUtility.vectorPolicyTooltip)}{" "} - Learn more + {t(Keys.common.learnMore)} ); diff --git a/src/Explorer/Panes/AddCollectionPanel/__snapshots__/AddCollectionPanel.test.tsx.snap b/src/Explorer/Panes/AddCollectionPanel/__snapshots__/AddCollectionPanel.test.tsx.snap index a43b63569..1b521e9a8 100644 --- a/src/Explorer/Panes/AddCollectionPanel/__snapshots__/AddCollectionPanel.test.tsx.snap +++ b/src/Explorer/Panes/AddCollectionPanel/__snapshots__/AddCollectionPanel.test.tsx.snap @@ -482,10 +482,8 @@ exports[`AddCollectionPanel should render Default properly 1`] = ` } variant="small" > - Azure Synapse Link is required for creating an analytical store + Azure Synapse Link is required for creating an analytical store container. Enable Synapse Link for this Cosmos DB account. - container - . Enable Synapse Link for this Cosmos DB account.
- To ensure compatibility with older SDKs, the created container will use a legacy partitioning scheme that supports partition key values of size only up to 101 bytes. If this is enabled, you will not be able to use hierarchical partition keys. + + To ensure compatibility with older SDKs, the created container will use a legacy partitioning scheme that supports partition key values of size only up to 101 bytes. If this is enabled, you will not be able to use hierarchical partition keys. = ({ const isCassandraAccount: boolean = userContext.apiType === "Cassandra"; const databaseLabel: string = isCassandraAccount ? "keyspace" : "database"; const collectionsLabel: string = isCassandraAccount ? "tables" : "collections"; - const databaseIdLabel: string = isCassandraAccount ? "Keyspace id" : "Database id"; - const databaseIdPlaceHolder: string = isCassandraAccount ? "Type a new keyspace id" : "Type a new database id"; + const databaseIdLabel: string = isCassandraAccount + ? t(Keys.panes.addDatabase.keyspaceIdLabel) + : t(Keys.panes.addDatabase.databaseIdLabel); + const databaseIdPlaceHolder: string = t(Keys.panes.addDatabase.databaseIdPlaceholder, { databaseLabel }); const [databaseId, setDatabaseId] = useState(""); - const databaseIdTooltipText = `A ${ - isCassandraAccount ? "keyspace" : "database" - } is a logical container of one or more ${isCassandraAccount ? "tables" : "collections"}`; + const databaseIdTooltipText = t(Keys.panes.addDatabase.databaseTooltip, { databaseLabel, collectionsLabel }); - const databaseLevelThroughputTooltipText = `Provisioned throughput at the ${databaseLabel} level will be shared across all ${collectionsLabel} within the ${databaseLabel}.`; + const databaseLevelThroughputTooltipText = t(Keys.panes.addDatabase.shareThroughputTooltip, { + databaseLabel, + collectionsLabel, + }); const [databaseCreateNewShared, setDatabaseCreateNewShared] = useState( getNewDatabaseSharedThroughputDefault(), ); @@ -144,15 +149,15 @@ export const AddDatabasePanel: FunctionComponent = ({ // TODO add feature flag that disables validation for customers with custom accounts if (isAutoscaleSelected) { if (!AutoPilotUtils.isValidAutoPilotThroughput(throughput)) { - setFormErrors( - `Please enter a value greater than ${AutoPilotUtils.autoPilotThroughput1K} for autopilot throughput`, - ); + setFormErrors(t(Keys.panes.addDatabase.greaterThanError, { minValue: AutoPilotUtils.autoPilotThroughput1K })); return false; } } if (throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !isCostAcknowledged) { - setFormErrors(`Please acknowledge the estimated ${isAutoscaleSelected ? "monthly" : "daily"} spend.`); + setFormErrors( + t(Keys.panes.addDatabase.acknowledgeSpendError, { period: isAutoscaleSelected ? "monthly" : "daily" }), + ); return false; } @@ -169,7 +174,7 @@ export const AddDatabasePanel: FunctionComponent = ({ const props: RightPaneFormProps = { formError: formErrors, isExecuting, - submitButtonText: "OK", + submitButtonText: t(Keys.common.ok), isSubmitButtonDisabled: isThroughputCapExceeded, onSubmit, }; @@ -187,7 +192,7 @@ export const AddDatabasePanel: FunctionComponent = ({ messageType="info" showErrorDetails={false} link={Constants.Urls.freeTierInformation} - linkText="Learn more" + linkText={t(Keys.common.learnMore)} /> )}
diff --git a/src/Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel.tsx b/src/Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel.tsx index 4e2c67026..ce768e552 100644 --- a/src/Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel.tsx +++ b/src/Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel.tsx @@ -40,6 +40,8 @@ import { PanelInfoErrorComponent } from "Explorer/Panes/PanelInfoErrorComponent" import { PanelLoadingScreen } from "Explorer/Panes/PanelLoadingScreen"; import { useDatabases } from "Explorer/useDatabases"; import { useSidePanel } from "hooks/useSidePanel"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import { CollectionCreation } from "Shared/Constants"; import { Action } from "Shared/Telemetry/TelemetryConstants"; @@ -168,19 +170,19 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel } if (globalSecondaryIndexThroughput > CollectionCreation.DefaultCollectionRUs100K && !isCostAcknowledged) { - const errorMessage: string = "Please acknowledge the estimated monthly spend."; + const errorMessage: string = t(Keys.panes.addCollection.acknowledgeSpendErrorMonthly); setErrorMessage(errorMessage); return false; } if (showVectorSearchParameters()) { if (!vectorPolicyValidated) { - setErrorMessage("Please fix errors in container vector policy"); + setErrorMessage(t(Keys.panes.addCollection.vectorPolicyError)); return false; } if (!fullTextPolicyValidated) { - setErrorMessage("Please fix errors in container full text search policy"); + setErrorMessage(t(Keys.panes.addCollection.fullTextSearchPolicyError)); return false; } } @@ -307,7 +309,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel - Global secondary index container id + {t(Keys.panes.addGlobalSecondaryIndex.globalSecondaryIndexId)} - Learn more about defining global secondary indexes. + {t(Keys.panes.addGlobalSecondaryIndex.projectionQueryTooltip)} } > @@ -349,7 +351,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel aria-required required autoComplete="off" - placeholder={"SELECT c.email, c.accountId FROM c"} + placeholder={t(Keys.panes.addGlobalSecondaryIndex.projectionQueryPlaceholder)} size={40} className="panelTextField" value={definition || ""} @@ -393,7 +395,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
- + {isExecuting && } ); diff --git a/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx b/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx index 49fe95ca5..1bf31e5b5 100644 --- a/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx +++ b/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx @@ -2,14 +2,16 @@ import { Checkbox, Dropdown, IDropdownOption, Link, Stack, Text, TextField } fro import * as Constants from "Common/Constants"; import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils"; import { InfoTooltip } from "Common/Tooltip/InfoTooltip"; +import { useSidePanel } from "hooks/useSidePanel"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; +import React, { FunctionComponent, useState } from "react"; import * as SharedConstants from "Shared/Constants"; import { Action } from "Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor"; import { userContext } from "UserContext"; import { isServerlessAccount } from "Utils/CapabilityUtils"; import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils"; -import { useSidePanel } from "hooks/useSidePanel"; -import React, { FunctionComponent, useState } from "react"; import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput"; import Explorer from "../../Explorer"; import { CassandraAPIDataClient } from "../../Tables/TableDataClient"; @@ -71,8 +73,8 @@ export const CassandraAddCollectionPane: FunctionComponent SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !isCostAcknowledged) { const errorMessage = isNewKeySpaceAutoscale || isTableAutoscale - ? "Please acknowledge the estimated monthly spend." - : "Please acknowledge the estimated daily spend."; + ? t(Keys.panes.addCollection.acknowledgeSpendErrorMonthly) + : t(Keys.panes.addCollection.acknowledgeSpendErrorDaily); setFormError(errorMessage); return; } @@ -149,7 +151,7 @@ export const CassandraAddCollectionPane: FunctionComponent - Keyspace name Select an existing keyspace or enter a new keyspace id. + {t(Keys.panes.cassandraAddCollection.keyspaceLabel)}{" "} + {t(Keys.panes.cassandraAddCollection.keyspaceTooltip)}
@@ -179,7 +182,7 @@ export const CassandraAddCollectionPane: FunctionComponent - Create new + {t(Keys.panes.addCollection.createNew)} - Use existing + {t(Keys.panes.addCollection.useExisting)} {keyspaceCreateNew && ( @@ -275,9 +278,9 @@ export const CassandraAddCollectionPane: FunctionComponent - Enter CQL command to create the table.{" "} + {t(Keys.panes.cassandraAddCollection.tableIdLabel)}{" "} - Learn More + {t(Keys.common.learnMore)} @@ -295,7 +298,7 @@ export const CassandraAddCollectionPane: FunctionComponent setTableId(newValue)} @@ -307,7 +310,7 @@ export const CassandraAddCollectionPane: FunctionComponent setUserTableQuery(newValue)} /> @@ -318,17 +321,12 @@ export const CassandraAddCollectionPane: FunctionComponent setDedicateTableThroughput(e.target.checked)} /> - Provision dedicated throughput for this table - - You can optionally provision dedicated throughput for a table within a keyspace that has throughput - provisioned. This dedicated throughput amount will not be shared with other tables in the keyspace and - does not count towards the throughput you provisioned for the keyspace. This throughput amount will be - billed in addition to the throughput amount you provisioned at the keyspace level. - + {t(Keys.panes.cassandraAddCollection.provisionDedicatedThroughput)} + {t(Keys.panes.cassandraAddCollection.provisionDedicatedThroughputTooltip)} )} {!isServerlessAccount() && (!isKeyspaceShared || dedicateTableThroughput) && ( diff --git a/src/Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane.tsx b/src/Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane.tsx index 8f03b3f79..4ee6437ea 100644 --- a/src/Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane.tsx +++ b/src/Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane.tsx @@ -26,6 +26,8 @@ import { import Explorer from "Explorer/Explorer"; import { RightPaneForm } from "Explorer/Panes/RightPaneForm/RightPaneForm"; import { useDatabases } from "Explorer/useDatabases"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { userContext } from "UserContext"; import { getCollectionName } from "Utils/APITypeUtils"; import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils"; @@ -72,7 +74,7 @@ export const ChangePartitionKeyPane: React.FC = ({ await createDataTransferJob(); await onClose(); } catch (error) { - handleError(error, "ChangePartitionKey", "Failed to start data transfer job"); + handleError(error, "ChangePartitionKey", t(Keys.panes.changePartitionKey.failedToStartError)); } setIsExecuting(false); useSidePanel.getState().closeSidePanel(); @@ -133,17 +135,21 @@ export const ChangePartitionKeyPane: React.FC = ({ }; return ( - + - When changing a container’s partition key, you will need to create a destination container with the correct - partition key. You may also select an existing destination container.  + {t(Keys.panes.changePartitionKey.description)}  - Learn more + {t(Keys.common.learnMore)} @@ -218,14 +224,18 @@ export const ChangePartitionKeyPane: React.FC = ({ @@ -239,10 +249,14 @@ export const ChangePartitionKeyPane: React.FC = ({ autoComplete="off" pattern={ValidCosmosDbIdInputPattern.source} title={ValidCosmosDbIdDescription} - placeholder={`e.g., ${getCollectionName()}1`} + placeholder={t(Keys.panes.changePartitionKey.collectionIdPlaceholder, { + collectionName: getCollectionName(), + })} size={40} className="panelTextField" - aria-label={`${getCollectionName()} id, Example ${getCollectionName()}1`} + aria-label={t(Keys.panes.changePartitionKey.collectionIdAriaLabel, { + collectionName: getCollectionName(), + })} value={targetCollectionId} onChange={(event: React.ChangeEvent) => setTargetCollectionId(event.target.value)} /> @@ -349,7 +363,7 @@ export const ChangePartitionKeyPane: React.FC = ({ disabled={subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition} onClick={() => setSubPartitionKeys([...subPartitionKeys, ""])} > - Add hierarchical partition key + {t(Keys.panes.addCollection.addPartitionKey)} {subPartitionKeys.length > 0 && ( = ({ variant="small" style={{ color: "var(--colorNeutralForeground1)" }} > - This feature allows you to - partition your data with up to three levels of keys for better data distribution. Requires .NET V3, - Java V4 SDK, or preview JavaScript V3 SDK.{" "} + {" "} + {t(Keys.panes.addCollection.hierarchicalPartitionKeyInfo)}{" "} - Learn more + {t(Keys.common.learnMore)} )} @@ -377,14 +390,18 @@ export const ChangePartitionKeyPane: React.FC = ({ @@ -400,7 +417,7 @@ export const ChangePartitionKeyPane: React.FC = ({ }} defaultSelectedKey={targetCollectionId} responsiveMode={999} - ariaLabel="Existing Containers" + ariaLabel={t(Keys.panes.changePartitionKey.existingContainers)} /> )} diff --git a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx index 0f7927b3b..d623a8db3 100644 --- a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx +++ b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx @@ -4,6 +4,8 @@ import { HttpStatusCodes, PoolIdType } from "../../../Common/Constants"; import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils"; import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService"; import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient"; +import { Keys } from "../../../Localization/Keys.generated"; +import { t } from "../../../Localization/t"; import * as GitHubUtils from "../../../Utils/GitHubUtils"; import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils"; import { useSidePanel } from "../../../hooks/useSidePanel"; @@ -82,14 +84,14 @@ export const CopyNotebookPane: FunctionComponent = ({ const notebookContentItem = await copyNotebook(selectedLocation); if (!notebookContentItem) { - throw new Error(`Failed to upload ${name}`); + throw new Error(t(Keys.panes.copyNotebook.uploadFailedError, { name })); } NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${name} to ${destination}`); closeSidePanel(); } catch (error) { const errorMessage = getErrorMessage(error); - setFormError(`Failed to copy ${name} to ${destination}`); + setFormError(t(Keys.panes.copyNotebook.copyFailedError, { name, destination })); handleError(errorMessage, "CopyNotebookPaneAdapter/submit", formError); } finally { clearMessage && clearMessage(); @@ -136,7 +138,7 @@ export const CopyNotebookPane: FunctionComponent = ({ const props: RightPaneFormProps = { formError, isExecuting: isExecuting, - submitButtonText: "OK", + submitButtonText: t(Keys.common.ok), onSubmit: () => submit(), }; diff --git a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx index 5cd0cfdc1..78a9839fe 100644 --- a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx +++ b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx @@ -12,6 +12,8 @@ import { import { GitHubReposTitle } from "Explorer/Tree/ResourceTree"; import React, { FormEvent, FunctionComponent } from "react"; import { IPinnedRepo } from "../../../Juno/JunoClient"; +import { Keys } from "../../../Localization/Keys.generated"; +import { t } from "../../../Localization/t"; import * as GitHubUtils from "../../../Utils/GitHubUtils"; import { useNotebook } from "../../Notebook/useNotebook"; @@ -96,8 +98,8 @@ export const CopyNotebookPaneComponent: FunctionComponent return options; }; const dropDownProps: IDropdownProps = { - label: "Location", - ariaLabel: "Location", + label: t(Keys.panes.copyNotebook.location), + ariaLabel: t(Keys.panes.copyNotebook.locationAriaLabel), placeholder: "Select an option", onRenderTitle: onRenderDropDownTitle, onRenderOption: onRenderDropDownOption, @@ -109,7 +111,7 @@ export const CopyNotebookPaneComponent: FunctionComponent
- + {name} diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx index 05eff191f..6ce8ba413 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx @@ -4,6 +4,8 @@ import DeleteFeedback from "Common/DeleteFeedback"; import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils"; import { deleteCollection } from "Common/dataAccess/deleteCollection"; import { Collection } from "Contracts/ViewModels"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility"; import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor"; @@ -34,12 +36,15 @@ export const DeleteCollectionConfirmationPane: FunctionComponent => { const collection = useSelectedNode.getState().findSelectedCollection(); if (!collection || inputCollectionName !== collection.id()) { - const errorMessage = "Input id " + inputCollectionName + " does not match the selected " + collection.id(); + const errorMessage = t(Keys.panes.deleteCollection.inputMismatch, { + input: inputCollectionName, + selectedId: collection.id(), + }); setFormError(errorMessage); NotificationConsoleUtils.logConsoleError( `Error while deleting ${collectionName} ${collection.id()}: ${errorMessage}`, @@ -106,18 +111,23 @@ export const DeleteCollectionConfirmationPane: FunctionComponent
* - Confirm by typing the {collectionName.toLowerCase()} id + {confirmContainer} - Help us improve Azure Cosmos DB! + {t(Keys.panes.deleteCollection.feedbackTitle)} - What is the reason why you are deleting this {collectionName}? + {t(Keys.panes.deleteCollection.feedbackReason, { collectionName })} - Confirm by typing the - container - id + Confirm by typing the container id => { if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) { setFormError( - `Input ${getDatabaseName()} name "${databaseInput}" does not match the selected ${getDatabaseName()} "${selectedDatabase.id()}"`, + t(Keys.panes.deleteDatabase.inputMismatch, { + databaseName: getDatabaseName(), + input: databaseInput, + selectedId: selectedDatabase.id(), + }), ); logConsoleError(`Error while deleting ${getDatabaseName()} ${selectedDatabase && selectedDatabase.id()}`); logConsoleError( - `Input ${getDatabaseName()} name "${databaseInput}" does not match the selected ${getDatabaseName()} "${selectedDatabase.id()}"`, + t(Keys.panes.deleteDatabase.inputMismatch, { + databaseName: getDatabaseName(), + input: databaseInput, + selectedId: selectedDatabase.id(), + }), ); return; } @@ -114,18 +124,20 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent submit(), }; const errorProps: PanelInfoErrorProps = { messageType: "warning", showErrorDetails: false, - message: - "Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.", + message: t(Keys.panes.deleteDatabase.warningMessage), }; - const confirmDatabase = `Confirm by typing the ${getDatabaseName()} id (name)`; - const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${getDatabaseName()}?`; + const confirmDatabase = t(Keys.panes.deleteDatabase.confirmPrompt, { databaseName: getDatabaseName() }); + const reasonInfo = + t(Keys.panes.deleteDatabase.feedbackTitle) + + " " + + t(Keys.panes.deleteDatabase.feedbackReason, { databaseName: getDatabaseName() }); return ( {!formError && } @@ -148,10 +160,10 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent - Help us improve Azure Cosmos DB! + {t(Keys.panes.deleteDatabase.feedbackTitle)} - What is the reason why you are deleting this {getDatabaseName()}? + {t(Keys.panes.deleteDatabase.feedbackReason, { databaseName: getDatabaseName() })} { - setFormError(`Invalid param specified: ${invalidParam}`); - logConsoleError(`Invalid param specified: ${invalidParam} is not a valid literal value`); + setFormError(t(Keys.panes.executeStoredProcedure.invalidParamError, { invalidParam })); + logConsoleError(t(Keys.panes.executeStoredProcedure.invalidParamConsoleError, { invalidParam })); }; const submit = (): void => { @@ -96,7 +98,7 @@ export const ExecuteSprocParamsPane: FunctionComponent submit(), }; @@ -107,9 +109,9 @@ export const ExecuteSprocParamsPane: FunctionComponent deleteParamAtIndex(i)} onAddNewParamKeyPress={() => addNewParamAtIndex(i + 1)} @@ -130,9 +132,9 @@ export const ExecuteSprocParamsPane: FunctionComponent
(partitionValueRef.current = newInput)} onParamKeyChange={(_event: React.FormEvent, item: IDropdownOption) => @@ -143,8 +145,8 @@ export const ExecuteSprocParamsPane: FunctionComponent {getInputParameterComponent()} addNewParamAtLastIndex()} tabIndex={0}> - Add param - Add New Param + {t(Keys.panes.executeStoredProcedure.addParam)} + {t(Keys.panes.executeStoredProcedure.addNewParam)}
diff --git a/src/Explorer/Panes/ExecuteSprocParamsPane/InputParameter.tsx b/src/Explorer/Panes/ExecuteSprocParamsPane/InputParameter.tsx index 6fcea9328..78e2edf8b 100644 --- a/src/Explorer/Panes/ExecuteSprocParamsPane/InputParameter.tsx +++ b/src/Explorer/Panes/ExecuteSprocParamsPane/InputParameter.tsx @@ -11,6 +11,8 @@ import { import React, { FunctionComponent } from "react"; import AddPropertyIcon from "../../../../images/Add-property.svg"; import EntityCancelIcon from "../../../../images/Entity_cancel.svg"; +import { Keys } from "../../../Localization/Keys.generated"; +import { t } from "../../../Localization/t"; const dropdownStyles: Partial = { dropdown: { width: 100 } }; const options = [ @@ -74,7 +76,7 @@ export const InputParameter: FunctionComponent = ({ Delete param = ({ Add param = ({ }: GraphStylingProps): JSX.Element => { const closeSidePanel = useSidePanel((state) => state.closeSidePanel); - const buttonLabel = "Ok"; + const buttonLabel = t(Keys.common.ok); const submit = (event: React.FormEvent) => { event.preventDefault(); diff --git a/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx b/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx index 52214c308..ef9942004 100644 --- a/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx +++ b/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx @@ -5,6 +5,8 @@ import folderIcon from "../../../../images/folder_16x16.svg"; import { logError } from "../../../Common/Logger"; import { Collection } from "../../../Contracts/ViewModels"; import { useSidePanel } from "../../../hooks/useSidePanel"; +import { Keys } from "../../../Localization/Keys.generated"; +import { t } from "../../../Localization/t"; import { userContext } from "../../../UserContext"; import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils"; import { useSelectedNode } from "../../useSelectedNode"; @@ -33,8 +35,8 @@ export const LoadQueryPane: FunctionComponent = (): JSX.Element => { const submit = async (): Promise => { setFormError(""); if (!selectedFiles || selectedFiles.length === 0) { - setFormError("No file specified"); - logConsoleError("Could not load query -- No file specified. Please input a file."); + setFormError(t(Keys.panes.loadQuery.noFileSpecifiedError)); + logConsoleError(t(Keys.panes.loadQuery.noFileSpecifiedError)); return; } @@ -48,7 +50,7 @@ export const LoadQueryPane: FunctionComponent = (): JSX.Element => { setLoadingFalse(); } catch (error) { setLoadingFalse(); - setFormError("Failed to load query"); + setFormError(t(Keys.panes.loadQuery.failedToLoadQueryError)); logConsoleError(`Failed to load query from file ${file.name}: ${error}`); } }; @@ -71,7 +73,7 @@ export const LoadQueryPane: FunctionComponent = (): JSX.Element => { }; reader.onerror = (): void => { - setFormError("Failed to load query"); + setFormError(t(Keys.panes.loadQuery.failedToLoadQueryFromFileError, { fileName: file.name })); logConsoleError(`Failed to load query from file ${file.name}`); }; return reader.readAsText(file); @@ -79,7 +81,7 @@ export const LoadQueryPane: FunctionComponent = (): JSX.Element => { const props: RightPaneFormProps = { formError: formError, isExecuting: isLoading, - submitButtonText: "Load", + submitButtonText: t(Keys.common.load), onSubmit: () => submit(), }; @@ -90,7 +92,7 @@ export const LoadQueryPane: FunctionComponent = (): JSX.Element => { = ({ const props: RightPaneFormProps = { formError: errorMessage, isExecuting: isLoading, - submitButtonText: "OK", + submitButtonText: t(Keys.common.ok), onSubmit: () => submit(), }; diff --git a/src/Explorer/Panes/PanelInfoErrorComponent.tsx b/src/Explorer/Panes/PanelInfoErrorComponent.tsx index 92934c28b..76532db38 100644 --- a/src/Explorer/Panes/PanelInfoErrorComponent.tsx +++ b/src/Explorer/Panes/PanelInfoErrorComponent.tsx @@ -1,4 +1,6 @@ import { Icon, Link, Stack, Text } from "@fluentui/react"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import React from "react"; import { useNotificationConsole } from "../../hooks/useNotificationConsole"; @@ -20,13 +22,17 @@ export const PanelInfoErrorComponent: React.FunctionComponent { const expandConsole = useNotificationConsole((state) => state.expandConsole); - let icon: JSX.Element = ; + let icon: JSX.Element = ( + + ); if (messageType === "error") { icon = ; } else if (messageType === "warning") { icon = ; } else if (messageType === "info") { - icon = ; + icon = ( + + ); } return ( @@ -43,7 +49,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent {showErrorDetails && ( - More details + {t(Keys.panes.panelInfo.moreDetails)} )} diff --git a/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx b/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx index 7de0bc8f7..e3bf676c5 100644 --- a/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx +++ b/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx @@ -5,6 +5,8 @@ import { getErrorMessage, getErrorStack, handleError } from "../../../Common/Err import { useNotebookSnapshotStore } from "../../../hooks/useNotebookSnapshotStore"; import { useSidePanel } from "../../../hooks/useSidePanel"; import { JunoClient } from "../../../Juno/JunoClient"; +import { Keys } from "../../../Localization/Keys.generated"; +import { t } from "../../../Localization/t"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor"; import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils"; @@ -91,7 +93,7 @@ export const PublishNotebookPane: FunctionComponent = let startKey: number; if (!notebookName || !notebookDescription || !author || !imageSrc) { - setFormError(`Failed to publish ${notebookName} to gallery`); + setFormError(t(Keys.panes.publishNotebook.publishFailedError, { notebookName })); setFormErrorDetail("Name, description, author and cover image are required"); createFormError(formError, formErrorDetail, "PublishNotebookPaneAdapter/submit"); setIsExecuting(false); @@ -143,7 +145,11 @@ export const PublishNotebookPane: FunctionComponent = ); const errorMessage = getErrorMessage(error); - setFormError(`Failed to publish ${FileSystemUtil.stripExtension(notebookName, "ipynb")} to gallery`); + setFormError( + t(Keys.panes.publishNotebook.publishFailedError, { + notebookName: FileSystemUtil.stripExtension(notebookName, "ipynb"), + }), + ); setFormErrorDetail(`${errorMessage}`); handleError(errorMessage, "PublishNotebookPaneAdapter/submit", formError); return; diff --git a/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPaneComponent.tsx b/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPaneComponent.tsx index c24ead062..e73516a67 100644 --- a/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPaneComponent.tsx +++ b/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPaneComponent.tsx @@ -1,6 +1,8 @@ import { Dropdown, IDropdownProps, ITextFieldProps, Stack, Text, TextField } from "@fluentui/react"; import { ImmutableNotebook } from "@nteract/commutable"; import React, { FunctionComponent, useState } from "react"; +import { Keys } from "../../../Localization/Keys.generated"; +import { t } from "../../../Localization/t"; import { GalleryCardComponent } from "../../Controls/NotebookGallery/Cards/GalleryCardComponent"; import * as FileSystemUtil from "../../Notebook/FileSystemUtil"; import { SnapshotRequest } from "../../Notebook/NotebookComponent/types"; @@ -57,13 +59,11 @@ export const PublishNotebookPaneComponent: FunctionComponent ({ text: value, key: value })), onChange: async (event, options) => { setImageSrc(""); @@ -99,7 +99,7 @@ export const PublishNotebookPaneComponent: FunctionComponent { setImageSrc(newValue); @@ -116,7 +116,7 @@ export const PublishNotebookPaneComponent: FunctionComponent { - const formError = "Failed to capture first output"; + const formError = t(Keys.panes.publishNotebook.failedToCaptureOutput); const formErrorDetail = `${error}`; const area = "PublishNotebookPaneComponent/UseFirstOutput"; onError(formError, formErrorDetail, area); @@ -130,7 +130,7 @@ export const PublishNotebookPaneComponent: FunctionComponent { - const formError = `Failed to convert ${file.name} to base64 format`; + const formError = t(Keys.panes.publishNotebook.failedToConvertError, { fileName: file.name }); const formErrorDetail = `${error}`; const area = "PublishNotebookPaneComponent/selectImageFile"; onError(formError, formErrorDetail, area); @@ -151,7 +151,7 @@ export const PublishNotebookPaneComponent: FunctionComponent maxImageSizeInMib) { event.target.value = ""; - const formError = `Failed to upload ${file.name}`; + const formError = t(Keys.panes.publishNotebook.failedToUploadError, { fileName: file.name }); const formErrorDetail = `Image is larger than ${maxImageSizeInMib} MiB. Please Choose a different image.`; const area = "PublishNotebookPaneComponent/selectImageFile"; @@ -185,8 +185,8 @@ export const PublishNotebookPaneComponent: FunctionComponent { @@ -198,8 +198,8 @@ export const PublishNotebookPaneComponent: FunctionComponent { setNotebookTags(newValue); }} @@ -227,7 +227,7 @@ export const PublishNotebookPaneComponent: FunctionComponent{renderThumbnailSelectors(type)} - Preview + {t(Keys.panes.publishNotebook.preview)} = ({ const [formError, setFormError] = useState(""); const [queryName, setQueryName] = useState(""); - const setupSaveQueriesText = `For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “${SavedQueries.DatabaseName}”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.`; - const title = "Save Query"; + const setupSaveQueriesText = t(Keys.panes.saveQuery.setupCostMessage, { databaseName: SavedQueries.DatabaseName }); + const title = t(Keys.panes.saveQuery.panelTitle); const isSaveQueryEnabled = useDatabases((state) => state.isSaveQueryEnabled); const submit = async (): Promise => { setFormError(""); if (!isSaveQueryEnabled()) { setFormError("Cannot save query"); - logConsoleError("Failed to save query: account not setup to save queries"); + logConsoleError(t(Keys.panes.saveQuery.accountNotSetupError)); } const queryTab = useTabs.getState().activeTab as NewQueryTab; const query: string = queryToSave || queryTab?.iTabAccessor.onSaveClickEvent(); if (!queryName || queryName.length === 0) { - setFormError("No query name specified"); - logConsoleError("Could not save query -- No query name specified. Please specify a query name."); + setFormError(t(Keys.panes.saveQuery.noQueryNameError)); + logConsoleError(t(Keys.panes.saveQuery.noQueryNameError)); return; } else if (!query || query.length === 0) { - setFormError("Invalid query content specified"); - logConsoleError("Could not save query -- Invalid query content specified. Please enter query content."); + setFormError(t(Keys.panes.saveQuery.invalidQueryContentError)); + logConsoleError(t(Keys.panes.saveQuery.invalidQueryContentError)); return; } @@ -80,8 +82,8 @@ export const SaveQueryPane: FunctionComponent = ({ } catch (error) { setLoadingFalse(); const errorMessage = getErrorMessage(error); - setFormError("Failed to save query"); - logConsoleError(`Failed to save query: ${errorMessage}`); + setFormError(t(Keys.panes.saveQuery.failedToSaveQueryError, { queryName })); + logConsoleError(t(Keys.panes.saveQuery.failedToSaveQueryError, { queryName }) + ": " + errorMessage); traceFailure( Action.SaveQuery, { @@ -126,8 +128,8 @@ export const SaveQueryPane: FunctionComponent = ({ }, startKey, ); - setFormError("Failed to setup a container for saved queries"); - logConsoleError(`Failed to setup a container for saved queries: ${errorMessage}`); + setFormError(t(Keys.panes.saveQuery.failedToSetupContainerError)); + logConsoleError(t(Keys.panes.saveQuery.failedToSetupContainerError) + ": " + errorMessage); } finally { setLoadingFalse(); } @@ -136,7 +138,7 @@ export const SaveQueryPane: FunctionComponent = ({ const props: RightPaneFormProps = { formError: formError, isExecuting: isLoading, - submitButtonText: isSaveQueryEnabled() ? "Save" : "Complete setup", + submitButtonText: isSaveQueryEnabled() ? t(Keys.common.save) : t(Keys.panes.saveQuery.completeSetup), onSubmit: () => { isSaveQueryEnabled() ? submit() : setupQueries(); }, @@ -160,7 +162,7 @@ export const SaveQueryPane: FunctionComponent = ({ ) : ( { diff --git a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx index 69cca5fc5..00fc142cd 100644 --- a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx +++ b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx @@ -24,6 +24,8 @@ import { InfoTooltip } from "Common/Tooltip/InfoTooltip"; import { Platform, configContext } from "ConfigContext"; import { useDialog } from "Explorer/Controls/Dialog"; import { useDatabases } from "Explorer/useDatabases"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil"; import { AppStateComponentNames, @@ -235,7 +237,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ const regionOptions: IDropdownOption[] = []; regionOptions.push({ key: userContext?.databaseAccount?.properties?.documentEndpoint, - text: `Global (Default)`, + text: t(Keys.panes.settings.globalDefault), data: { isGlobal: true, writeEnabled: true, @@ -246,7 +248,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ uniqueAccountRegions.add(loc.locationName); regionOptions.push({ key: loc.documentEndpoint, - text: `${loc.locationName} (Read/Write)`, + text: `${loc.locationName} ${t(Keys.panes.settings.readWrite)}`, data: { isGlobal: false, writeEnabled: true, @@ -259,7 +261,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ uniqueAccountRegions.add(loc.locationName); regionOptions.push({ key: loc.documentEndpoint, - text: `${loc.locationName} (Read)`, + text: `${loc.locationName} ${t(Keys.panes.settings.read)}`, data: { isGlobal: false, writeEnabled: false, @@ -317,13 +319,9 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ authError instanceof msalAuthError && authError.errorCode === msalBrowserAuthErrorMessage.popUpWindowError.code ) { - logConsoleError( - `We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease enable pop-ups for this site and click on "Login for Entra ID" button`, - ); + logConsoleError(t(Keys.panes.settings.popupsDisabledError)); } else { - logConsoleError( - `"Failed to acquire authorization token automatically. Please click on "Login for Entra ID" button to enable Entra ID RBAC operations`, - ); + logConsoleError(t(Keys.panes.settings.failedToAcquireTokenError)); } } } else { @@ -485,33 +483,33 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ const genericPaneProps: RightPaneFormProps = { formError: "", isExecuting, - submitButtonText: "Apply", + submitButtonText: t(Keys.common.apply), onSubmit: () => handlerOnSubmit(), }; const pageOptionList: IChoiceGroupOption[] = [ - { key: Constants.Queries.CustomPageOption, text: "Custom" }, - { key: Constants.Queries.UnlimitedPageOption, text: "Unlimited" }, + { key: Constants.Queries.CustomPageOption, text: t(Keys.panes.settings.custom) }, + { key: Constants.Queries.UnlimitedPageOption, text: t(Keys.panes.settings.unlimited) }, ]; const graphAutoOptionList: IChoiceGroupOption[] = [ - { key: "false", text: "Graph" }, - { key: "true", text: "JSON" }, + { key: "false", text: t(Keys.panes.settings.graph) }, + { key: "true", text: t(Keys.panes.settings.json) }, ]; const priorityLevelOptionList: IChoiceGroupOption[] = [ - { key: Constants.PriorityLevel.Low, text: "Low" }, - { key: Constants.PriorityLevel.High, text: "High" }, + { key: Constants.PriorityLevel.Low, text: t(Keys.panes.settings.low) }, + { key: Constants.PriorityLevel.High, text: t(Keys.panes.settings.high) }, ]; const dataPlaneRBACOptionsList: IChoiceGroupOption[] = [ - { key: Constants.RBACOptions.setAutomaticRBACOption, text: "Automatic" }, - { key: Constants.RBACOptions.setTrueRBACOption, text: "True" }, - { key: Constants.RBACOptions.setFalseRBACOption, text: "False" }, + { key: Constants.RBACOptions.setAutomaticRBACOption, text: t(Keys.panes.settings.automatic) }, + { key: Constants.RBACOptions.setTrueRBACOption, text: t(Keys.panes.settings["true"]) }, + { key: Constants.RBACOptions.setFalseRBACOption, text: t(Keys.panes.settings["false"]) }, ]; const defaultQueryResultsViewOptionList: IChoiceGroupOption[] = [ - { key: SplitterDirection.Vertical, text: "Vertical" }, - { key: SplitterDirection.Horizontal, text: "Horizontal" }, + { key: SplitterDirection.Vertical, text: t(Keys.tabs.query.vertical) }, + { key: SplitterDirection.Horizontal, text: t(Keys.tabs.query.horizontal) }, ]; const mongoGuidRepresentationDropdownOptions: IDropdownOption[] = [ @@ -724,13 +722,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowQueryPageOptions && ( -
Page Options
+
{t(Keys.panes.settings.pageOptions)}
- Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as - many query results per page. + {t(Keys.panes.settings.pageOptionsDescription)}
= ({ {isCustomPageOptionSelected() && (
- Query results per page{" "} + {t(Keys.panes.settings.queryResultsPerPage)}{" "} - Enter the number of query results that should be shown per page. + {t(Keys.panes.settings.queryResultsPerPageTooltip)}
{ setCustomItemPerPage(parseInt(newValue) + 1 || customItemPerPage); @@ -761,8 +758,8 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ min={1} step={1} className="textfontclr" - incrementButtonAriaLabel="Increase value by 1" - decrementButtonAriaLabel="Decrease value by 1" + incrementButtonAriaLabel={t(Keys.common.increaseValueBy1)} + decrementButtonAriaLabel={t(Keys.common.decreaseValueBy1)} styles={spinButtonStyles} />
@@ -774,20 +771,19 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {showEnableEntraIdRbac && ( -
Enable Entra ID RBAC
+
{t(Keys.panes.settings.entraIdRbac)}
- Choose Automatic to enable Entra ID RBAC automatically. True/False to force enable/disable Entra - ID RBAC. + {t(Keys.panes.settings.entraIdRbacDescription)} {" "} - Learn more{" "} + {t(Keys.common.learnMore)}{" "}
= ({ {userContext.apiType === "SQL" && userContext.authType === AuthType.AAD && !isFabric() && ( -
Region Selection
+
{t(Keys.panes.settings.regionSelection)}
- Changes region the Cosmos Client uses to access account. + {t(Keys.panes.settings.regionSelectionDescription)}
- Select Region + {t(Keys.panes.settings.selectRegion)} - Changes the account endpoint used to perform client operations. + {t(Keys.panes.settings.selectRegionTooltip)}
= ({ <> -
Query Timeout
+
{t(Keys.panes.settings.queryTimeout)}
- When a query reaches a specified time limit, a popup with an option to cancel the query will - show unless automatic cancellation has been enabled. + {t(Keys.panes.settings.queryTimeoutDescription)}
@@ -883,18 +878,18 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {queryTimeoutEnabled && (
= ({ -
RU Limit
+
{t(Keys.panes.settings.ruLimit)}
- If a query exceeds a configured RU limit, the query will be aborted. + {t(Keys.panes.settings.ruLimitDescription)}
@@ -922,14 +917,14 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {ruThresholdEnabled && (
@@ -939,12 +934,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ -
Default Query Results View
+
{t(Keys.panes.settings.defaultQueryResults)}
- Select the default view to use when displaying query results. + {t(Keys.panes.settings.defaultQueryResultsDescription)}
= ({ {showRetrySettings && ( -
Retry Settings
+
{t(Keys.panes.settings.retrySettings)}
- Retry policy associated with throttled requests during CosmosDB queries. + {t(Keys.panes.settings.retrySettingsDescription)}
- Max retry attempts + {t(Keys.panes.settings.maxRetryAttempts)} - Max number of retries to be performed for a request. Default value 9. + {t(Keys.panes.settings.maxRetryAttemptsTooltip)}
= ({ step={1} value={"" + retryAttempts} onChange={handleOnQueryRetryAttemptsSpinButtonChange} - incrementButtonAriaLabel="Increase value by 1" - decrementButtonAriaLabel="Decrease value by 1" + incrementButtonAriaLabel={t(Keys.common.increaseValueBy1)} + decrementButtonAriaLabel={t(Keys.common.decreaseValueBy1)} onIncrement={(newValue) => setRetryAttempts(parseInt(newValue) + 1 || retryAttempts)} onDecrement={(newValue) => setRetryAttempts(parseInt(newValue) - 1 || retryAttempts)} onValidate={(newValue) => setRetryAttempts(parseInt(newValue) || retryAttempts)} styles={spinButtonStyles} />
- Fixed retry interval (ms) + {t(Keys.panes.settings.fixedRetryInterval)} - Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned - as part of the response. Default value is 0 milliseconds. + {t(Keys.panes.settings.fixedRetryIntervalTooltip)}
= ({ step={1000} value={"" + retryInterval} onChange={handleOnRetryIntervalSpinButtonChange} - incrementButtonAriaLabel="Increase value by 1000" - decrementButtonAriaLabel="Decrease value by 1000" + incrementButtonAriaLabel={t(Keys.panes.settings.increaseValueBy1000)} + decrementButtonAriaLabel={t(Keys.panes.settings.decreaseValueBy1000)} onIncrement={(newValue) => setRetryInterval(parseInt(newValue) + 1000 || retryInterval)} onDecrement={(newValue) => setRetryInterval(parseInt(newValue) - 1000 || retryInterval)} onValidate={(newValue) => setRetryInterval(parseInt(newValue) || retryInterval)} styles={spinButtonStyles} />
- Max wait time (s) + {t(Keys.panes.settings.maxWaitTime)} - Max wait time in seconds to wait for a request while the retries are happening. Default value 30 - seconds. + {t(Keys.panes.settings.maxWaitTimeTooltip)}
= ({ step={1} value={"" + MaxWaitTimeInSeconds} onChange={handleOnMaxWaitTimeSpinButtonChange} - incrementButtonAriaLabel="Increase value by 1" - decrementButtonAriaLabel="Decrease value by 1" + incrementButtonAriaLabel={t(Keys.common.increaseValueBy1)} + decrementButtonAriaLabel={t(Keys.common.decreaseValueBy1)} onIncrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) + 1 || MaxWaitTimeInSeconds) } @@ -1039,24 +1032,26 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {!isEmulator && ( -
Enable container pagination
+
{t(Keys.panes.settings.enableContainerPagination)}
- Load 50 containers at a time. Currently, containers are not pulled in alphanumeric order. + {t(Keys.panes.settings.enableContainerPaginationDescription)}
setContainerPaginationEnabled(!containerPaginationEnabled)} - label="Enable container pagination" + label={t(Keys.panes.settings.enableContainerPagination)} onRenderLabel={() => ( - Enable container pagination + + {t(Keys.panes.settings.enableContainerPagination)} + )} />
@@ -1066,24 +1061,25 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowCrossPartitionOption && ( -
Enable cross-partition query
+
{t(Keys.panes.settings.enableCrossPartitionQuery)}
- Send more than one request while executing a query. More than one request is necessary if the - query is not scoped to single partition key value. + {t(Keys.panes.settings.enableCrossPartitionQueryDescription)}
setCrossPartitionQueryEnabled(!crossPartitionQueryEnabled)} onRenderLabel={() => ( - Enable cross-partition query + + {t(Keys.panes.settings.enableCrossPartitionQuery)} + )} />
@@ -1093,19 +1089,19 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowEnhancedQueryControl && ( -
Enhanced query control
+
{t(Keys.panes.settings.enhancedQueryControl)}
- Query up to the max degree of parallelism. + {t(Keys.panes.settings.maxDegreeOfParallelismQuery)} {" "} - Learn more{" "} + {t(Keys.common.learnMore)}{" "}
= ({ label: { padding: 0 }, }} className="padding" - ariaLabel="EnableQueryControl" + ariaLabel={t(Keys.panes.settings.enableQueryControl)} checked={queryControlEnabled} onChange={() => setQueryControlEnabled(!queryControlEnabled)} onRenderLabel={() => ( - Enable query control + + {t(Keys.panes.settings.enableQueryControl)} + )} />
@@ -1127,14 +1125,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowParallelismOption && ( -
Max degree of parallelism
+
{t(Keys.panes.settings.maxDegreeOfParallelism)}
- Gets or sets the number of concurrent operations run client side during parallel query execution. - A positive property value limits the number of concurrent operations to the set value. If it is - set to less than 0, the system automatically decides the number of concurrent operations to run. + {t(Keys.panes.settings.maxDegreeOfParallelismDescription)}
= ({ setMaxDegreeOfParallelism(parseInt(newValue) - 1 || maxDegreeOfParallelism) } onValidate={(newValue) => setMaxDegreeOfParallelism(parseInt(newValue) || maxDegreeOfParallelism)} - ariaLabel="Max degree of parallelism" - label="Max degree of parallelism" + ariaLabel={t(Keys.panes.settings.maxDegreeOfParallelism)} + label={t(Keys.panes.settings.maxDegreeOfParallelism)} styles={spinButtonStyles} />
@@ -1161,14 +1157,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowPriorityLevelOption && ( -
Priority Level
+
{t(Keys.panes.settings.priorityLevel)}
- Sets the priority level for data-plane requests from Data Explorer when using Priority-Based - Execution. If "None" is selected, Data Explorer will not specify priority level, and the - server-side default priority level will be used. + {t(Keys.panes.settings.priorityLevelDescription)}
= ({ {shouldShowGraphAutoVizOption && ( -
Display Gremlin query results as: 
+
{t(Keys.panes.settings.displayGremlinQueryResults)} 
- Select Graph to automatically visualize the query results as a Graph or JSON to display the - results as JSON. + {t(Keys.panes.settings.displayGremlinQueryResultsDescription)}
@@ -1205,25 +1198,25 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowCopilotSampleDBOption && ( -
Enable sample database
+
{t(Keys.panes.settings.enableSampleDatabase)}
- This is a sample database and collection with synthetic product data you can use to explore using - NoSQL queries. This will appear as another database in the Data Explorer UI, and is created by, - and maintained by Microsoft at no cost to you. + {t(Keys.panes.settings.enableSampleDatabaseDescription)}
( - Enable sample database + + {t(Keys.panes.settings.enableSampleDatabase)} + )} />
@@ -1233,13 +1226,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ {shouldShowMongoGuidRepresentationOption && ( -
Guid Representation
+
{t(Keys.panes.settings.guidRepresentation)}
- GuidRepresentation in MongoDB refers to how Globally Unique Identifiers (GUIDs) are serialized and - deserialized when stored in BSON documents. This will apply to all document operations. + {t(Keys.panes.settings.guidRepresentationDescription)}
= ({ )} -
Advanced Settings
+
{t(Keys.panes.settings.advancedSettings)}
@@ -1283,14 +1275,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ }, }} className="padding" - ariaLabel="Ignore partition key on document update" + ariaLabel={t(Keys.panes.settings.ignorePartitionKey)} checked={ignorePartitionKeyOnDocumentUpdate} onChange={handleOnIgnorePartitionKeyOnDocumentUpdateChange} - label="Ignore partition key on document update" + label={t(Keys.panes.settings.ignorePartitionKey)} /> - If checked, the partition key value will not be used to locate the document during update - operations. Only use this if document updates are failing due to an abnormal partition key. + {t(Keys.panes.settings.ignorePartitionKeyTooltip)}
@@ -1320,9 +1311,9 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ }} onClick={() => { useDialog.getState().showOkCancelModalDialog( - "Clear History", + t(Keys.panes.settings.clearHistory), undefined, - "Are you sure you want to proceed?", + t(Keys.panes.settings.clearHistoryConfirm), () => { deleteAllStates(); updateUserContext({ @@ -1332,35 +1323,33 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ }); useClientWriteEnabled.setState({ clientWriteEnabled: true }); }, - "Cancel", + t(Keys.common.cancel), undefined, <> - - This action will clear the all customizations for this account in this browser, including: - + {t(Keys.panes.settings.clearHistoryDescription)}
    -
  • Reset your customized tab layout, including the splitter positions
  • -
  • Erase your table column preferences, including any custom columns
  • -
  • Clear your filter history
  • -
  • Reset region selection to global
  • +
  • {t(Keys.panes.settings.clearHistoryTabLayout)}
  • +
  • {t(Keys.panes.settings.clearHistoryTableColumns)}
  • +
  • {t(Keys.panes.settings.clearHistoryFilters)}
  • +
  • {t(Keys.panes.settings.clearHistoryRegion)}
, ); }} > - Clear History + {t(Keys.panes.settings.clearHistory)}
-
Explorer Version
+
{t(Keys.panes.settings.explorerVersion)}
{explorerVersion}
-
Session ID
+
{t(Keys.panes.settings.sessionId)}
{sessionId}
diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap index af4dbbd11..31cca5d75 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap @@ -660,7 +660,7 @@ exports[`Settings Pane should render Default properly 1`] = ` Send more than one request while executing a query. More than one request is necessary if the query is not scoped to single partition key value.
- Display Gremlin query results as:  + Display Gremlin query results as: +  
diff --git a/src/Explorer/Panes/TableColumnSelectionPane/TableColumnSelectionPane.tsx b/src/Explorer/Panes/TableColumnSelectionPane/TableColumnSelectionPane.tsx index a3cb08a4a..dadd00636 100644 --- a/src/Explorer/Panes/TableColumnSelectionPane/TableColumnSelectionPane.tsx +++ b/src/Explorer/Panes/TableColumnSelectionPane/TableColumnSelectionPane.tsx @@ -11,6 +11,8 @@ import { import { configContext } from "ConfigContext"; import { ColumnDefinition } from "Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent"; import { CosmosFluentProvider, getPlatformTheme } from "Explorer/Theme/ThemeUtil"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import React from "react"; import { useSidePanel } from "../../../hooks/useSidePanel"; @@ -113,13 +115,13 @@ export const TableColumnSelectionPane: React.FC =
- Select which columns to display in your view of items in your container. + {t(Keys.panes.tableColumnSelection.selectColumns)}
to avoid margin-bottom set by panelMainContent css */>
@@ -130,7 +132,9 @@ export const TableColumnSelectionPane: React.FC = key={columnDefinition.id} label={{ className: styles.checkboxLabel, - children: `${columnDefinition.label}${columnDefinition.isPartitionKey ? " (partition key)" : ""}`, + children: `${columnDefinition.label}${ + columnDefinition.isPartitionKey ? t(Keys.panes.tableColumnSelection.partitionKeySuffix) : "" + }`, }} checked={selectedColumnIdsSet.has(columnDefinition.id)} onChange={(_, data) => onCheckedValueChange(columnDefinition.id, data)} @@ -138,15 +142,15 @@ export const TableColumnSelectionPane: React.FC = ))}
diff --git a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx index 7d73ccc1f..bc375e578 100644 --- a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx @@ -1,5 +1,7 @@ import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react"; import { useBoolean } from "@fluentui/react-hooks"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { logConsoleError } from "Utils/NotificationConsoleUtils"; import React, { FunctionComponent, useEffect, useState } from "react"; import * as _ from "underscore"; @@ -100,8 +102,8 @@ export const AddTableEntityPanel: FunctionComponent = for (let i = 0; i < entities.length; i++) { const { property, type, value } = entities[i]; if ((property === "PartitionKey" && value === "") || (property === "RowKey" && value === "")) { - logConsoleError(`${property} cannot be empty. Please input a value for ${property}`); - setFormError(`${property} cannot be empty. Please input a value for ${property}`); + logConsoleError(t(Keys.panes.tables.propertyEmptyError, { property })); + setFormError(t(Keys.panes.tables.propertyEmptyError, { property })); return; } @@ -109,13 +111,13 @@ export const AddTableEntityPanel: FunctionComponent = (property === "PartitionKey" && containsAnyWhiteSpace(value) === true) || (property === "RowKey" && containsAnyWhiteSpace(value) === true) ) { - logConsoleError(`${property} cannot have whitespace. Please input a value for ${property} without whitespace`); - setFormError(`${property} cannot have whitespace. Please input a value for ${property} without whitespace`); + logConsoleError(t(Keys.panes.tables.whitespaceError, { property })); + setFormError(t(Keys.panes.tables.whitespaceError, { property })); return; } if (!type) { - setFormError(`Property type cannot be empty. Please select a type from the dropdown for property ${property}`); + setFormError(t(Keys.panes.tables.propertyTypeEmptyError, { property })); return; } diff --git a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx index e59fceee9..19de83b3a 100644 --- a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx @@ -1,5 +1,7 @@ import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react"; import { useBoolean } from "@fluentui/react-hooks"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { logConsoleError } from "Utils/NotificationConsoleUtils"; import React, { FunctionComponent, useEffect, useState } from "react"; import * as _ from "underscore"; @@ -198,7 +200,7 @@ export const EditTableEntityPanel: FunctionComponent } if (!type) { - setFormError(`Property type cannot be empty. Please select a type from the dropdown for property ${property}`); + setFormError(t(Keys.panes.tables.propertyTypeEmptyError, { property })); return; } @@ -208,8 +210,8 @@ export const EditTableEntityPanel: FunctionComponent (property === "RowKey" && value === "") || (property === "RowKey" && value === undefined) ) { - logConsoleError(`${property} cannot be empty. Please input a value for ${property}`); - setFormError(`${property} cannot be empty. Please input a value for ${property}`); + logConsoleError(t(Keys.panes.tables.propertyEmptyError, { property })); + setFormError(t(Keys.panes.tables.propertyEmptyError, { property })); return; } } @@ -403,7 +405,7 @@ export const EditTableEntityPanel: FunctionComponent )}
- Warning: Null fields will not be displayed for editing. + {t(Keys.panes.tables.nullFieldsWarning)}
); diff --git a/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx b/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx index 14d90f5eb..c46774ad8 100644 --- a/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx +++ b/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx @@ -1,4 +1,6 @@ import { Checkbox, Text } from "@fluentui/react"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import React, { FunctionComponent, useEffect, useState } from "react"; import { userContext } from "../../../../UserContext"; import { useSidePanel } from "../../../../hooks/useSidePanel"; @@ -35,7 +37,7 @@ export const TableQuerySelectPanel: FunctionComponent
- Select the columns that you want to query. + {t(Keys.panes.tableQuerySelect.selectColumns)}
diff --git a/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx b/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx index 47d7fd846..60a1f5c97 100644 --- a/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx +++ b/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx @@ -13,6 +13,8 @@ import { } from "@fluentui/react"; import { Upload } from "Common/Upload/Upload"; import { UploadDetailsRecord } from "Contracts/ViewModels"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { logConsoleError } from "Utils/NotificationConsoleUtils"; import React, { ChangeEvent, FunctionComponent, useReducer, useState } from "react"; import { getErrorMessage } from "../../Tables/Utilities"; @@ -63,8 +65,8 @@ export const UploadItemsPane: FunctionComponent = ({ onUpl const onSubmit = () => { setFormError(""); if (!files || files.length === 0) { - setFormError("No files were specified. Please input at least one file."); - logConsoleError("Could not upload items -- No files were specified. Please input at least one file."); + setFormError(t(Keys.panes.uploadItems.noFilesSpecifiedError)); + logConsoleError(t(Keys.panes.uploadItems.noFilesSpecifiedError)); return; } @@ -150,7 +152,7 @@ export const UploadItemsPane: FunctionComponent = ({ onUpl }, { key: "fileName", - name: "FILE NAME", + name: t(Keys.panes.uploadItems.fileNameColumn), fieldName: "fileName", minWidth: 120, maxWidth: 140, @@ -169,7 +171,7 @@ export const UploadItemsPane: FunctionComponent = ({ onUpl }, { key: "status", - name: "STATUS", + name: t(Keys.panes.uploadItems.statusColumn), fieldName: "numSucceeded", minWidth: 120, maxWidth: 140, @@ -178,7 +180,11 @@ export const UploadItemsPane: FunctionComponent = ({ onUpl data: "string", isPadded: true, onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => { - const fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`; + const fieldContent = t(Keys.panes.uploadItems.uploadStatus, { + numSucceeded: item.numSucceeded, + numThrottled: item.numThrottled, + numFailed: item.numFailed, + }); return ( = ({ onUpl
{uploadFileData?.length > 0 && (
diff --git a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap index d391b5ae3..ed87acb0a 100644 --- a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap +++ b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap @@ -714,9 +714,7 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = ` - What is the reason why you are deleting this - Database - ? + What is the reason why you are deleting this Database?