From 77132be3b356ff00df2b9e592b41b7c3a02c3684 Mon Sep 17 00:00:00 2001 From: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com> Date: Thu, 5 Mar 2026 12:05:45 -0600 Subject: [PATCH] Localization second batch (#2410) * Localization second batch * update test * fix tests * Fix test --- src/Explorer/ContextMenuButtonFactory.tsx | 42 ++--- src/Explorer/Tabs/ConflictsTab.ts | 26 +-- .../Tabs/DocumentsTabV2/DocumentsTabV2.tsx | 149 ++++++++++-------- .../DocumentsTabV2.test.tsx.snap | 4 +- .../MongoShellTab/MongoShellTabComponent.tsx | 4 +- .../Tabs/QueryTab/QueryTabComponent.tsx | 38 +++-- .../StoredProcedureTabComponent.tsx | 38 ++--- src/Explorer/Tabs/TriggerTabContent.tsx | 36 +++-- .../Tabs/UserDefinedFunctionTabContent.tsx | 16 +- src/Localization/en/Resources.json | 121 ++++++++++++++ src/setupTests.ts | 5 + 11 files changed, 320 insertions(+), 159 deletions(-) diff --git a/src/Explorer/ContextMenuButtonFactory.tsx b/src/Explorer/ContextMenuButtonFactory.tsx index 76b75dda8..ad4013f7e 100644 --- a/src/Explorer/ContextMenuButtonFactory.tsx +++ b/src/Explorer/ContextMenuButtonFactory.tsx @@ -7,6 +7,8 @@ import { AddGlobalSecondaryIndexPanelProps, } from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel"; import { useDatabases } from "Explorer/useDatabases"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { isFabric, isFabricNative, openRestoreContainerDialog } from "Platform/Fabric/FabricUtil"; import { Action } from "Shared/Telemetry/TelemetryConstants"; import { traceOpen } from "Shared/Telemetry/TelemetryProcessor"; @@ -24,6 +26,7 @@ import DeleteTriggerIcon from "../../images/DeleteTrigger.svg"; import DeleteUDFIcon from "../../images/DeleteUDF.svg"; import HostedTerminalIcon from "../../images/Hosted-Terminal.svg"; import * as ViewModels from "../Contracts/ViewModels"; +import { extractFeatures } from "../Platform/Hosted/extractFeatures"; import { userContext } from "../UserContext"; import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils"; import { useSidePanel } from "../hooks/useSidePanel"; @@ -35,7 +38,6 @@ import StoredProcedure from "./Tree/StoredProcedure"; import Trigger from "./Tree/Trigger"; import UserDefinedFunction from "./Tree/UserDefinedFunction"; import { useSelectedNode } from "./useSelectedNode"; -import { extractFeatures } from "../Platform/Hosted/extractFeatures"; export interface CollectionContextMenuButtonParams { databaseId: string; @@ -57,7 +59,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin { iconSrc: AddCollectionIcon, onClick: () => container.onNewCollectionClicked({ databaseId }), - label: `New ${getCollectionName()}`, + label: t(Keys.contextMenu.newContainer, { containerName: getCollectionName() }), }, ]; @@ -67,7 +69,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin items.push({ iconSrc: AddCollectionIcon, onClick: () => openRestoreContainerDialog(), - label: `Restore ${getCollectionName()}`, + label: t(Keys.contextMenu.restoreContainer, { containerName: getCollectionName() }), }); } } @@ -80,11 +82,11 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin useSidePanel .getState() .openSidePanel( - "Delete " + getDatabaseName(), + t(Keys.contextMenu.deleteDatabase, { databaseName: getDatabaseName() }), container.refreshAllDatabases()} />, ); }, - label: `Delete ${getDatabaseName()}`, + label: t(Keys.contextMenu.deleteDatabase, { databaseName: getDatabaseName() }), styleClass: "deleteDatabaseMenuItem", }); } @@ -100,7 +102,7 @@ export const createCollectionContextMenuButton = ( items.push({ iconSrc: AddSqlQueryIcon, onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, undefined), - label: "New SQL Query", + label: t(Keys.contextMenu.newSqlQuery), }); } @@ -108,7 +110,7 @@ export const createCollectionContextMenuButton = ( items.push({ iconSrc: AddSqlQueryIcon, onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, undefined), - label: "New Query", + label: t(Keys.contextMenu.newQuery), }); items.push({ @@ -123,8 +125,8 @@ export const createCollectionContextMenuButton = ( }, label: useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell - ? "Open Mongo Shell" - : "New Shell", + ? t(Keys.contextMenu.openMongoShell) + : t(Keys.contextMenu.newShell), }); } @@ -137,7 +139,7 @@ export const createCollectionContextMenuButton = ( onClick: () => { container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra); }, - label: "Open Cassandra Shell", + label: t(Keys.contextMenu.openCassandraShell), }); } @@ -150,7 +152,7 @@ export const createCollectionContextMenuButton = ( onClick: () => { selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, undefined); }, - label: "New Stored Procedure", + label: t(Keys.contextMenu.newStoredProcedure), }); items.push({ @@ -158,7 +160,7 @@ export const createCollectionContextMenuButton = ( onClick: () => { selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection); }, - label: "New UDF", + label: t(Keys.contextMenu.newUdf), }); items.push({ @@ -166,7 +168,7 @@ export const createCollectionContextMenuButton = ( onClick: () => { selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, undefined); }, - label: "New Trigger", + label: t(Keys.contextMenu.newTrigger), }); } @@ -179,11 +181,11 @@ export const createCollectionContextMenuButton = ( useSidePanel .getState() .openSidePanel( - "Delete " + getCollectionName(), + t(Keys.contextMenu.deleteContainer, { containerName: getCollectionName() }), container.refreshAllDatabases()} />, ); }, - label: `Delete ${getCollectionName()}`, + label: t(Keys.contextMenu.deleteContainer, { containerName: getCollectionName() }), styleClass: "deleteCollectionMenuItem", }); } @@ -220,14 +222,14 @@ export const createSampleCollectionContextMenuButton = (): TreeNodeMenuItem[] => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot); traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType }); }, - label: "New SQL Query", + label: t(Keys.contextMenu.newSqlQuery), }); } else if (copilotVersion === "v2.0") { const sampleCollection = useDatabases.getState().sampleDataResourceTokenCollection; items.push({ iconSrc: AddSqlQueryIcon, onClick: () => sampleCollection && sampleCollection.onNewQueryClick(sampleCollection, undefined), - label: "New SQL Query", + label: t(Keys.contextMenu.newSqlQuery), }); } } @@ -247,7 +249,7 @@ export const createStoreProcedureContextMenuItems = ( { iconSrc: DeleteSprocIcon, onClick: () => storedProcedure.delete(), - label: "Delete Stored Procedure", + label: t(Keys.contextMenu.deleteStoredProcedure), }, ]; }; @@ -261,7 +263,7 @@ export const createTriggerContextMenuItems = (container: Explorer, trigger: Trig { iconSrc: DeleteTriggerIcon, onClick: () => trigger.delete(), - label: "Delete Trigger", + label: t(Keys.contextMenu.deleteTrigger), }, ]; }; @@ -278,7 +280,7 @@ export const createUserDefinedFunctionContextMenuItems = ( { iconSrc: DeleteUDFIcon, onClick: () => userDefinedFunction.delete(), - label: "Delete User Defined Function", + label: t(Keys.contextMenu.deleteUdf), }, ]; }; diff --git a/src/Explorer/Tabs/ConflictsTab.ts b/src/Explorer/Tabs/ConflictsTab.ts index 15355bd63..42bb930d2 100644 --- a/src/Explorer/Tabs/ConflictsTab.ts +++ b/src/Explorer/Tabs/ConflictsTab.ts @@ -18,6 +18,8 @@ import { queryConflicts } from "../../Common/dataAccess/queryConflicts"; import { updateDocument } from "../../Common/dataAccess/updateDocument"; import * as DataModels from "../../Contracts/DataModels"; import * as ViewModels from "../../Contracts/ViewModels"; +import { Keys } from "../../Localization/Keys.generated"; +import { t } from "../../Localization/t"; import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; @@ -57,7 +59,7 @@ export default class ConflictsTab extends TabsBase { private _documentsIterator: MinimalQueryIterator; private _container: Explorer; - private _acceptButtonLabel: ko.Observable = ko.observable("Save"); + private _acceptButtonLabel: ko.Observable = ko.observable(t(Keys.common.save)); constructor(options: ViewModels.ConflictsTabOptions) { super(options); @@ -213,9 +215,9 @@ export default class ConflictsTab extends TabsBase { this.selectedConflictContent.subscribe((newContent: string) => this._onEditorContentChange(newContent)); this.conflictOperation.subscribe((newOperationType: string) => { - let operationLabel = "Save"; + let operationLabel = t(Keys.common.save); if (newOperationType === Constants.ConflictOperationType.Replace) { - operationLabel = "Update"; + operationLabel = t(Keys.common.update); } this._acceptButtonLabel(operationLabel); @@ -229,7 +231,7 @@ export default class ConflictsTab extends TabsBase { this._documentsIterator = this.createIterator(); await this.loadNextPage(); } catch (error) { - useDialog.getState().showOkModalDialog("Refresh documents grid failed", getErrorMessage(error)); + useDialog.getState().showOkModalDialog(t(Keys.tabs.conflicts.refreshGridFailed), getErrorMessage(error)); } } @@ -257,11 +259,11 @@ export default class ConflictsTab extends TabsBase { useDialog .getState() .showOkCancelModalDialog( - "Unsaved changes", - "Changes will be lost. Do you want to continue?", - "OK", + t(Keys.tabs.conflicts.unsavedChanges), + t(Keys.tabs.conflicts.changesWillBeLost), + t(Keys.common.ok), async () => await this.resolveConflict(), - "Cancel", + t(Keys.common.cancel), undefined, ); } else { @@ -332,7 +334,7 @@ export default class ConflictsTab extends TabsBase { } catch (error) { this.isExecutionError(true); const errorMessage = getErrorMessage(error); - useDialog.getState().showOkModalDialog("Resolve conflict failed", errorMessage); + useDialog.getState().showOkModalDialog(t(Keys.tabs.conflicts.resolveConflictFailed), errorMessage); TelemetryProcessor.traceFailure( Action.ResolveConflict, { @@ -386,7 +388,7 @@ export default class ConflictsTab extends TabsBase { } catch (error) { this.isExecutionError(true); const errorMessage = getErrorMessage(error); - useDialog.getState().showOkModalDialog("Delete conflict failed", errorMessage); + useDialog.getState().showOkModalDialog(t(Keys.tabs.conflicts.deleteConflictFailed), errorMessage); TelemetryProcessor.traceFailure( Action.DeleteConflict, { @@ -617,7 +619,7 @@ export default class ConflictsTab extends TabsBase { } if (this.discardButton.visible()) { - const label = "Discard"; + const label = t(Keys.common.discard); buttons.push({ iconSrc: DiscardIcon, iconAlt: label, @@ -630,7 +632,7 @@ export default class ConflictsTab extends TabsBase { } if (this.deleteButton.visible()) { - const label = "Delete"; + const label = t(Keys.common.delete); buttons.push({ iconSrc: DeleteIcon, iconAlt: label, diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx index 34411b58e..aef6236b1 100644 --- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx +++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx @@ -41,6 +41,8 @@ import { usePrevious } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper"; import { CosmosFluentProvider, LayoutConstants, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil"; import { useSelectedNode } from "Explorer/useSelectedNode"; import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts"; +import { Keys } from "Localization/Keys.generated"; +import { t } from "Localization/t"; import { isFabric } from "Platform/Fabric/FabricUtil"; import { QueryConstants } from "Shared/Constants"; import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; @@ -349,7 +351,7 @@ export const getTabsButtons = ({ } const buttons: CommandButtonComponentProps[] = []; - const label = !isPreferredApiMongoDB ? "New Item" : "New Document"; + const label = !isPreferredApiMongoDB ? t(Keys.tabs.documents.newItem) : t(Keys.tabs.documents.newDocument); if (getNewDocumentButtonState(editorState).visible) { buttons.push({ iconSrc: NewDocumentIcon, @@ -368,7 +370,7 @@ export const getTabsButtons = ({ } if (getSaveNewDocumentButtonState(editorState).visible) { - const label = "Save"; + const label = t(Keys.common.save); buttons.push({ iconSrc: SaveIcon, iconAlt: label, @@ -386,7 +388,7 @@ export const getTabsButtons = ({ } if (getDiscardNewDocumentChangesButtonState(editorState).visible) { - const label = "Discard"; + const label = t(Keys.common.discard); buttons.push({ iconSrc: DiscardIcon, iconAlt: label, @@ -403,7 +405,7 @@ export const getTabsButtons = ({ } if (getSaveExistingDocumentButtonState(editorState).visible) { - const label = "Update"; + const label = t(Keys.common.update); buttons.push({ iconSrc: SaveIcon, iconAlt: label, @@ -421,7 +423,7 @@ export const getTabsButtons = ({ } if (getDiscardExistingDocumentChangesButtonState(editorState).visible) { - const label = "Discard"; + const label = t(Keys.common.discard); buttons.push({ iconSrc: DiscardIcon, iconAlt: label, @@ -438,7 +440,7 @@ export const getTabsButtons = ({ } if (selectedRows.size > 0) { - const label = "Delete"; + const label = t(Keys.common.delete); buttons.push({ iconSrc: DeleteDocumentIcon, iconAlt: label, @@ -453,7 +455,7 @@ export const getTabsButtons = ({ } if (!isPreferredApiMongoDB) { - const label = "Upload Item"; + const label = t(Keys.tabs.documents.uploadItem); buttons.push({ id: UPLOAD_BUTTON_ID, iconSrc: UploadIcon, @@ -737,17 +739,18 @@ export const DocumentsTabComponent: React.FunctionComponent= 400) { newFailed.push(result.documentId); logConsoleError( - `Failed to delete document ${result.documentId.id()} with status code ${result.statusCode}`, + t(Keys.tabs.documents.deleteDocumentFailedLog, { + documentId: result.documentId.id(), + statusCode: result.statusCode, + }), ); } }); - logConsoleInfo(`Successfully deleted ${newSuccessful.length} document(s)`); + logConsoleInfo(t(Keys.tabs.documents.deleteSuccessLog, { count: newSuccessful.length })); if (newThrottled.length > 0) { - logConsoleError( - `Failed to delete ${newThrottled.length} document(s) due to "Request too large" (429) error. Retrying...`, - ); + logConsoleError(t(Keys.tabs.documents.deleteThrottledLog, { count: newThrottled.length })); } // Update result of the bulk delete: method is called again, because the state variables changed @@ -917,11 +920,11 @@ export const DocumentsTabComponent: React.FunctionComponent { onExecutionErrorChange(true); const errorMessage = getErrorMessage(error); - useDialog.getState().showOkModalDialog("Create document failed", errorMessage); + useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.createDocumentFailed), errorMessage); TelemetryProcessor.traceFailure( Action.CreateDocument, { @@ -1097,7 +1100,7 @@ export const DocumentsTabComponent: React.FunctionComponent { - useDialog.getState().showOkModalDialog("Delete document", "Document successfully deleted."); + useDialog + .getState() + .showOkModalDialog( + t(Keys.tabs.documents.deleteDocumentDialogTitle), + t(Keys.tabs.documents.documentDeleted), + ); return [toDeleteDocumentIds[0]]; }); // ---------------------------------------------------------------------------------------------------- @@ -1251,17 +1259,20 @@ export const DocumentsTabComponent: React.FunctionComponent 1; const documentName = !isPreferredApiMongoDB ? isPlural - ? `the selected ${selectedRows.size} items` - : "the selected item" + ? t(Keys.tabs.documents.selectedItems, { count: selectedRows.size }) + : t(Keys.tabs.documents.selectedItem) : isPlural - ? `the selected ${selectedRows.size} documents` - : "the selected document"; - const msg = `Are you sure you want to delete ${documentName}?`; + ? t(Keys.tabs.documents.selectedDocuments, { count: selectedRows.size }) + : t(Keys.tabs.documents.selectedDocument); + const msg = t(Keys.tabs.documents.confirmDelete, { documentName }); useDialog .getState() .showOkCancelModalDialog( - "Confirm delete", + t(Keys.tabs.documents.confirmDeleteTitle), msg, - "Delete", + t(Keys.common.delete), () => deleteDocuments(Array.from(selectedRows).map((index) => documentIds[index as number])), - "Cancel", + t(Keys.common.cancel), undefined, ); }, [deleteDocuments, documentIds, isPreferredApiMongoDB, selectedRows]); @@ -1823,8 +1834,8 @@ export const DocumentsTabComponent: React.FunctionComponent { onExecutionErrorChange(true); const errorMessage = getErrorMessage(error); - useDialog.getState().showOkModalDialog("Create document failed", errorMessage); + useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.createDocumentFailed), errorMessage); TelemetryProcessor.traceFailure( Action.CreateDocument, { @@ -1949,7 +1960,7 @@ export const DocumentsTabComponent: React.FunctionComponent { onExecutionErrorChange(true); const errorMessage = getErrorMessage(error); - useDialog.getState().showOkModalDialog("Update document failed", errorMessage); + useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.updateDocumentFailed), errorMessage); TelemetryProcessor.traceFailure( Action.UpdateDocument, { @@ -2058,7 +2069,7 @@ export const DocumentsTabComponent: React.FunctionComponent { - let message = 'Some delete requests failed due to a "Request too large" exception (429)'; + let message = t(Keys.tabs.documents.requestTooLargeBase); if (bulkDeleteOperation.count === bulkDeleteProcess.successfulIds.length) { - message += ", but were successfully retried."; + message += ", " + t(Keys.tabs.documents.retriedSuccessfully); } else if (bulkDeleteMode === "inProgress" || bulkDeleteMode === "aborting") { - message += ". Retrying now."; + message += ". " + t(Keys.tabs.documents.retryingNow); } else { message += "."; } - return (message += - " To prevent this in the future, consider increasing the throughput on your container or database."); + return (message += " " + t(Keys.tabs.documents.increaseThroughputTip)); }; const onColumnSelectionChange = (newSelectedColumnIds: string[]): void => { @@ -2128,7 +2138,7 @@ export const DocumentsTabComponent: React.FunctionComponent filter.trim() !== ""); if (nonBlankLastFilters.length > 0) { options.push({ - label: "Saved filters", + label: t(Keys.tabs.documents.savedFilters), options: nonBlankLastFilters, }); } @@ -2157,14 +2167,14 @@ export const DocumentsTabComponent: React.FunctionComponent {tableContainerSizePx?.width >= calculateOffset(selectedColumnIds.length) + 200 && (
refreshDocumentsGrid(false)} - aria-label="Refresh" + aria-label={t(Keys.common.refresh)} tabIndex={0} > - Refresh + {t(Keys.common.refresh)}
)} @@ -2247,7 +2259,7 @@ export const DocumentsTabComponent: React.FunctionComponent loadNextPage(documentsIterator.iterator, false)} onKeyDown={onLoadMoreKeyInput} > - Load more + {t(Keys.tabs.documents.loadMore)} )} @@ -2259,7 +2271,7 @@ export const DocumentsTabComponent: React.FunctionComponent )} {selectedRows.size > 1 && ( - Number of selected documents: {selectedRows.size} + + {t(Keys.tabs.documents.numberOfSelectedDocuments, { count: selectedRows.size })} + )} @@ -2276,42 +2290,43 @@ export const DocumentsTabComponent: React.FunctionComponent { setIsBulkDeleteDialogOpen(false); setBulkDeleteOperation(undefined); }} onCancel={() => setBulkDeleteMode("aborting")} - title={`Deleting ${bulkDeleteOperation.count} document(s)`} - message={`Successfully deleted ${bulkDeleteProcess.successfulIds.length} document(s).`} + title={t(Keys.tabs.documents.deletingDocuments, { count: bulkDeleteOperation.count })} + message={t(Keys.tabs.documents.deletedDocumentsSuccess, { count: bulkDeleteProcess.successfulIds.length })} maxValue={bulkDeleteOperation.count} value={bulkDeleteProcess.successfulIds.length} mode={bulkDeleteMode} >
{(bulkDeleteMode === "aborting" || bulkDeleteMode === "aborted") && ( -
Deleting document(s) was aborted.
+
{t(Keys.tabs.documents.deleteAborted)}
)} {(bulkDeleteProcess.failedIds.length > 0 || (bulkDeleteProcess.throttledIds.length > 0 && bulkDeleteMode !== "inProgress")) && ( - Error - Failed to delete{" "} - {bulkDeleteMode === "inProgress" - ? bulkDeleteProcess.failedIds.length - : bulkDeleteProcess.failedIds.length + bulkDeleteProcess.throttledIds.length}{" "} - document(s). + {t(Keys.tabs.documents.error)} + {t(Keys.tabs.documents.failedToDeleteDocuments, { + count: + bulkDeleteMode === "inProgress" + ? bulkDeleteProcess.failedIds.length + : bulkDeleteProcess.failedIds.length + bulkDeleteProcess.throttledIds.length, + })} )} {bulkDeleteProcess.hasBeenThrottled && ( - Warning + {t(Keys.tabs.documents.warning)} {get429WarningMessageNoSql()}{" "} - Learn More + {t(Keys.common.learnMore)} diff --git a/src/Explorer/Tabs/DocumentsTabV2/__snapshots__/DocumentsTabV2.test.tsx.snap b/src/Explorer/Tabs/DocumentsTabV2/__snapshots__/DocumentsTabV2.test.tsx.snap index 0369f00b4..f3247e88f 100644 --- a/src/Explorer/Tabs/DocumentsTabV2/__snapshots__/DocumentsTabV2.test.tsx.snap +++ b/src/Explorer/Tabs/DocumentsTabV2/__snapshots__/DocumentsTabV2.test.tsx.snap @@ -44,13 +44,13 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = ` } onChange={[Function]} onKeyDown={[Function]} - placeholder="Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents." + placeholder="Type a query predicate (e.g., WHERE c.id="1"), or choose one from the drop down list, or leave empty to query all documents." title="Type a query predicate or choose one from the list." value="" />