Localization second batch (#2410)

* Localization second batch

* update test

* fix tests

* Fix test
This commit is contained in:
sunghyunkang1111
2026-03-05 12:05:45 -06:00
committed by GitHub
parent c343bad630
commit 77132be3b3
11 changed files with 320 additions and 159 deletions

View File

@@ -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() }),
<DeleteDatabaseConfirmationPanel refreshDatabases={() => 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() }),
<DeleteCollectionConfirmationPane refreshDatabases={() => 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),
},
];
};

View File

@@ -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<string> = ko.observable("Save");
private _acceptButtonLabel: ko.Observable<string> = 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,

View File

@@ -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<IDocumentsTabCompone
} else if (result.statusCode >= 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<IDocumentsTabCompone
useDialog
.getState()
.showOkCancelModalDialog(
"Unsaved changes",
"Your unsaved changes will be lost. Do you want to continue?",
"OK",
t(Keys.tabs.documents.unsavedChanges),
t(Keys.tabs.documents.unsavedChangesMessage),
t(Keys.common.ok),
onDiscard,
"Cancel",
t(Keys.common.cancel),
onCancelDiscard,
);
} else {
@@ -1011,7 +1014,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
(error) => {
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<IDocumentsTabCompone
selectedDocumentId.partitionKeyValue = originalPartitionKeyValue;
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,
{
@@ -1174,7 +1177,12 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
// Remove the check for systemKey, remove call to deleteNoSqlDocument(). deleteNoSqlDocuments() should
// always be called for NoSQL.
deletePromise = deleteNoSqlDocument(_collection, toDeleteDocumentIds[0]).then(() => {
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<IDocumentsTabCompone
useDialog
.getState()
.showOkModalDialog(
"Delete documents",
`Some documents failed to delete due to a rate limiting error. Please try again later. To prevent this in the future, consider increasing the throughput on your container or database.`,
t(Keys.tabs.documents.deleteDocumentsDialogTitle),
t(Keys.tabs.documents.throttlingError),
{
linkText: "Learn More",
linkText: t(Keys.common.learnMore),
linkUrl: MONGO_THROTTLING_DOC_URL,
},
);
} else {
useDialog
.getState()
.showOkModalDialog("Delete documents", `Deleting document(s) failed (${error.message})`);
.showOkModalDialog(
t(Keys.tabs.documents.deleteDocumentsDialogTitle),
t(Keys.tabs.documents.deleteFailed, { error: error.message }),
);
}
},
)
@@ -1275,21 +1286,21 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const isPlural = selectedRows.size > 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<IDocumentsTabCompone
const partitionKeyProperty = partitionKeyProperties?.[0];
if (partitionKeyProperty !== "_id" && !_hasShardKeySpecified(documentContent)) {
const message = `The document is lacking the shard property: ${partitionKeyProperty}`;
useDialog.getState().showOkModalDialog("Create document failed", message);
const message = t(Keys.tabs.documents.missingShardProperty, { partitionKeyProperty });
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.createDocumentFailed), message);
onExecutionErrorChange(true);
TelemetryProcessor.traceFailure(
Action.CreateDocument,
@@ -1835,7 +1846,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
},
startKey,
);
Logger.logError("Failed to save new document: Document shard key not defined", "MongoDocumentsTab");
Logger.logError(t(Keys.tabs.documents.missingShardKeyLog), "MongoDocumentsTab");
throw new Error("Document without shard key");
}
@@ -1878,7 +1889,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
(error) => {
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<IDocumentsTabCompone
(error) => {
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<IDocumentsTabCompone
}
} catch (error) {
console.error(error);
useDialog.getState().showOkModalDialog("Refresh documents grid failed", getErrorMessage(error));
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.refreshGridFailed), getErrorMessage(error));
}
},
[createIterator, filterContent],
@@ -2070,18 +2081,17 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
* @returns 429 warning message
*/
const get429WarningMessageNoSql = (): string => {
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<IDocumentsTabCompone
const nonBlankLastFilters = lastFilterContents.filter((filter) => 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<IDocumentsTabCompone
dropdownOptions={getFilterChoices()}
placeholder={
isPreferredApiMongoDB
? "Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents."
: "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."
? t(Keys.tabs.documents.mongoFilterPlaceholder)
: t(Keys.tabs.documents.sqlFilterPlaceholder)
}
title="Type a query predicate or choose one from the list."
title={t(Keys.tabs.documents.filterTooltip)}
value={filterContent}
onChange={updateFilterContent}
onKeyDown={onFilterKeyDown}
bottomLink={{ text: "Learn more", url: DATA_EXPLORER_DOC_URL }}
bottomLink={{ text: t(Keys.common.learnMore), url: DATA_EXPLORER_DOC_URL }}
/>
<Button
appearance="primary"
@@ -2180,10 +2190,12 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
}
}}
disabled={isExecuting && isPreferredApiMongoDB}
aria-label={!isExecuting || isPreferredApiMongoDB ? "Apply filter" : "Cancel"}
aria-label={
!isExecuting || isPreferredApiMongoDB ? t(Keys.tabs.documents.applyFilter) : t(Keys.common.cancel)
}
tabIndex={0}
>
{!isExecuting || isPreferredApiMongoDB ? "Apply Filter" : "Cancel"}
{!isExecuting || isPreferredApiMongoDB ? t(Keys.tabs.documents.applyFilter) : t(Keys.common.cancel)}
</Button>
</div>
<Allotment
@@ -2227,14 +2239,14 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
</div>
{tableContainerSizePx?.width >= calculateOffset(selectedColumnIds.length) + 200 && (
<div
title="Refresh"
title={t(Keys.common.refresh)}
className={styles.refreshBtn}
role="button"
onClick={() => refreshDocumentsGrid(false)}
aria-label="Refresh"
aria-label={t(Keys.common.refresh)}
tabIndex={0}
>
<img src={RefreshIcon} alt="Refresh" />
<img src={RefreshIcon} alt={t(Keys.common.refresh)} />
</div>
)}
</div>
@@ -2247,7 +2259,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onClick={() => loadNextPage(documentsIterator.iterator, false)}
onKeyDown={onLoadMoreKeyInput}
>
Load more
{t(Keys.tabs.documents.loadMore)}
</a>
)}
</div>
@@ -2259,7 +2271,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
language={"json"}
content={selectedDocumentContent}
isReadOnly={false}
ariaLabel={"Document editor"}
ariaLabel={t(Keys.tabs.documents.documentEditor)}
lineNumbers={"on"}
theme={"_theme"}
onContentChanged={_onEditorContentChange}
@@ -2267,7 +2279,9 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
/>
)}
{selectedRows.size > 1 && (
<span style={{ margin: 10 }}>Number of selected documents: {selectedRows.size}</span>
<span style={{ margin: 10 }}>
{t(Keys.tabs.documents.numberOfSelectedDocuments, { count: selectedRows.size })}
</span>
)}
</div>
</Allotment.Pane>
@@ -2276,42 +2290,43 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
{bulkDeleteOperation && (
<ProgressModalDialog
isOpen={isBulkDeleteDialogOpen}
dismissText="Abort"
dismissText={t(Keys.tabs.documents.abort)}
onDismiss={() => {
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}
>
<div className={styles.deleteProgressContent}>
{(bulkDeleteMode === "aborting" || bulkDeleteMode === "aborted") && (
<div style={{ paddingBottom: tokens.spacingVerticalL }}>Deleting document(s) was aborted.</div>
<div style={{ paddingBottom: tokens.spacingVerticalL }}>{t(Keys.tabs.documents.deleteAborted)}</div>
)}
{(bulkDeleteProcess.failedIds.length > 0 ||
(bulkDeleteProcess.throttledIds.length > 0 && bulkDeleteMode !== "inProgress")) && (
<MessageBar intent="error" style={{ marginBottom: tokens.spacingVerticalL }}>
<MessageBarBody>
<MessageBarTitle>Error</MessageBarTitle>
Failed to delete{" "}
{bulkDeleteMode === "inProgress"
? bulkDeleteProcess.failedIds.length
: bulkDeleteProcess.failedIds.length + bulkDeleteProcess.throttledIds.length}{" "}
document(s).
<MessageBarTitle>{t(Keys.tabs.documents.error)}</MessageBarTitle>
{t(Keys.tabs.documents.failedToDeleteDocuments, {
count:
bulkDeleteMode === "inProgress"
? bulkDeleteProcess.failedIds.length
: bulkDeleteProcess.failedIds.length + bulkDeleteProcess.throttledIds.length,
})}
</MessageBarBody>
</MessageBar>
)}
{bulkDeleteProcess.hasBeenThrottled && (
<MessageBar intent="warning">
<MessageBarBody>
<MessageBarTitle>Warning</MessageBarTitle>
<MessageBarTitle>{t(Keys.tabs.documents.warning)}</MessageBarTitle>
{get429WarningMessageNoSql()}{" "}
<Link href={NO_SQL_THROTTLING_DOC_URL} target="_blank">
Learn More
{t(Keys.common.learnMore)}
</Link>
</MessageBarBody>
</MessageBar>

View File

@@ -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=""
/>
<Button
appearance="primary"
aria-label="Apply filter"
aria-label="Apply Filter"
data-test="DocumentsTab/ApplyFilter"
disabled={false}
onClick={[Function]}

View File

@@ -1,6 +1,8 @@
import React, { Component } from "react";
import { configContext } from "../../../ConfigContext";
import * as ViewModels from "../../../Contracts/ViewModels";
import { Keys } from "../../../Localization/Keys.generated";
import { t } from "../../../Localization/t";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../../UserContext";
@@ -212,7 +214,7 @@ export default class MongoShellTabComponent extends Component<
src={this.state.url}
id={this.props.tabsBaseInstance.tabId}
onLoad={(event) => this.setContentFocus(event)}
title="Mongo Shell"
title={t(Keys.tabs.mongoShell.title)}
role="tabpanel"
></iframe>
);

View File

@@ -17,6 +17,8 @@ import { QueryTabStyles, useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { KeyboardAction } from "KeyboardShortcuts";
import { Keys } from "Localization/Keys.generated";
import { t } from "Localization/t";
import { QueryConstants } from "Shared/Constants";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { Action } from "Shared/Telemetry/TelemetryConstants";
@@ -315,7 +317,9 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
};
public onSaveQueryClick = (): void => {
useSidePanel.getState().openSidePanel("Save Query", <SaveQueryPane explorer={this.props.collection.container} />);
useSidePanel
.getState()
.openSidePanel(t(Keys.tabs.query.saveQuery), <SaveQueryPane explorer={this.props.collection.container} />);
};
public launchQueryCopilotChat = (): void => {
@@ -325,7 +329,10 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
public onSavedQueriesClick = (): void => {
useSidePanel
.getState()
.openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={this.props.collection.container} />);
.openSidePanel(
t(Keys.tabs.query.openSavedQueries),
<BrowseQueriesPane explorer={this.props.collection.container} />,
);
};
public toggleResult(): void {
@@ -473,7 +480,8 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
if (this.executeQueryButton.visible) {
const label = this.state.selectedContent?.length > 0 ? "Execute Selection" : "Execute Query";
const label =
this.state.selectedContent?.length > 0 ? t(Keys.tabs.query.executeSelection) : t(Keys.tabs.query.executeQuery);
buttons.push({
iconSrc: ExecuteQueryIcon,
iconAlt: label,
@@ -490,7 +498,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
if (this.saveQueryButton.visible) {
if (configContext.platform !== Platform.Fabric) {
const label = "Save Query";
const label = t(Keys.tabs.query.saveQuery);
buttons.push({
iconSrc: SaveQueryIcon,
iconAlt: label,
@@ -507,11 +515,11 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
buttons.push({
iconSrc: DownloadQueryIcon,
iconAlt: "Download Query",
iconAlt: t(Keys.tabs.query.downloadQuery),
keyboardAction: KeyboardAction.DOWNLOAD_ITEM,
onCommandClick: this.onDownloadQueryClick,
commandButtonLabel: "Download Query",
ariaLabel: "Download Query",
commandButtonLabel: t(Keys.tabs.query.downloadQuery),
ariaLabel: t(Keys.tabs.query.downloadQuery),
hasPopup: false,
disabled: !this.saveQueryButton.enabled,
});
@@ -568,7 +576,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
// }
if (!this.props.isPreferredApiMongoDB && this.state.isExecuting) {
const label = "Cancel query";
const label = t(Keys.tabs.query.cancelQuery);
buttons.push({
iconSrc: CancelQueryIcon,
iconAlt: label,
@@ -589,23 +597,23 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
const verticalButton: CommandButtonComponentProps = {
isSelected: this.state.queryResultsView === SplitterDirection.Vertical,
iconSrc: this.state.queryResultsView === SplitterDirection.Vertical ? CheckIcon : undefined,
commandButtonLabel: "Vertical",
ariaLabel: "Vertical",
commandButtonLabel: t(Keys.tabs.query.vertical),
ariaLabel: t(Keys.tabs.query.vertical),
onCommandClick: () => this._setViewLayout(SplitterDirection.Vertical),
hasPopup: false,
};
const horizontalButton: CommandButtonComponentProps = {
isSelected: this.state.queryResultsView === SplitterDirection.Horizontal,
iconSrc: this.state.queryResultsView === SplitterDirection.Horizontal ? CheckIcon : undefined,
commandButtonLabel: "Horizontal",
ariaLabel: "Horizontal",
commandButtonLabel: t(Keys.tabs.query.horizontal),
ariaLabel: t(Keys.tabs.query.horizontal),
onCommandClick: () => this._setViewLayout(SplitterDirection.Horizontal),
hasPopup: false,
};
return {
commandButtonLabel: "View",
ariaLabel: "View",
commandButtonLabel: t(Keys.tabs.query.view),
ariaLabel: t(Keys.tabs.query.view),
hasPopup: true,
children: [verticalButton, horizontalButton],
};
@@ -782,7 +790,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
modelMarkers={this.state.modelMarkers}
isReadOnly={false}
wordWrap={"on"}
ariaLabel={"Editing Query"}
ariaLabel={t(Keys.tabs.query.editingQuery)}
lineNumbers={"on"}
theme={this.props.monacoTheme}
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}

View File

@@ -11,6 +11,8 @@ import { createStoredProcedure } from "../../../Common/dataAccess/createStoredPr
import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure";
import { updateStoredProcedure } from "../../../Common/dataAccess/updateStoredProcedure";
import * as ViewModels from "../../../Contracts/ViewModels";
import { Keys } from "../../../Localization/Keys.generated";
import { t } from "../../../Localization/t";
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
import { useTabs } from "../../../hooks/useTabs";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
@@ -133,7 +135,7 @@ export default class StoredProcedureTabComponent extends React.Component<
this.collection = this.props.collection;
this.executeResultsEditorId = `executestoredprocedureresults${this.props.scriptTabBaseInstance.tabId}`;
this.executeLogsEditorId = `executestoredprocedurelogs${this.props.scriptTabBaseInstance.tabId}`;
this.props.scriptTabBaseInstance.ariaLabel("Stored Procedure Body");
this.props.scriptTabBaseInstance.ariaLabel(t(Keys.tabs.storedProcedure.body));
this.props.iStorProcTabComponentAccessor({
onExecuteSprocsResultEvent: this.onExecuteSprocsResult.bind(this),
@@ -318,7 +320,7 @@ export default class StoredProcedureTabComponent extends React.Component<
protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
const label = "Save";
const label = t(Keys.common.save);
if (this.state.saveButton.visible) {
buttons.push({
iconSrc: SaveIcon,
@@ -333,7 +335,7 @@ export default class StoredProcedureTabComponent extends React.Component<
}
if (this.state.updateButton.visible) {
const label = "Update";
const label = t(Keys.common.update);
buttons.push({
iconSrc: SaveIcon,
iconAlt: label,
@@ -347,7 +349,7 @@ export default class StoredProcedureTabComponent extends React.Component<
}
if (this.state.discardButton.visible) {
const label = "Discard";
const label = t(Keys.common.discard);
buttons.push({
iconSrc: DiscardIcon,
iconAlt: label,
@@ -361,7 +363,7 @@ export default class StoredProcedureTabComponent extends React.Component<
}
if (this.state.executeButton.visible) {
const label = "Execute";
const label = t(Keys.common.execute);
buttons.push({
iconSrc: ExecuteQueryIcon,
iconAlt: label,
@@ -519,7 +521,7 @@ export default class StoredProcedureTabComponent extends React.Component<
<div className="tab-pane flexContainer stored-procedure-tab" role="tabpanel">
<div className="storedTabForm flexContainer">
<div className="formTitleFirst">
Stored Procedure Id
{t(Keys.tabs.storedProcedure.id)}
<span className="mandatoryStar" style={{ color: "#ff0707", fontSize: "14px", fontWeight: "bold" }}>
*&nbsp;
</span>
@@ -531,28 +533,28 @@ export default class StoredProcedureTabComponent extends React.Component<
required
pattern={ValidCosmosDbIdInputPattern.source}
title={ValidCosmosDbIdDescription}
aria-label="Stored procedure id"
placeholder="Enter the new stored procedure id"
aria-label={t(Keys.tabs.storedProcedure.idAriaLabel)}
placeholder={t(Keys.tabs.storedProcedure.idPlaceholder)}
size={40}
value={this.state.id}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => this.handleIdOnChange(event)}
/>
</span>
<div className="spUdfTriggerHeader">Stored Procedure Body</div>
<div className="spUdfTriggerHeader">{t(Keys.tabs.storedProcedure.body)}</div>
<EditorReact
language={"javascript"}
content={this.state.sProcEditorContent}
isReadOnly={false}
ariaLabel={"Stored procedure body"}
ariaLabel={t(Keys.tabs.storedProcedure.bodyAriaLabel)}
lineNumbers={"on"}
theme={"_theme"}
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
/>
{this.state.hasResults && (
<div className="results-container">
<Pivot aria-label="Successful execution of stored procedure" style={{ height: "100%" }}>
<Pivot aria-label={t(Keys.tabs.storedProcedure.successfulExecution)} style={{ height: "100%" }}>
<PivotItem
headerText="Result"
headerText={t(Keys.common.result)}
headerButtonProps={{
"data-order": 1,
"data-title": "Result",
@@ -563,11 +565,11 @@ export default class StoredProcedureTabComponent extends React.Component<
language={"javascript"}
content={this.state.resultData}
isReadOnly={true}
ariaLabel={"Execute stored procedure result"}
ariaLabel={t(Keys.tabs.storedProcedure.resultAriaLabel)}
/>
</PivotItem>
<PivotItem
headerText="console.log"
headerText={t(Keys.tabs.storedProcedure.consoleLogTab)}
headerButtonProps={{
"data-order": 2,
"data-title": "console.log",
@@ -578,7 +580,7 @@ export default class StoredProcedureTabComponent extends React.Component<
language={"javascript"}
content={this.state.logsData}
isReadOnly={true}
ariaLabel={"Execute stored procedure logs"}
ariaLabel={t(Keys.tabs.storedProcedure.logsAriaLabel)}
/>
</PivotItem>
</Pivot>
@@ -586,16 +588,16 @@ export default class StoredProcedureTabComponent extends React.Component<
)}
{this.state.hasErrors && (
<div className="errors-container">
<div className="errors-header">Errors:</div>
<div className="errors-header">{t(Keys.tabs.storedProcedure.errors)}</div>
<div className="errorContent">
<span className="errorMessage">{this.state.error}</span>
<span className="errorDetailsLink">
<a
aria-label="Error details link"
aria-label={t(Keys.tabs.storedProcedure.errorDetailsAriaLabel)}
onClick={() => this.onErrorDetailsClick()}
onKeyPress={(event: React.KeyboardEvent<HTMLAnchorElement>) => this.onErrorDetailsKeyPress(event)}
>
More details
{t(Keys.tabs.storedProcedure.moreDetails)}
</a>
</span>
</div>

View File

@@ -11,6 +11,8 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
import { createTrigger } from "../../Common/dataAccess/createTrigger";
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
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 { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
@@ -19,8 +21,8 @@ import { EditorReact } from "../Controls/Editor/EditorReact";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
import TriggerTab from "./TriggerTab";
const triggerTypeOptions: IDropdownOption[] = [
{ key: "Pre", text: "Pre" },
{ key: "Post", text: "Post" },
{ key: "Pre", text: t(Keys.tabs.trigger.pre) },
{ key: "Post", text: t(Keys.tabs.trigger.post) },
];
const dropdownStyles: Partial<IDropdownStyles> = {
@@ -147,10 +149,10 @@ const dropdownStyles: Partial<IDropdownStyles> = {
};
const triggerOperationOptions: IDropdownOption[] = [
{ key: "All", text: "All" },
{ key: "Create", text: "Create" },
{ key: "Delete", text: "Delete" },
{ key: "Replace", text: "Replace" },
{ key: "All", text: t(Keys.tabs.trigger.all) },
{ key: "Create", text: t(Keys.tabs.trigger.operationCreate) },
{ key: "Delete", text: t(Keys.tabs.trigger.operationDelete) },
{ key: "Replace", text: t(Keys.tabs.trigger.operationReplace) },
];
interface Ibutton {
@@ -334,7 +336,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
const label = "Save";
const label = t(Keys.common.save);
if (this.saveButton.visible) {
buttons.push({
setState: this.setState,
@@ -351,7 +353,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
}
if (this.updateButton.visible) {
const label = "Update";
const label = t(Keys.common.update);
buttons.push({
...this,
iconSrc: SaveIcon,
@@ -366,7 +368,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
}
if (this.discardButton.visible) {
const label = "Discard";
const label = t(Keys.common.discard);
buttons.push({
setState: this.setState,
...this,
@@ -415,14 +417,14 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
<div className="tab-pane flexContainer trigger-form" role="tabpanel">
<TextField
className="trigger-field"
label="Trigger Id"
label={t(Keys.tabs.trigger.id)}
id="entityTimeId"
autoFocus
required
type="text"
pattern={ValidCosmosDbIdInputPattern.source}
title={ValidCosmosDbIdDescription}
placeholder="Enter the new trigger id"
placeholder={t(Keys.tabs.trigger.idPlaceholder)}
size={40}
value={triggerId}
readOnly={!isIdEditable}
@@ -446,8 +448,8 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
}}
/>
<Dropdown
placeholder="Trigger Type"
label="Trigger Type"
placeholder={t(Keys.tabs.trigger.type)}
label={t(Keys.tabs.trigger.type)}
options={triggerTypeOptions}
selectedKey={triggerType}
className="trigger-field"
@@ -455,8 +457,8 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
styles={dropdownStyles}
/>
<Dropdown
placeholder="Trigger Operation"
label="Trigger Operation"
placeholder={t(Keys.tabs.trigger.operation)}
label={t(Keys.tabs.trigger.operation)}
selectedKey={triggerOperation}
options={triggerOperationOptions}
className="trigger-field"
@@ -465,12 +467,12 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
}
styles={dropdownStyles}
/>
<Label className="trigger-field">Trigger Body</Label>
<Label className="trigger-field">{t(Keys.tabs.trigger.body)}</Label>
<EditorReact
language={"json"}
content={triggerBody}
isReadOnly={false}
ariaLabel={"Graph JSON"}
ariaLabel={t(Keys.tabs.trigger.bodyAriaLabel)}
onContentChanged={this.handleTriggerBodyChange}
/>
</div>

View File

@@ -12,6 +12,8 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
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";
@@ -82,7 +84,7 @@ export default class UserDefinedFunctionTabContent extends Component<
protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
const label = "Save";
const label = t(Keys.common.save);
if (this.saveButton.visible) {
buttons.push({
...this,
@@ -99,7 +101,7 @@ export default class UserDefinedFunctionTabContent extends Component<
}
if (this.updateButton.visible) {
const label = "Update";
const label = t(Keys.common.update);
buttons.push({
...this,
iconSrc: SaveIcon,
@@ -114,7 +116,7 @@ export default class UserDefinedFunctionTabContent extends Component<
}
if (this.discardButton.visible) {
const label = "Discard";
const label = t(Keys.common.discard);
buttons.push({
setState: this.setState,
...this,
@@ -265,7 +267,7 @@ export default class UserDefinedFunctionTabContent extends Component<
<FluentProvider theme={currentTheme}>
<TextField
className="trigger-field"
label="User Defined Function Id"
label={t(Keys.tabs.udf.id)}
id="entityTimeId"
autoFocus
required
@@ -273,7 +275,7 @@ export default class UserDefinedFunctionTabContent extends Component<
type="text"
pattern={ValidCosmosDbIdInputPattern.source}
title={ValidCosmosDbIdDescription}
placeholder="Enter the new user defined function id"
placeholder={t(Keys.tabs.udf.idPlaceholder)}
size={40}
value={udfId}
onChange={this.handleUdfIdChange}
@@ -299,12 +301,12 @@ export default class UserDefinedFunctionTabContent extends Component<
}}
/>{" "}
</FluentProvider>
<Label className="trigger-field">User Defined Function Body</Label>
<Label className="trigger-field">{t(Keys.tabs.udf.body)}</Label>
<EditorReact
language={"javascript"}
content={udfBody}
isReadOnly={false}
ariaLabel={"User defined function body"}
ariaLabel={t(Keys.tabs.udf.bodyAriaLabel)}
onContentChanged={this.handleUdfBodyChange}
/>
</div>

View File

@@ -291,5 +291,126 @@
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
},
"contextMenu": {
"newContainer": "New {{containerName}}",
"restoreContainer": "Restore {{containerName}}",
"deleteDatabase": "Delete {{databaseName}}",
"deleteContainer": "Delete {{containerName}}",
"newSqlQuery": "New SQL Query",
"newQuery": "New Query",
"openMongoShell": "Open Mongo Shell",
"newShell": "New Shell",
"openCassandraShell": "Open Cassandra Shell",
"newStoredProcedure": "New Stored Procedure",
"newUdf": "New UDF",
"newTrigger": "New Trigger",
"deleteStoredProcedure": "Delete Stored Procedure",
"deleteTrigger": "Delete Trigger",
"deleteUdf": "Delete User Defined Function"
},
"tabs": {
"documents": {
"newItem": "New Item",
"newDocument": "New Document",
"uploadItem": "Upload Item",
"applyFilter": "Apply Filter",
"unsavedChanges": "Unsaved changes",
"unsavedChangesMessage": "Your unsaved changes will be lost. Do you want to continue?",
"createDocumentFailed": "Create document failed",
"updateDocumentFailed": "Update document failed",
"documentDeleted": "Document successfully deleted.",
"deleteDocumentDialogTitle": "Delete document",
"deleteDocumentsDialogTitle": "Delete documents",
"throttlingError": "Some documents failed to delete due to a rate limiting error. Please try again later. To prevent this in the future, consider increasing the throughput on your container or database.",
"deleteFailed": "Deleting document(s) failed ({{error}})",
"missingShardProperty": "The document is lacking the shard property: {{partitionKeyProperty}}",
"refreshGridFailed": "Refresh documents grid failed",
"confirmDelete": "Are you sure you want to delete {{documentName}}?",
"confirmDeleteTitle": "Confirm delete",
"selectedItems": "the selected {{count}} items",
"selectedItem": "the selected item",
"selectedDocuments": "the selected {{count}} documents",
"selectedDocument": "the selected document",
"deleteDocumentFailedLog": "Failed to delete document {{documentId}} with status code {{statusCode}}",
"deleteSuccessLog": "Successfully deleted {{count}} document(s)",
"deleteThrottledLog": "Failed to delete {{count}} document(s) due to \"Request too large\" (429) error. Retrying...",
"missingShardKeyLog": "Failed to save new document: Document shard key not defined",
"filterTooltip": "Type a query predicate or choose one from the list.",
"loadMore": "Load more",
"documentEditor": "Document editor",
"savedFilters": "Saved filters",
"defaultFilters": "Default filters",
"abort": "Abort",
"deletingDocuments": "Deleting {{count}} document(s)",
"deletedDocumentsSuccess": "Successfully deleted {{count}} document(s).",
"deleteAborted": "Deleting document(s) was aborted.",
"failedToDeleteDocuments": "Failed to delete {{count}} document(s).",
"requestTooLargeBase": "Some delete requests failed due to a \"Request too large\" exception (429)",
"retriedSuccessfully": "but were successfully retried.",
"retryingNow": "Retrying now.",
"increaseThroughputTip": "To prevent this in the future, consider increasing the throughput on your container or database.",
"numberOfSelectedDocuments": "Number of selected documents: {{count}}",
"mongoFilterPlaceholder": "Type a query predicate (e.g., {\"id\":\"foo\"}), or choose one from the drop down list, or leave empty to query all documents.",
"sqlFilterPlaceholder": "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.",
"error": "Error",
"warning": "Warning"
},
"query": {
"executeQuery": "Execute Query",
"executeSelection": "Execute Selection",
"saveQuery": "Save Query",
"downloadQuery": "Download Query",
"cancelQuery": "Cancel query",
"openSavedQueries": "Open Saved Queries",
"vertical": "Vertical",
"horizontal": "Horizontal",
"view": "View",
"editingQuery": "Editing Query"
},
"storedProcedure": {
"id": "Stored Procedure Id",
"idPlaceholder": "Enter the new stored procedure id",
"idAriaLabel": "Stored procedure id",
"body": "Stored Procedure Body",
"bodyAriaLabel": "Stored procedure body",
"successfulExecution": "Successful execution of stored procedure",
"resultAriaLabel": "Execute stored procedure result",
"logsAriaLabel": "Execute stored procedure logs",
"errors": "Errors:",
"errorDetailsAriaLabel": "Error details link",
"moreDetails": "More details",
"consoleLogTab": "console.log"
},
"trigger": {
"id": "Trigger Id",
"idPlaceholder": "Enter the new trigger id",
"type": "Trigger Type",
"operation": "Trigger Operation",
"body": "Trigger Body",
"bodyAriaLabel": "Trigger body",
"pre": "Pre",
"post": "Post",
"all": "All",
"operationCreate": "Create",
"operationDelete": "Delete",
"operationReplace": "Replace"
},
"udf": {
"id": "User Defined Function Id",
"idPlaceholder": "Enter the new user defined function id",
"body": "User Defined Function Body",
"bodyAriaLabel": "User defined function body"
},
"conflicts": {
"unsavedChanges": "Unsaved changes",
"changesWillBeLost": "Changes will be lost. Do you want to continue?",
"resolveConflictFailed": "Resolve conflict failed",
"deleteConflictFailed": "Delete conflict failed",
"refreshGridFailed": "Refresh documents grid failed"
},
"mongoShell": {
"title": "Mongo Shell"
}
}
}

View File

@@ -4,9 +4,14 @@ import Adapter from "enzyme-adapter-react-16";
import "jest-canvas-mock";
import enableHooks from "jest-react-hooks-shallow";
import { TextDecoder, TextEncoder } from "util";
import i18n from "./i18n";
import enResources from "./Localization/en/Resources.json";
configure({ adapter: new Adapter() });
initializeIcons();
// Load English translations synchronously so t() returns real values in tests
i18n.addResourceBundle("en", "Resources", enResources, true, true);
if (typeof window.URL.createObjectURL === "undefined") {
Object.defineProperty(window.URL, "createObjectURL", { value: () => {} });
}