Compare commits

..

20 Commits

Author SHA1 Message Date
Sung-Hyun Kang
f1dab2f7b8 Fix unit tests 2026-03-06 01:58:28 -06:00
Sung-Hyun Kang
12d535d000 Added localizations 3rd batch 2026-03-05 20:41:36 -06:00
olprod
915f549df9 Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2444293 (#2412)
* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2444219

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2444293

---------

Co-authored-by: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com>
2026-03-05 12:54:04 -06:00
sunghyunkang1111
77132be3b3 Localization second batch (#2410)
* Localization second batch

* update test

* fix tests

* Fix test
2026-03-05 12:05:45 -06:00
jawelton74
c343bad630 Remove chromium from the playwright test matrix. (#2409) 2026-03-04 06:05:27 -08:00
sunghyunkang1111
9579d1270b Localization first batch (#2408) 2026-03-03 16:17:18 -06:00
sunghyunkang1111
9496cf83ee Fixing metrics (#2395)
* Fixing metrics

* run prettier
2026-03-02 11:32:51 -06:00
jawelton74
dbe26654f1 Add new config for sov. clouds (#2402)
* Add new cloud origins for CSP.

* Remove the explorer endpoint, it is not needed here.

* Add other clouds as well.

* Fix formatting.
2026-03-02 07:52:00 -08:00
sakshigupta12feb
b478f2732c fixed the % update issue in copyjobs (#2399)
Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
2026-03-02 17:37:08 +05:30
sunghyunkang1111
204444b878 Added quote escaping for partition key extraction (#2403) 2026-02-27 14:58:14 -06:00
sunghyunkang1111
2e5c355479 Added localization build (#2380)
* Added localization build

* Commit types

* Added locProject.json

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2425084 (#2392)

* Fixed package.json

* Fix compilation error

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2431677 (#2396)

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2432830 (#2397)

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2431872

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2432783

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2432830

* Localized file check-in by OneLocBuild Task: Build definition ID 13114: Build ID 2434398 (#2400)

---------

Co-authored-by: olprod <olprod@microsoft.com>
2026-02-26 13:32:33 -06:00
Laurent Nguyen
5832170b2b Fix extractHeaderStatus to handle undefined and non-string messages (#2393)
* Fix extractHeaderStatus to handle undefined and non-string messages

* Fix unsafe casts

---------

Co-authored-by: Laurent Nguyen <languye@microsoft.com>
2026-02-20 16:42:17 +01:00
sunghyunkang1111
cc26e2800e Fix health metrics race condition and improve expected-failure handling (#2387)
* Fix health monitoring

* fix compile error
2026-02-17 12:54:04 -06:00
Laurent Nguyen
39ac7cf3f2 Add SC1010 to codes redacted in syntax error messages (#2388) 2026-02-16 18:34:54 +01:00
bogercraig
ed3a79f880 Adding origins for additional sov cloud. (#2389) 2026-02-13 16:58:07 -08:00
asier-isayas
68ba19d406 dependabot security alerts + delete containers after each migration e2e test (#2384)
* dependabot security alerts + delete containers after each migration e2e test

* catch error

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
2026-02-12 11:24:30 -08:00
asier-isayas
81ad13508b dependabot batch 3 (#2379)
Co-authored-by: Asier Isayas <aisayas@microsoft.com>
2026-02-11 12:52:05 -08:00
sunghyunkang1111
3f959e2235 Added expected errort handling (#2368) 2026-02-09 14:34:40 -06:00
sindhuba
67250f0f6b Fix timeout for flaky tests (#2378)
* Add changes for Load more option to work

* Remove localhost

* Add Mongo Pagination tests

* Run npm format

* Fix timeout in tests

* Revert "Fix timeout in tests"

This reverts commit 418b81c490.

* Fix timeout in tests

* Minor fix

* Revert "Minor fix"

This reverts commit 87fe289e3a.

* Revert "Fix timeout in tests"

This reverts commit 11c5748a31.

* Fix timeout
2026-02-05 16:14:37 -08:00
asier-isayas
df6312038a Dependabot Part 2 (#2370)
* dependabot part 1

* fix npm ci

* remove overrides

* undo playwright test update

* dependabot part 2

* cross-spawn

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
2026-02-05 08:45:21 -08:00
104 changed files with 9253 additions and 6993 deletions

View File

@@ -18,10 +18,10 @@ jobs:
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: node utils/codeMetrics.js
env:
@@ -31,10 +31,10 @@ jobs:
name: "Compile TypeScript"
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: npm run compile
- run: npm run compile:strict
@@ -43,10 +43,10 @@ jobs:
name: "Check Format"
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: npm run format:check
lint:
@@ -54,10 +54,10 @@ jobs:
name: "Lint"
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: npm run lint
unittest:
@@ -65,10 +65,10 @@ jobs:
name: "Unit Tests"
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: npm run test
build:
@@ -76,10 +76,10 @@ jobs:
name: "Build"
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: npm run build:contracts
- name: Restore Build Cache
@@ -168,10 +168,10 @@ jobs:
shardTotal: [20]
steps:
- uses: actions/checkout@v4
- name: Use Node.js 24.13.0
- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: npx playwright install --with-deps
- name: "Az CLI login"
@@ -236,7 +236,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24.13.0
node-version: 18
- name: Install dependencies
run: npm ci

View File

@@ -22,7 +22,7 @@ jobs:
env:
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: "Az CLI login"
uses: azure/login@v1
@@ -31,9 +31,9 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Use Node.js 24.13.0
uses: actions/setup-node@v4
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 24.13.0
node-version: 18.x
- run: npm ci
- run: node utils/cleanupDBs.js

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ Contracts/*
failure.png
screenshots/*
GettingStarted-ignore*.ipynb
src/Localization/Keys.generated.ts
/test-results/
/playwright-report/
/blob-report/

7362
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
"@fluentui/react": "8.119.0",
"@fluentui/react-components": "9.54.2",
"@jupyterlab/services": "6.0.2",
"@jupyterlab/terminal": "3.0.3",
"@jupyterlab/terminal": "3.6.8",
"@microsoft/applicationinsights-web": "2.6.1",
"@nteract/commutable": "7.5.1",
"@nteract/connected-components": "6.8.2",
@@ -39,6 +39,7 @@
"@nteract/transform-plotly": "6.1.6",
"@nteract/transform-vdom": "4.0.11",
"@nteract/transform-vega": "7.0.6",
"@octokit/request": "8.4.1",
"@octokit/rest": "17.9.2",
"@phosphor/widgets": "1.9.3",
"@testing-library/jest-dom": "6.4.6",
@@ -57,7 +58,7 @@
"copy-webpack-plugin": "11.0.0",
"crossroads": "0.12.2",
"css-element-queries": "1.1.1",
"d3": "7.8.5",
"d3": "7.9.0",
"datatables.net-colreorder-dt": "1.7.0",
"datatables.net-dt": "1.13.8",
"date-fns": "1.29.0",
@@ -71,6 +72,7 @@
"i18next": "23.11.5",
"i18next-browser-languagedetector": "6.0.1",
"i18next-http-backend": "3.0.2",
"i18next-resources-to-backend": "1.2.1",
"iframe-resizer-react": "1.1.0",
"immer": "9.0.6",
"immutable": "4.0.0-rc.12",
@@ -79,11 +81,15 @@
"jquery-typeahead": "2.11.1",
"jquery-ui-dist": "1.13.2",
"knockout": "3.5.1",
"lodash": "4.17.23",
"lodash-es": "4.17.23",
"min-document": "2.19.1",
"mkdirp": "1.0.4",
"monaco-editor": "0.44.0",
"ms": "2.1.3",
"nanoid": "3.3.8",
"p-retry": "6.2.1",
"patch-package": "8.0.0",
"patch-package": "8.0.1",
"plotly.js-cartesian-dist-min": "1.52.3",
"post-robot": "10.0.42",
"q": "1.5.1",
@@ -102,7 +108,6 @@
"react-youtube": "9.0.1",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"sanitize-html": "2.3.3",
"shell-quote": "1.7.3",
"styled-components": "5.0.1",
"swr": "0.4.0",
@@ -115,12 +120,25 @@
"ws": "8.17.1",
"zustand": "3.5.0"
},
"overrides": {
"d3-color": "3.1.0",
"cross-spawn": "7.0.6",
"less-vars-loader": {
"json5": "1.0.2"
},
"trim": "0.0.3",
"@octokit/plugin-paginate-rest": "9.2.2",
"@octokit/request-error": "5.1.1",
"@octokit/request": "8.4.1",
"prismjs": "1.30.0",
"sanitize-html": "2.17.0"
},
"devDependencies": {
"@babel/core": "7.24.7",
"@babel/core": "7.29.0",
"@babel/preset-env": "7.24.7",
"@babel/preset-react": "7.24.7",
"@babel/preset-typescript": "7.24.7",
"@playwright/test": "1.49.1",
"@playwright/test": "1.55.1",
"@testing-library/react": "11.2.3",
"@types/applicationinsights-js": "1.0.7",
"@types/codemirror": "0.0.56",
@@ -145,7 +163,7 @@
"@types/react-window": "1.8.8",
"@types/sanitize-html": "1.27.2",
"@types/sinon": "2.3.3",
"@types/styled-components": "5.1.1",
"@types/styled-components": "5.1.32",
"@types/underscore": "1.7.36",
"@types/youtube-player": "5.5.6",
"@typescript-eslint/eslint-plugin": "6.7.4",
@@ -153,6 +171,7 @@
"@webpack-cli/serve": "2.0.5",
"babel-jest": "29.7.0",
"babel-loader": "8.1.0",
"brace-expansion": "1.1.12",
"buffer": "5.1.0",
"case-sensitive-paths-webpack-plugin": "2.4.0",
"create-file-webpack": "1.0.2",
@@ -170,6 +189,7 @@
"html-inline-css-webpack-plugin": "1.11.2",
"html-loader": "5.0.0",
"html-webpack-plugin": "5.5.3",
"i18next-resources-for-ts": "2.0.0",
"jest": "29.7.0",
"jest-canvas-mock": "2.5.2",
"jest-circus": "29.7.0",
@@ -177,13 +197,13 @@
"jest-html-loader": "1.0.0",
"jest-react-hooks-shallow": "1.5.1",
"jest-trx-results-processor": "3.0.2",
"js-yaml": "3.14.2",
"less": "4.5.1",
"less-loader": "11.1.3",
"less-vars-loader": "1.1.0",
"loader-utils": "2.0.4",
"mini-css-extract-plugin": "2.1.0",
"monaco-editor-webpack-plugin": "7.1.0",
"node-fetch": "3.3.2",
"node-fetch": "2.6.7",
"prettier": "3.0.3",
"process": "0.11.10",
"querystring-es3": "0.2.1",
@@ -196,15 +216,17 @@
"typedoc": "0.26.2",
"typescript": "4.9.5",
"url-loader": "4.1.1",
"values-to-keys": "1.1.0",
"wait-on": "9.0.3",
"webpack": "5.88.2",
"webpack": "5.104.1",
"webpack-bundle-analyzer": "5.2.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.2.3",
"ws": "8.17.1"
},
"scripts": {
"postinstall": "patch-package",
"postinstall": "patch-package && npm run generate:i18n-keys",
"prestart": "npm run generate:i18n-keys",
"start": "webpack serve --mode development",
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
"build:dataExplorer:ci": "npm run build:ci",
@@ -231,6 +253,7 @@
"strict:find": "node ./strict-null-checks/find.js",
"strict:add": "node ./strict-null-checks/auto-add.js",
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks",
"generate:i18n-keys": "node utils/generateI18nKeys.mjs",
"generateARMClients": "npx ts-node utils/armClientGenerator/generator.ts"
},
"repository": {

View File

@@ -26,15 +26,6 @@ export default defineConfig({
},
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
launchOptions: {
args: ["--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"],
},
},
},
{
name: "firefox",
use: {

View File

@@ -10,7 +10,8 @@
"dependencies": {
"body-parser": "^2.2.2",
"express": "^5.2.1",
"http-proxy-middleware": "^3.0.3",
"follow-redirects": "^1.15.6",
"http-proxy-middleware": "^3.0.5",
"node": "^20.19.5",
"node-fetch": "^2.6.1",
"path-to-regexp": "^0.1.12"

View File

@@ -13,7 +13,8 @@
"dependencies": {
"body-parser": "^2.2.2",
"express": "^5.2.1",
"http-proxy-middleware": "^3.0.3",
"follow-redirects": "^1.15.6",
"http-proxy-middleware": "^3.0.5",
"node": "^20.19.5",
"node-fetch": "^2.6.1",
"path-to-regexp": "^0.1.12"

11
src/@types/i18next.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
import "i18next";
import Resources from "../Localization/en/Resources.json";
declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "Resources";
resources: {
Resources: typeof Resources;
};
}
}

View File

@@ -1,5 +1,7 @@
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { SubscriptionType } from "../Contracts/SubscriptionType";
import { isExpectedError } from "../Metrics/ErrorClassification";
import { scenarioMonitor } from "../Metrics/ScenarioMonitor";
import { userContext } from "../UserContext";
import { ARMError } from "../Utils/arm/request";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
@@ -31,6 +33,12 @@ export const handleError = (
// checks for errors caused by firewall and sends them to portal to handle
sendNotificationForError(errorMessage, errorCode);
// Mark expected failures for health metrics (auth, firewall, permissions, etc.)
// This ensures timeouts with expected failures emit healthy instead of unhealthy
if (isExpectedError(error)) {
scenarioMonitor.markExpectedFailure();
}
};
export const getErrorMessage = (error: string | Error = ""): string => {

View File

@@ -6,7 +6,7 @@ import { MinimalQueryIterator, nextPage } from "../IteratorUtilities";
// Redact sensitive information from BadRequest errors with specific codes
export const redactSyntaxErrorMessage = (error: unknown): unknown => {
const codesToRedact = ["SC1001", "SC2001"];
const codesToRedact = ["SC1001", "SC2001", "SC1010"];
try {
// Handle error objects with a message property

View File

@@ -77,6 +77,12 @@ let configContext: Readonly<ConfigContext> = {
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
`^https:\\/\\/.*\\.powerbi\\.com$`,
`^https:\\/\\/dataexplorer-preview\\.azurewebsites\\.net$`,
`^https:\\/\\/explorer\\.cosmos\\.sovcloud-api\\.fr$`,
`^https:\\/\\/portal\\.sovcloud-azure\\.fr$`,
`^https:\\/\\/explorer\\.cosmos\\.sovcloud-api\\.de$`,
`^https:\\/\\/portal\\.sovcloud-azure\\.de$`,
`^https:\\/\\/explorer\\.cosmos\\.sovcloud-api\\.sg$`,
`^https:\\/\\/portal\\.sovcloud-azure\\.sg$`,
], // Webpack injects this at build time
gitSha: process.env.GIT_SHA,
hostedExplorerURL: "https://cosmos.azure.com/",

View File

@@ -395,6 +395,14 @@ describe("CopyJobUtils", () => {
expect(result).toBe(false);
});
it("should return false for different completion percentage", () => {
const jobs1 = [createMockJob("job1", "Running")];
const jobs2 = [{ ...createMockJob("job1", "Running"), CompletionPercentage: 75 }];
const result = CopyJobUtils.isEqual(jobs1, jobs2);
expect(result).toBe(false);
});
it("should return true for empty arrays", () => {
const result = CopyJobUtils.isEqual([], []);
expect(result).toBe(true);

View File

@@ -142,7 +142,7 @@ export function isEqual(prevJobs: CopyJobType[], newJobs: CopyJobType[]): boolea
if (!newJob) {
return false;
}
return prevJob.Status === newJob.Status;
return prevJob.Status === newJob.Status && prevJob.CompletionPercentage === newJob.CompletionPercentage;
});
}

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

@@ -113,7 +113,7 @@ export class ContainerSampleGenerator {
? await createMongoDocument(collection.databaseId, collection, shardKey, doc)
: await createDocument(collection, doc);
} catch (error) {
NotificationConsoleUtils.logConsoleError(error);
NotificationConsoleUtils.logConsoleError(error instanceof Error ? error.message : String(error));
}
}),
);

View File

@@ -329,7 +329,10 @@ export class NotificationConsoleComponent extends React.Component<
}
private static extractHeaderStatus(consoleData: ConsoleData) {
return consoleData?.message.split(":\n")[0];
if (!consoleData?.message || typeof consoleData.message !== "string") {
return undefined;
}
return consoleData.message.split(":\n")[0];
}
private onConsoleWasExpanded = (): void => {

View File

@@ -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<AddCollectionPanelProps,
messageType="info"
showErrorDetails={false}
link={Constants.Urls.freeTierInformation}
linkText="Learn more"
linkText={t(Keys.common.learnMore)}
/>
)}
@@ -274,17 +276,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`A database is analogous to a namespace. It is the unit of management for a set of ${getCollectionName(
true,
).toLocaleLowerCase()}.`}
content={t(Keys.panes.addCollection.databaseTooltip, {
collectionName: getCollectionName(true).toLocaleLowerCase(),
})}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`A database is analogous to a namespace. It is the unit of management for a set of ${getCollectionName(
true,
).toLocaleLowerCase()}.`}
ariaLabel={t(Keys.panes.addCollection.databaseTooltip, {
collectionName: getCollectionName(true).toLocaleLowerCase(),
})}
/>
</TooltipHost>
</Stack>
@@ -304,7 +306,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
tabIndex={0}
onChange={this.onCreateNewDatabaseRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Create new</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.createNew)}</span>
<input
className="panelRadioBtn"
@@ -317,7 +319,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
tabIndex={0}
onChange={this.onUseExistingDatabaseRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Use existing</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.useExisting)}</span>
</div>
</Stack>
)}
@@ -347,7 +349,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{!isServerlessAccount() && (
<Stack horizontal>
<Checkbox
label={`Share throughput across ${getCollectionName(true).toLocaleLowerCase()}`}
label={t(Keys.panes.addCollection.shareThroughput, {
collectionName: getCollectionName(true).toLocaleLowerCase(),
})}
checked={this.state.isSharedThroughputChecked}
styles={{
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
@@ -365,17 +369,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
/>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`Throughput configured at the database level will be shared across all ${getCollectionName(
true,
).toLocaleLowerCase()} within the database.`}
content={t(Keys.panes.addCollection.shareThroughputTooltip, {
collectionName: getCollectionName(true).toLocaleLowerCase(),
})}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`Throughput configured at the database level will be shared across all ${getCollectionName(
true,
).toLocaleLowerCase()} within the database.`}
ariaLabel={t(Keys.panes.addCollection.shareThroughputTooltip, {
collectionName: getCollectionName(true).toLocaleLowerCase(),
})}
/>
</TooltipHost>
</Stack>
@@ -424,14 +428,18 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
content={t(Keys.panes.addCollection.collectionIdTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
>
<Icon
role="button"
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
ariaLabel={t(Keys.panes.addCollection.collectionIdTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
/>
</TooltipHost>
</Stack>
@@ -445,10 +453,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
autoComplete="off"
pattern={ValidCosmosDbIdInputPattern.source}
title={ValidCosmosDbIdDescription}
placeholder={`e.g., ${getCollectionName()}1`}
placeholder={t(Keys.panes.addCollection.collectionIdPlaceholder, { collectionName: getCollectionName() })}
size={40}
className="panelTextField"
aria-label={`${getCollectionName()} id, Example ${getCollectionName()}1`}
aria-label={t(Keys.panes.addCollection.collectionIdAriaLabel, { collectionName: getCollectionName() })}
value={this.state.collectionId}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
this.setState({ collectionId: event.target.value })
@@ -462,7 +470,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<Stack horizontal style={{ marginTop: -4, marginBottom: -5 }}>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Indexing
{t(Keys.panes.addCollection.indexing)}
</Text>
</Stack>
@@ -470,32 +478,32 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<input
className="panelRadioBtn"
checked={this.state.enableIndexing}
aria-label="Turn on indexing"
aria-label={t(Keys.panes.addCollection.turnOnIndexing)}
aria-checked={this.state.enableIndexing}
type="radio"
role="radio"
tabIndex={0}
onChange={this.onTurnOnIndexing.bind(this)}
/>
<span className="panelRadioBtnLabel">Automatic</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.automatic)}</span>
<input
className="panelRadioBtn"
checked={!this.state.enableIndexing}
aria-label="Turn off indexing"
aria-label={t(Keys.panes.addCollection.turnOffIndexing)}
aria-checked={!this.state.enableIndexing}
type="radio"
role="radio"
tabIndex={0}
onChange={this.onTurnOffIndexing.bind(this)}
/>
<span className="panelRadioBtnLabel">Off</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.off)}</span>
</Stack>
<Text variant="small">
{this.getFreeTierIndexingText()}{" "}
<Link target="_blank" href="https://aka.ms/cosmos-indexing-policy">
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
</Stack>
@@ -508,21 +516,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Sharding
{t(Keys.panes.addCollection.sharding)}
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
}
content={t(Keys.panes.addCollection.shardingTooltip)}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
}
ariaLabel={t(Keys.panes.addCollection.shardingTooltip)}
/>
</TooltipHost>
</Stack>
@@ -531,7 +535,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<input
className="panelRadioBtn"
checked={!this.state.isSharded}
aria-label="Unsharded"
aria-label={t(Keys.panes.addCollection.unsharded)}
aria-checked={!this.state.isSharded}
name="unsharded"
type="radio"
@@ -540,12 +544,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
tabIndex={0}
onChange={this.onUnshardedRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Unsharded (20GB limit)</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.unshardedLabel)}</span>
<input
className="panelRadioBtn"
checked={this.state.isSharded}
aria-label="Sharded"
aria-label={t(Keys.panes.addCollection.sharded)}
aria-checked={this.state.isSharded}
name="sharded"
type="radio"
@@ -554,7 +558,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
tabIndex={0}
onChange={this.onShardedRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Sharded</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.sharded)}</span>
</Stack>
</Stack>
)}
@@ -679,15 +683,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
disabled={this.state.subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })}
>
Add hierarchical partition key
{t(Keys.panes.addCollection.addPartitionKey)}
</DefaultButton>
{this.state.subPartitionKeys.length > 0 && (
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={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.{" "}
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} />{" "}
{t(Keys.panes.addCollection.hierarchicalPartitionKeyInfo)}{" "}
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
)}
@@ -700,7 +703,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{!isServerlessAccount() && !this.state.createNewDatabase && this.isSelectedDatabaseSharedThroughput() && (
<Stack horizontal verticalAlign="center">
<Checkbox
label={`Provision dedicated throughput for this ${getCollectionName().toLocaleLowerCase()}`}
label={t(Keys.panes.addCollection.provisionDedicatedThroughput, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
checked={this.state.enableDedicatedThroughput}
styles={{
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
@@ -718,23 +723,19 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
/>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`You can optionally provision dedicated throughput for a ${getCollectionName().toLocaleLowerCase()} within a database that has throughput
provisioned. This dedicated throughput amount will not be shared with other ${getCollectionName(
true,
).toLocaleLowerCase()} in the database and
does not count towards the throughput you provisioned for the database. This throughput amount will be
billed in addition to the throughput amount you provisioned at the database level.`}
content={t(Keys.panes.addCollection.provisionDedicatedThroughputTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
collectionNamePlural: getCollectionName(true).toLocaleLowerCase(),
})}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`You can optionally provision dedicated throughput for a ${getCollectionName().toLocaleLowerCase()} within a database that has throughput
provisioned. This dedicated throughput amount will not be shared with other ${getCollectionName(
true,
).toLocaleLowerCase()} in the database and
does not count towards the throughput you provisioned for the database. This throughput amount will be
billed in addition to the throughput amount you provisioned at the database level.`}
ariaLabel={t(Keys.panes.addCollection.provisionDedicatedThroughputTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
collectionNamePlural: getCollectionName(true).toLocaleLowerCase(),
})}
/>
</TooltipHost>
</Stack>
@@ -769,8 +770,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
autoComplete="off"
placeholder={
userContext.apiType === "Mongo"
? "Comma separated paths e.g. firstName,address.zipCode"
: "Comma separated paths e.g. /firstName,/address/zipCode"
? t(Keys.panes.addCollection.uniqueKeysPlaceholderMongo)
: t(Keys.panes.addCollection.uniqueKeysPlaceholderSql)
}
className="panelTextField"
value={uniqueKey}
@@ -802,7 +803,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
styles={{ root: { padding: 0 }, label: { fontSize: 12, color: "var(--colorNeutralForeground1)" } }}
onClick={() => this.setState({ uniqueKeys: [...this.state.uniqueKeys, ""] })}
>
Add unique key
{t(Keys.panes.addCollection.addUniqueKey)}
</ActionButton>
</Stack>
)}
@@ -823,7 +824,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
className="panelRadioBtn"
checked={this.state.enableAnalyticalStore}
disabled={!isSynapseLinkEnabled()}
aria-label="Enable analytical store"
aria-label={t(Keys.panes.addCollection.enableAnalyticalStore)}
aria-checked={this.state.enableAnalyticalStore}
name="analyticalStore"
type="radio"
@@ -832,13 +833,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
tabIndex={0}
onChange={this.onEnableAnalyticalStoreRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">On</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.on)}</span>
<input
className="panelRadioBtn"
checked={!this.state.enableAnalyticalStore}
disabled={!isSynapseLinkEnabled()}
aria-label="Disable analytical store"
aria-label={t(Keys.panes.addCollection.disableAnalyticalStore)}
aria-checked={!this.state.enableAnalyticalStore}
name="analyticalStore"
type="radio"
@@ -847,26 +848,28 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
tabIndex={0}
onChange={this.onDisableAnalyticalStoreRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Off</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.off)}</span>
</div>
</Stack>
{!isSynapseLinkEnabled() && (
<Stack className="panelGroupSpacing">
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
Azure Synapse Link is required for creating an analytical store{" "}
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account. <br />
{t(Keys.panes.addCollection.analyticalStoreSynapseLinkRequired, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}{" "}
<br />
<Link
href="https://aka.ms/cosmosdb-synapselink"
target="_blank"
aria-label={Constants.ariaLabelForLearnMoreLink.AzureSynapseLink}
className="capacitycalculator-link"
>
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
<DefaultButton
text="Enable"
text={t(Keys.panes.addCollection.enable)}
onClick={() => this.props.explorer.openEnableSynapseLinkDialog()}
style={{ height: 27, width: 80 }}
styles={{ label: { fontSize: 12 } }}
@@ -878,7 +881,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{this.shouldShowVectorSearchParameters() && (
<Stack>
<CollapsibleSectionComponent
title="Container Vector Policy"
title={t(Keys.panes.addCollection.containerVectorPolicy)}
isExpandedByDefault={false}
onExpand={() => {
scrollToSection("collapsibleVectorPolicySectionContent");
@@ -906,7 +909,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{this.shouldShowFullTextSearchParameters() && (
<Stack>
<CollapsibleSectionComponent
title="Container Full Text Search Policy"
title={t(Keys.panes.addCollection.containerFullTextSearchPolicy)}
isExpandedByDefault={false}
onExpand={() => {
scrollToSection("collapsibleFullTextPolicySectionContent");
@@ -935,7 +938,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
)}
{!isFabricNative() && userContext.apiType !== "Tables" && (
<CollapsibleSectionComponent
title="Advanced"
title={t(Keys.panes.addCollection.advanced)}
isExpandedByDefault={false}
onExpand={() => {
TelemetryProcessor.traceOpen(Action.ExpandAddCollectionPaneAdvancedSection);
@@ -948,23 +951,23 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Indexing
{t(Keys.panes.addCollection.indexing)}
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
content={t(Keys.panes.addCollection.mongoIndexingTooltip)}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
ariaLabel={t(Keys.panes.addCollection.mongoIndexingTooltip)}
/>
</TooltipHost>
</Stack>
<Checkbox
label="Create a Wildcard Index on all fields"
label={t(Keys.panes.addCollection.createWildcardIndex)}
checked={this.state.createMongoWildCardIndex}
styles={{
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
@@ -986,7 +989,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{userContext.apiType === "SQL" && (
<Stack className="panelGroupSpacing">
<Checkbox
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
label={t(Keys.panes.addCollection.legacySdkCheckbox)}
checked={this.state.useHashV1}
styles={{
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
@@ -1003,11 +1006,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}
/>
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
<Icon iconName="InfoSolid" className="removeIcon" /> 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.{" "}
<Icon iconName="InfoSolid" className="removeIcon" /> {t(Keys.panes.addCollection.legacySdkInfo)}{" "}
<Link href="https://aka.ms/cosmos-large-pk" target="_blank">
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
</Stack>
@@ -1018,7 +1019,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</div>
{!this.props.isCopyJobFlow && (
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={this.state.isThroughputCapExceeded} />
<PanelFooterComponent buttonLabel={t(Keys.common.ok)} isButtonDisabled={this.state.isThroughputCapExceeded} />
)}
{this.state.isExecuting && (
@@ -1044,7 +1045,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
progressTrack: { backgroundColor: "#A6A6A6" },
progressBar: { background: "white" },
}}
label="Adding sample data set"
label={t(Keys.panes.addCollection.addingSampleDataSet)}
/>
</TeachingBubble>
)}
@@ -1150,8 +1151,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
private getFreeTierIndexingText(): string {
return this.state.enableIndexing
? "All properties in your documents will be indexed by default for flexible and efficient queries."
: "Indexing will be turned off. Recommended if you don't need to run queries or only have key value operations.";
? t(Keys.panes.addCollection.indexingOnInfo)
: t(Keys.panes.addCollection.indexingOffInfo);
}
private getPartitionKeySubtext(): string {
@@ -1249,14 +1250,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
const throughput = this.state.createNewDatabase ? this.newDatabaseThroughput : this.collectionThroughput;
if (throughput > 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<AddCollectionPanelProps,
if (this.shouldShowVectorSearchParameters()) {
if (!this.state.vectorPolicyValidated) {
this.setState({ errorMessage: "Please fix errors in container vector policy" });
this.setState({ errorMessage: t(Keys.panes.addCollection.vectorPolicyError) });
return false;
}
if (!this.state.fullTextPolicyValidated) {
this.setState({ errorMessage: "Please fix errors in container full text search polilcy" });
this.setState({ errorMessage: t(Keys.panes.addCollection.fullTextSearchPolicyError) });
return false;
}
}

View File

@@ -3,28 +3,33 @@ import * as Constants from "Common/Constants";
import { configContext, Platform } from "ConfigContext";
import * as DataModels from "Contracts/DataModels";
import { getFullTextLanguageOptions } from "Explorer/Controls/FullTextSeach/FullTextPoliciesComponent";
import { Keys } from "Localization/Keys.generated";
import { t } from "Localization/t";
import { isFabricNative } from "Platform/Fabric/FabricUtil";
import React from "react";
import { userContext } from "UserContext";
export function getPartitionKeyTooltipText(): string {
if (userContext.apiType === "Mongo") {
return "The shard key (field) is used to split your data across many replica sets (shards) to achieve unlimited scalability. Its critical to choose a field that will evenly distribute your data.";
return t(Keys.panes.addCollectionUtility.shardKeyTooltip);
}
let tooltipText = `The ${getPartitionKeyName(
true,
)} is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume.`;
let tooltipText = t(Keys.panes.addCollectionUtility.partitionKeyTooltip, {
partitionKeyName: getPartitionKeyName(true),
});
if (userContext.apiType === "SQL") {
tooltipText += " For small read-heavy workloads or write-heavy workloads of any size, id is often a good choice.";
tooltipText += t(Keys.panes.addCollectionUtility.partitionKeyTooltipSqlSuffix);
}
return tooltipText;
}
export function getPartitionKeyName(isLowerCase?: boolean): string {
const partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
const partitionKeyName =
userContext.apiType === "Mongo"
? t(Keys.panes.addCollectionUtility.shardKeyLabel)
: t(Keys.panes.addCollectionUtility.partitionKeyLabel);
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
}
@@ -32,19 +37,19 @@ export function getPartitionKeyName(isLowerCase?: boolean): string {
export function getPartitionKeyPlaceHolder(index?: number): string {
switch (userContext.apiType) {
case "Mongo":
return "e.g., categoryId";
return t(Keys.panes.addCollectionUtility.shardKeyPlaceholder);
case "Gremlin":
return "e.g., /address";
return t(Keys.panes.addCollectionUtility.partitionKeyPlaceholderDefault);
case "SQL":
return `${
index === undefined
? "Required - first partition key e.g., /TenantId"
? t(Keys.panes.addCollectionUtility.partitionKeyPlaceholderFirst)
: index === 0
? "second partition key e.g., /UserId"
: "third partition key e.g., /SessionId"
? t(Keys.panes.addCollectionUtility.partitionKeyPlaceholderSecond)
: t(Keys.panes.addCollectionUtility.partitionKeyPlaceholderThird)
}`;
default:
return "e.g., /address/zipCode";
return t(Keys.panes.addCollectionUtility.partitionKeyPlaceholderGraph);
}
}
@@ -69,13 +74,12 @@ export function isFreeTierAccount(): boolean {
}
export function UniqueKeysHeader(): JSX.Element {
const tooltipContent =
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key.";
const tooltipContent = t(Keys.panes.addCollectionUtility.uniqueKeysTooltip);
return (
<Stack horizontal style={{ marginBottom: -2 }}>
<Text className="panelTextBold" variant="small">
Unique keys
{t(Keys.panes.addCollectionUtility.uniqueKeysLabel)}
</Text>
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={tooltipContent}>
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} ariaLabel={tooltipContent} />
@@ -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 (
<Stack horizontal style={{ marginBottom: -2 }}>
<Text className="panelTextBold" variant="small">
Analytical Store
{t(Keys.panes.addCollectionUtility.analyticalStoreLabel)}
</Text>
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={tooltipContent}>
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} ariaLabel={tooltipContent} />
@@ -116,14 +119,13 @@ export function AnalyticalStoreHeader(): JSX.Element {
export function AnalyticalStorageContent(): JSX.Element {
return (
<Text variant="small">
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)}{" "}
<Link
aria-label={Constants.ariaLabelForLearnMoreLink.AnalyticalStore}
target="_blank"
href="https://aka.ms/analytical-store-overview"
>
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
);
@@ -155,10 +157,9 @@ export function scrollToSection(id: string): void {
export function ContainerVectorPolicyTooltipContent(): JSX.Element {
return (
<Text variant="small">
Describe any properties in your data that contain vectors, so that they can be made available for similarity
queries.{" "}
{t(Keys.panes.addCollectionUtility.vectorPolicyTooltip)}{" "}
<Link target="_blank" href="https://aka.ms/CosmosDBVectorSetup">
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
);

View File

@@ -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.
<br />
<StyledLinkBase
aria-label="Learn more about Azure Synapse Link."
@@ -608,7 +606,8 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
className="removeIcon"
iconName="InfoSolid"
/>
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.
<StyledLinkBase
href="https://aka.ms/cosmos-large-pk"

View File

@@ -1,5 +1,7 @@
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
import { Keys } from "Localization/Keys.generated";
import { t } from "Localization/t";
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
import React, { FunctionComponent, useEffect, useState } from "react";
import * as Constants from "../../../Common/Constants";
@@ -40,15 +42,18 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
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<string>("");
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<boolean>(
getNewDatabaseSharedThroughputDefault(),
);
@@ -144,15 +149,15 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
// 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<AddDatabasePaneProps> = ({
const props: RightPaneFormProps = {
formError: formErrors,
isExecuting,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
isSubmitButtonDisabled: isThroughputCapExceeded,
onSubmit,
};
@@ -187,7 +192,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
messageType="info"
showErrorDetails={false}
link={Constants.Urls.freeTierInformation}
linkText="Learn more"
linkText={t(Keys.common.learnMore)}
/>
)}
<div className="panelMainContent">

View File

@@ -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
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Global secondary index container id
{t(Keys.panes.addGlobalSecondaryIndex.globalSecondaryIndexId)}
</Text>
</Stack>
<input
@@ -318,7 +320,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
autoComplete="off"
pattern={ValidCosmosDbIdInputPattern.source}
title={ValidCosmosDbIdDescription}
placeholder={`e.g., indexbyEmailId`}
placeholder={t(Keys.panes.addGlobalSecondaryIndex.globalSecondaryIndexIdPlaceholder)}
size={40}
className="panelTextField"
value={globalSecondaryIndexId}
@@ -336,7 +338,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/materialized-views#defining-materialized-views"
target="blank"
>
Learn more about defining global secondary indexes.
{t(Keys.panes.addGlobalSecondaryIndex.projectionQueryTooltip)}
</Link>
}
>
@@ -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
<AdvancedComponent {...{ useHashV1, setUseHashV1, setSubPartitionKeys }} />
</Stack>
</div>
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={isThroughputCapExceeded} />
<PanelFooterComponent buttonLabel={t(Keys.common.ok)} isButtonDisabled={isThroughputCapExceeded} />
{isExecuting && <PanelLoadingScreen />}
</form>
);

View File

@@ -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<CassandraAddCollectio
if (throughput > 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<CassandraAddCollectio
const props: RightPaneFormProps = {
formError,
isExecuting,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
isSubmitButtonDisabled: isThroughputCapExceeded,
onSubmit,
};
@@ -161,7 +163,8 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Keyspace name <InfoTooltip>Select an existing keyspace or enter a new keyspace id.</InfoTooltip>
{t(Keys.panes.cassandraAddCollection.keyspaceLabel)}{" "}
<InfoTooltip>{t(Keys.panes.cassandraAddCollection.keyspaceTooltip)}</InfoTooltip>
</Text>
</Stack>
@@ -179,7 +182,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
setExistingKeyspaceId("");
}}
/>
<span className="panelRadioBtnLabel">Create new</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.createNew)}</span>
<input
className="panelRadioBtn"
@@ -193,7 +196,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
setIsKeyspaceShared(false);
}}
/>
<span className="panelRadioBtnLabel">Use existing</span>
<span className="panelRadioBtnLabel">{t(Keys.panes.addCollection.useExisting)}</span>
</Stack>
{keyspaceCreateNew && (
@@ -275,9 +278,9 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Enter CQL command to create the table.{" "}
{t(Keys.panes.cassandraAddCollection.tableIdLabel)}{" "}
<Link className="underlinedLink" href="https://aka.ms/cassandra-create-table" target="_blank">
Learn More
{t(Keys.common.learnMore)}
</Link>
</Text>
</Stack>
@@ -295,7 +298,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
autoComplete="off"
pattern={ValidCosmosDbIdInputPattern.source}
title={ValidCosmosDbIdDescription}
placeholder="Enter table Id"
placeholder={t(Keys.panes.cassandraAddCollection.enterTableId)}
size={20}
value={tableId}
onChange={(e, newValue) => setTableId(newValue)}
@@ -307,7 +310,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
multiline
id="editor-area"
rows={5}
ariaLabel="Table schema"
ariaLabel={t(Keys.panes.cassandraAddCollection.tableSchemaAriaLabel)}
value={userTableQuery}
onChange={(e, newValue) => setUserTableQuery(newValue)}
/>
@@ -318,17 +321,12 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
<input
type="checkbox"
id="tableSharedThroughput"
title="Provision dedicated throughput for this table"
title={t(Keys.panes.cassandraAddCollection.provisionDedicatedThroughput)}
checked={dedicateTableThroughput}
onChange={(e) => setDedicateTableThroughput(e.target.checked)}
/>
<span>Provision dedicated throughput for this table</span>
<InfoTooltip>
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.
</InfoTooltip>
<span>{t(Keys.panes.cassandraAddCollection.provisionDedicatedThroughput)}</span>
<InfoTooltip>{t(Keys.panes.cassandraAddCollection.provisionDedicatedThroughputTooltip)}</InfoTooltip>
</Stack>
)}
{!isServerlessAccount() && (!isKeyspaceShared || dedicateTableThroughput) && (

View File

@@ -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<ChangePartitionKeyPaneProps> = ({
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<ChangePartitionKeyPaneProps> = ({
};
return (
<RightPaneForm formError={formError} isExecuting={isExecuting} onSubmit={submit} submitButtonText="OK">
<RightPaneForm
formError={formError}
isExecuting={isExecuting}
onSubmit={submit}
submitButtonText={t(Keys.common.ok)}
>
<Stack tokens={{ childrenGap: 10 }} className="panelMainContent">
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
When changing a containers partition key, you will need to create a destination container with the correct
partition key. You may also select an existing destination container.&nbsp;
{t(Keys.panes.changePartitionKey.description)}&nbsp;
<Link
href="https://learn.microsoft.com/en-us/azure/cosmos-db/container-copy#container-copy-within-an-azure-cosmos-db-account"
target="_blank"
underline
>
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
<Stack>
@@ -218,14 +224,18 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
content={t(Keys.panes.changePartitionKey.collectionIdTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
>
<Icon
role="button"
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
ariaLabel={t(Keys.panes.changePartitionKey.collectionIdTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
/>
</TooltipHost>
</Stack>
@@ -239,10 +249,14 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
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<HTMLInputElement>) => setTargetCollectionId(event.target.value)}
/>
@@ -349,7 +363,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
disabled={subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
onClick={() => setSubPartitionKeys([...subPartitionKeys, ""])}
>
Add hierarchical partition key
{t(Keys.panes.addCollection.addPartitionKey)}
</DefaultButton>
{subPartitionKeys.length > 0 && (
<Text
@@ -357,11 +371,10 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
variant="small"
style={{ color: "var(--colorNeutralForeground1)" }}
>
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={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.{" "}
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} />{" "}
{t(Keys.panes.addCollection.hierarchicalPartitionKeyInfo)}{" "}
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
Learn more
{t(Keys.common.learnMore)}
</Link>
</Text>
)}
@@ -377,14 +390,18 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
content={t(Keys.panes.changePartitionKey.collectionIdTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
>
<Icon
role="button"
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
ariaLabel={t(Keys.panes.changePartitionKey.collectionIdTooltip, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
/>
</TooltipHost>
</Stack>
@@ -400,7 +417,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
}}
defaultSelectedKey={targetCollectionId}
responsiveMode={999}
ariaLabel="Existing Containers"
ariaLabel={t(Keys.panes.changePartitionKey.existingContainers)}
/>
</Stack>
)}

View File

@@ -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<CopyNotebookPanelProps> = ({
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<CopyNotebookPanelProps> = ({
const props: RightPaneFormProps = {
formError,
isExecuting: isExecuting,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
onSubmit: () => submit(),
};

View File

@@ -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<CopyNotebookPaneProps>
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<CopyNotebookPaneProps>
<div className="paneMainContent">
<Stack tokens={{ childrenGap: 10 }}>
<Stack.Item>
<Label htmlFor="notebookName">Name</Label>
<Label htmlFor="notebookName">{t(Keys.panes.copyNotebook.name)}</Label>
<Text id="notebookName">{name}</Text>
</Stack.Item>

View File

@@ -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<DeleteCollectio
useDatabases.getState().isLastCollection() && !useDatabases.getState().findSelectedDatabase()?.isDatabaseShared();
const collectionName = getCollectionName().toLocaleLowerCase();
const paneTitle = "Delete " + collectionName;
const paneTitle = t(Keys.panes.deleteCollection.panelTitle, { collectionName });
const onSubmit = async (): Promise<void> => {
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<DeleteCollectio
const props: RightPaneFormProps = {
formError: formError,
isExecuting,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
onSubmit,
};
const confirmContainer = `Confirm by typing the ${collectionName.toLowerCase()} id`;
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${collectionName}?`;
const confirmContainer = t(Keys.panes.deleteCollection.confirmPrompt, {
collectionName: collectionName.toLowerCase(),
});
const reasonInfo =
t(Keys.panes.deleteCollection.feedbackTitle) +
" " +
t(Keys.panes.deleteCollection.feedbackReason, { collectionName });
return (
<RightPaneForm {...props}>
<div className="panelFormWrapper">
<div className="panelMainContent">
<div className="confirmDeleteInput">
<span className="mandatoryStar">* </span>
<Text variant="small">Confirm by typing the {collectionName.toLowerCase()} id</Text>
<Text variant="small">{confirmContainer}</Text>
<TextField
id="confirmCollectionId"
autoFocus
@@ -133,10 +143,10 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
{shouldRecordFeedback() && (
<div className="deleteCollectionFeedback">
<Text variant="small" block>
Help us improve Azure Cosmos DB!
{t(Keys.panes.deleteCollection.feedbackTitle)}
</Text>
<Text variant="small" block>
What is the reason why you are deleting this {collectionName}?
{t(Keys.panes.deleteCollection.feedbackReason, { collectionName })}
</Text>
<TextField
id="deleteCollectionFeedbackInput"

View File

@@ -34,9 +34,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
<span
className="css-109"
>
Confirm by typing the
container
id
Confirm by typing the container id
</span>
</Text>
<StyledTextFieldBase

View File

@@ -5,6 +5,8 @@ import DeleteFeedback from "Common/DeleteFeedback";
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
import { deleteDatabase } from "Common/dataAccess/deleteDatabase";
import { Collection, Database } 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";
@@ -38,11 +40,19 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
const submit = async (): Promise<void> => {
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<DeleteDatabaseCo
const props: RightPaneFormProps = {
formError,
isExecuting: isLoading,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
onSubmit: () => 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 (
<RightPaneForm {...props}>
{!formError && <PanelInfoErrorComponent {...errorProps} />}
@@ -148,10 +160,10 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
{isLastNonEmptyDatabase() && (
<div className="deleteDatabaseFeedback">
<Text variant="small" block>
Help us improve Azure Cosmos DB!
{t(Keys.panes.deleteDatabase.feedbackTitle)}
</Text>
<Text variant="small" block>
What is the reason why you are deleting this {getDatabaseName()}?
{t(Keys.panes.deleteDatabase.feedbackReason, { databaseName: getDatabaseName() })}
</Text>
<TextField
id="deleteDatabaseFeedbackInput"

View File

@@ -3,6 +3,8 @@ import { useBoolean } from "@fluentui/react-hooks";
import React, { FunctionComponent, useRef, useState } from "react";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { Keys } from "../../../Localization/Keys.generated";
import { t } from "../../../Localization/t";
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
import StoredProcedure from "../../Tree/StoredProcedure";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
@@ -45,8 +47,8 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
};
const setInvalidParamError = (invalidParam: string): void => {
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<ExecuteSprocParamsPanePro
const props: RightPaneFormProps = {
formError: formError,
isExecuting: isLoading,
submitButtonText: "Execute",
submitButtonText: t(Keys.common.execute),
onSubmit: () => submit(),
};
@@ -107,9 +109,9 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
inputParameters.push(
<InputParameter
key={paramKeyValue.text + i}
dropdownLabel={i === 0 ? "Key" : ""}
inputParameterTitle={i === 0 ? "Enter input parameters (if any)" : ""}
inputLabel={i === 0 ? "Param" : ""}
dropdownLabel={i === 0 ? t(Keys.panes.executeStoredProcedure.key) : ""}
inputParameterTitle={i === 0 ? t(Keys.panes.executeStoredProcedure.enterInputParameters) : ""}
inputLabel={i === 0 ? t(Keys.panes.executeStoredProcedure.param) : ""}
isAddRemoveVisible={true}
onDeleteParamKeyPress={() => deleteParamAtIndex(i)}
onAddNewParamKeyPress={() => addNewParamAtIndex(i + 1)}
@@ -130,9 +132,9 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
<RightPaneForm {...props}>
<div className="panelMainContent">
<InputParameter
dropdownLabel="Key"
inputParameterTitle="Partition key value"
inputLabel="Value"
dropdownLabel={t(Keys.panes.executeStoredProcedure.key)}
inputParameterTitle={t(Keys.panes.executeStoredProcedure.partitionKeyValue)}
inputLabel={t(Keys.panes.executeStoredProcedure.value)}
isAddRemoveVisible={false}
onParamValueChange={(_event, newInput?: string) => (partitionValueRef.current = newInput)}
onParamKeyChange={(_event: React.FormEvent<HTMLDivElement>, item: IDropdownOption) =>
@@ -143,8 +145,8 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
/>
{getInputParameterComponent()}
<Stack horizontal onClick={() => addNewParamAtLastIndex()} tabIndex={0}>
<Image {...imageProps} src={AddPropertyIcon} alt="Add param" />
<Text className="addNewParamStyle">Add New Param</Text>
<Image {...imageProps} src={AddPropertyIcon} alt={t(Keys.panes.executeStoredProcedure.addParam)} />
<Text className="addNewParamStyle">{t(Keys.panes.executeStoredProcedure.addNewParam)}</Text>
</Stack>
</div>
</RightPaneForm>

View File

@@ -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<IDropdownStyles> = { dropdown: { width: 100 } };
const options = [
@@ -74,7 +76,7 @@ export const InputParameter: FunctionComponent<InputParameterProps> = ({
<Image
{...imageProps}
src={EntityCancelIcon}
alt="Delete param"
alt={t(Keys.panes.executeStoredProcedure.deleteParam)}
id="deleteparam"
role="button"
onClick={onDeleteParamKeyPress}
@@ -84,7 +86,7 @@ export const InputParameter: FunctionComponent<InputParameterProps> = ({
<Image
{...imageProps}
src={AddPropertyIcon}
alt="Add param"
alt={t(Keys.panes.executeStoredProcedure.addParam)}
id="addparam"
role="button"
onClick={onAddNewParamKeyPress}

View File

@@ -1,5 +1,7 @@
import React, { FunctionComponent } from "react";
import * as ViewModels from "../../../Contracts/ViewModels";
import { Keys } from "../../../Localization/Keys.generated";
import { t } from "../../../Localization/t";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { GraphStyleComponent } from "../../Graph/GraphStyleComponent/GraphStyleComponent";
import { IGraphConfig } from "../../Tabs/GraphTab";
@@ -17,7 +19,7 @@ export const GraphStylingPanel: FunctionComponent<GraphStylingProps> = ({
}: GraphStylingProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const buttonLabel = "Ok";
const buttonLabel = t(Keys.common.ok);
const submit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

View File

@@ -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<void> => {
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 => {
<Stack horizontal>
<TextField
id="confirmCollectionId"
label="Select a query document"
label={t(Keys.panes.loadQuery.selectFilesToOpen)}
value={selectedFileName}
autoFocus
readOnly

View File

@@ -1,6 +1,8 @@
import { useBoolean } from "@fluentui/react-hooks";
import React, { FunctionComponent, useState } from "react";
import * as ViewModels from "../../../Contracts/ViewModels";
import { Keys } from "../../../Localization/Keys.generated";
import { t } from "../../../Localization/t";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { NewVertexComponent } from "../../Graph/NewVertexComponent/NewVertexComponent";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
@@ -41,7 +43,7 @@ export const NewVertexPanel: FunctionComponent<INewVertexPanelProps> = ({
const props: RightPaneFormProps = {
formError: errorMessage,
isExecuting: isLoading,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
onSubmit: () => submit(),
};

View File

@@ -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<PanelInfoErrorProp
}: PanelInfoErrorProps): JSX.Element => {
const expandConsole = useNotificationConsole((state) => state.expandConsole);
let icon: JSX.Element = <Icon iconName="InfoSolid" className="panelLargeInfoIcon" aria-label="Infomation" />;
let icon: JSX.Element = (
<Icon iconName="InfoSolid" className="panelLargeInfoIcon" aria-label={t(Keys.panes.panelInfo.information)} />
);
if (messageType === "error") {
icon = <Icon iconName="StatusErrorFull" className="panelErrorIcon" aria-label="error" />;
} else if (messageType === "warning") {
icon = <Icon iconName="WarningSolid" className="panelWarningIcon" aria-label="warning" />;
} else if (messageType === "info") {
icon = <Icon iconName="InfoSolid" className="panelLargeInfoIcon" aria-label="Infomation" />;
icon = (
<Icon iconName="InfoSolid" className="panelLargeInfoIcon" aria-label={t(Keys.panes.panelInfo.information)} />
);
}
return (
@@ -43,7 +49,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
</Text>
{showErrorDetails && (
<a className="paneErrorLink" role="button" onClick={expandConsole} tabIndex={0} onKeyPress={expandConsole}>
More details
{t(Keys.panes.panelInfo.moreDetails)}
</a>
)}
</span>

View File

@@ -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<PublishNotebookPaneAProps> =
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<PublishNotebookPaneAProps> =
);
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;

View File

@@ -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<PublishNotebookPane
const maxImageSizeInMib = 1.5;
const descriptionPara1 =
"When published, this notebook will appear in the Azure Cosmos DB notebooks public gallery. Make sure you have removed any sensitive data or output before publishing.";
const descriptionPara1 = t(Keys.panes.publishNotebook.publishDescription);
const descriptionPara2 = `Would you like to publish and share "${FileSystemUtil.stripExtension(
notebookName,
"ipynb",
)}" to the gallery?`;
const descriptionPara2 = t(Keys.panes.publishNotebook.publishPrompt, {
name: FileSystemUtil.stripExtension(notebookName, "ipynb"),
});
const options: ImageTypes[] = [ImageTypes.CustomImage, ImageTypes.Url];
if (onTakeSnapshot) {
@@ -74,9 +74,9 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
}
const thumbnailSelectorProps: IDropdownProps = {
label: "Cover image",
label: t(Keys.panes.publishNotebook.coverImage),
selectedKey: type,
ariaLabel: "Cover image",
ariaLabel: t(Keys.panes.publishNotebook.coverImage),
options: options.map((value: string) => ({ text: value, key: value })),
onChange: async (event, options) => {
setImageSrc("");
@@ -99,7 +99,7 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
notebookContentRef,
});
} else {
firstOutputErrorHandler(new Error("Output does not exist for any of the cells."));
firstOutputErrorHandler(new Error(t(Keys.panes.publishNotebook.outputDoesNotExist)));
}
}
setType(options.text);
@@ -107,8 +107,8 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
};
const thumbnailUrlProps: ITextFieldProps = {
label: "Cover image url",
ariaLabel: "Cover image url",
label: t(Keys.panes.publishNotebook.coverImageUrl),
ariaLabel: t(Keys.panes.publishNotebook.coverImageUrl),
required: true,
onChange: (event, newValue) => {
setImageSrc(newValue);
@@ -116,7 +116,7 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
};
const firstOutputErrorHandler = (error: Error) => {
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<PublishNotebookPane
};
reader.onerror = (error) => {
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<PublishNotebookPane
const file = event.target.files[0];
if (file.size / 1024 ** 2 > 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<PublishNotebookPane
<Stack.Item>
<TextField
label="Name"
ariaLabel="Name"
label={t(Keys.panes.publishNotebook.name)}
ariaLabel={t(Keys.panes.publishNotebook.name)}
defaultValue={FileSystemUtil.stripExtension(notebookName, "ipynb")}
required
onChange={(event, newValue) => {
@@ -198,8 +198,8 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
<Stack.Item>
<TextField
label="Description"
ariaLabel="Description"
label={t(Keys.panes.publishNotebook.description)}
ariaLabel={t(Keys.panes.publishNotebook.description)}
multiline
rows={3}
required
@@ -211,9 +211,9 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
<Stack.Item>
<TextField
label="Tags"
ariaLabel="Tags"
placeholder="Optional tag 1, Optional tag 2"
label={t(Keys.panes.publishNotebook.tags)}
ariaLabel={t(Keys.panes.publishNotebook.tags)}
placeholder={t(Keys.panes.publishNotebook.tagsPlaceholder)}
onChange={(event, newValue) => {
setNotebookTags(newValue);
}}
@@ -227,7 +227,7 @@ export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPane
<Stack.Item>{renderThumbnailSelectors(type)}</Stack.Item>
<Stack.Item>
<Text>Preview</Text>
<Text>{t(Keys.panes.publishNotebook.preview)}</Text>
</Stack.Item>
<Stack.Item>
<GalleryCardComponent

View File

@@ -4,6 +4,8 @@ import React, { FunctionComponent, useState } from "react";
import { Areas, SavedQueries } from "../../../Common/Constants";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import { Query } from "../../../Contracts/DataModels";
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 { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
@@ -28,27 +30,27 @@ export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({
const [formError, setFormError] = useState<string>("");
const [queryName, setQueryName] = useState<string>("");
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<void> => {
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<SaveQueryPaneProps> = ({
} 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<SaveQueryPaneProps> = ({
},
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<SaveQueryPaneProps> = ({
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<SaveQueryPaneProps> = ({
) : (
<TextField
id="saveQueryInput"
label="Name"
label={t(Keys.panes.saveQuery.name)}
autoFocus
styles={{ fieldGroup: { width: 300 } }}
onChange={(event, newInput?: string) => {

View File

@@ -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 && (
<AccordionItem value="1">
<AccordionHeader>
<div className={styles.header}>Page Options</div>
<div className={styles.header}>{t(Keys.panes.settings.pageOptions)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
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)}
</div>
<ChoiceGroup
ariaLabelledBy="pageOptions"
@@ -744,14 +741,14 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{isCustomPageOptionSelected() && (
<div className="tabcontent">
<div className={styles.settingsSectionDescription}>
Query results per page{" "}
{t(Keys.panes.settings.queryResultsPerPage)}{" "}
<InfoTooltip className={styles.headerIcon}>
Enter the number of query results that should be shown per page.
{t(Keys.panes.settings.queryResultsPerPageTooltip)}
</InfoTooltip>
</div>
<SpinButton
ariaLabel="Custom query items per page"
ariaLabel={t(Keys.panes.settings.customQueryItemsPerPage)}
value={"" + customItemPerPage}
onIncrement={(newValue) => {
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}
/>
</div>
@@ -774,20 +771,19 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{showEnableEntraIdRbac && (
<AccordionItem value="2">
<AccordionHeader>
<div className={styles.header}>Enable Entra ID RBAC</div>
<div className={styles.header}>{t(Keys.panes.settings.entraIdRbac)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Choose Automatic to enable Entra ID RBAC automatically. True/False to force enable/disable Entra
ID RBAC.
{t(Keys.panes.settings.entraIdRbacDescription)}
<a
href="https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac#use-data-explorer"
target="_blank"
rel="noopener noreferrer"
>
{" "}
Learn more{" "}
{t(Keys.common.learnMore)}{" "}
</a>
</div>
<ChoiceGroup
@@ -804,17 +800,17 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{userContext.apiType === "SQL" && userContext.authType === AuthType.AAD && !isFabric() && (
<AccordionItem value="3">
<AccordionHeader>
<div className={styles.header}>Region Selection</div>
<div className={styles.header}>{t(Keys.panes.settings.regionSelection)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Changes region the Cosmos Client uses to access account.
{t(Keys.panes.settings.regionSelectionDescription)}
</div>
<div>
<span className={styles.subHeader}>Select Region</span>
<span className={styles.subHeader}>{t(Keys.panes.settings.selectRegion)}</span>
<InfoTooltip className={styles.headerIcon}>
Changes the account endpoint used to perform client operations.
{t(Keys.panes.settings.selectRegionTooltip)}
</InfoTooltip>
</div>
<Dropdown
@@ -865,17 +861,16 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
<>
<AccordionItem value="4">
<AccordionHeader>
<div className={styles.header}>Query Timeout</div>
<div className={styles.header}>{t(Keys.panes.settings.queryTimeout)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
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)}
</div>
<Toggle
styles={toggleStyles}
label="Enable query timeout"
label={t(Keys.panes.settings.enableQueryTimeout)}
onChange={handleOnQueryTimeoutToggleChange}
defaultChecked={queryTimeoutEnabled}
/>
@@ -883,18 +878,18 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{queryTimeoutEnabled && (
<div className={styles.settingsSectionContainer}>
<SpinButton
label="Query timeout (ms)"
label={t(Keys.panes.settings.queryTimeoutMs)}
labelPosition={Position.top}
defaultValue={(queryTimeout || 5000).toString()}
min={100}
step={1000}
onChange={handleOnQueryTimeoutSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
incrementButtonAriaLabel={t(Keys.panes.settings.increaseValueBy1000)}
decrementButtonAriaLabel={t(Keys.panes.settings.decreaseValueBy1000)}
styles={spinButtonStyles}
/>
<Toggle
label="Automatically cancel query after timeout"
label={t(Keys.panes.settings.automaticallyCancelQuery)}
styles={toggleStyles}
onChange={handleOnAutomaticallyCancelQueryToggleChange}
defaultChecked={automaticallyCancelQueryAfterTimeout}
@@ -905,16 +900,16 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</AccordionItem>
<AccordionItem value="5">
<AccordionHeader>
<div className={styles.header}>RU Limit</div>
<div className={styles.header}>{t(Keys.panes.settings.ruLimit)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
If a query exceeds a configured RU limit, the query will be aborted.
{t(Keys.panes.settings.ruLimitDescription)}
</div>
<Toggle
styles={toggleStyles}
label="Enable RU limit"
label={t(Keys.panes.settings.enableRuLimit)}
onChange={handleOnRUThresholdToggleChange}
defaultChecked={ruThresholdEnabled}
/>
@@ -922,14 +917,14 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{ruThresholdEnabled && (
<div className={styles.settingsSectionContainer}>
<SpinButton
label="RU Limit (RU)"
label={t(Keys.panes.settings.ruLimitLabel)}
labelPosition={Position.top}
defaultValue={(ruThreshold || DefaultRUThreshold).toString()}
min={1}
step={1000}
onChange={handleOnRUThresholdSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
incrementButtonAriaLabel={t(Keys.panes.settings.increaseValueBy1000)}
decrementButtonAriaLabel={t(Keys.panes.settings.decreaseValueBy1000)}
styles={spinButtonStyles}
/>
</div>
@@ -939,12 +934,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
<AccordionItem value="6">
<AccordionHeader>
<div className={styles.header}>Default Query Results View</div>
<div className={styles.header}>{t(Keys.panes.settings.defaultQueryResults)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Select the default view to use when displaying query results.
{t(Keys.panes.settings.defaultQueryResultsDescription)}
</div>
<ChoiceGroup
ariaLabelledBy="defaultQueryResultsView"
@@ -962,17 +957,17 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{showRetrySettings && (
<AccordionItem value="7">
<AccordionHeader>
<div className={styles.header}>Retry Settings</div>
<div className={styles.header}>{t(Keys.panes.settings.retrySettings)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Retry policy associated with throttled requests during CosmosDB queries.
{t(Keys.panes.settings.retrySettingsDescription)}
</div>
<div>
<span className={styles.subHeader}>Max retry attempts</span>
<span className={styles.subHeader}>{t(Keys.panes.settings.maxRetryAttempts)}</span>
<InfoTooltip className={styles.headerIcon}>
Max number of retries to be performed for a request. Default value 9.
{t(Keys.panes.settings.maxRetryAttemptsTooltip)}
</InfoTooltip>
</div>
<SpinButton
@@ -981,18 +976,17 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
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}
/>
<div>
<span className={styles.subHeader}>Fixed retry interval (ms)</span>
<span className={styles.subHeader}>{t(Keys.panes.settings.fixedRetryInterval)}</span>
<InfoTooltip className={styles.headerIcon}>
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)}
</InfoTooltip>
</div>
<SpinButton
@@ -1001,18 +995,17 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
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}
/>
<div>
<span className={styles.subHeader}>Max wait time (s)</span>
<span className={styles.subHeader}>{t(Keys.panes.settings.maxWaitTime)}</span>
<InfoTooltip className={styles.headerIcon}>
Max wait time in seconds to wait for a request while the retries are happening. Default value 30
seconds.
{t(Keys.panes.settings.maxWaitTimeTooltip)}
</InfoTooltip>
</div>
<SpinButton
@@ -1021,8 +1014,8 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
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 && (
<AccordionItem value="8">
<AccordionHeader>
<div className={styles.header}>Enable container pagination</div>
<div className={styles.header}>{t(Keys.panes.settings.enableContainerPagination)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Load 50 containers at a time. Currently, containers are not pulled in alphanumeric order.
{t(Keys.panes.settings.enableContainerPaginationDescription)}
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="Enable container pagination"
ariaLabel={t(Keys.panes.settings.enableContainerPagination)}
checked={containerPaginationEnabled}
onChange={() => setContainerPaginationEnabled(!containerPaginationEnabled)}
label="Enable container pagination"
label={t(Keys.panes.settings.enableContainerPagination)}
onRenderLabel={() => (
<span style={{ color: "var(--colorNeutralForeground1)" }}>Enable container pagination</span>
<span style={{ color: "var(--colorNeutralForeground1)" }}>
{t(Keys.panes.settings.enableContainerPagination)}
</span>
)}
/>
</div>
@@ -1066,24 +1061,25 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowCrossPartitionOption && (
<AccordionItem value="9">
<AccordionHeader>
<div className={styles.header}>Enable cross-partition query</div>
<div className={styles.header}>{t(Keys.panes.settings.enableCrossPartitionQuery)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
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)}
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="Enable cross partition query"
ariaLabel={t(Keys.panes.settings.enableCrossPartitionQuery)}
checked={crossPartitionQueryEnabled}
onChange={() => setCrossPartitionQueryEnabled(!crossPartitionQueryEnabled)}
onRenderLabel={() => (
<span style={{ color: "var(--colorNeutralForeground1)" }}>Enable cross-partition query</span>
<span style={{ color: "var(--colorNeutralForeground1)" }}>
{t(Keys.panes.settings.enableCrossPartitionQuery)}
</span>
)}
/>
</div>
@@ -1093,19 +1089,19 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowEnhancedQueryControl && (
<AccordionItem value="10">
<AccordionHeader>
<div className={styles.header}>Enhanced query control</div>
<div className={styles.header}>{t(Keys.panes.settings.enhancedQueryControl)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Query up to the max degree of parallelism.
{t(Keys.panes.settings.maxDegreeOfParallelismQuery)}
<a
href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/performance-tips-query-sdk?tabs=v3&pivots=programming-language-nodejs#enhanced-query-control"
target="_blank"
rel="noopener noreferrer"
>
{" "}
Learn more{" "}
{t(Keys.common.learnMore)}{" "}
</a>
</div>
<Checkbox
@@ -1113,11 +1109,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
label: { padding: 0 },
}}
className="padding"
ariaLabel="EnableQueryControl"
ariaLabel={t(Keys.panes.settings.enableQueryControl)}
checked={queryControlEnabled}
onChange={() => setQueryControlEnabled(!queryControlEnabled)}
onRenderLabel={() => (
<span style={{ color: "var(--colorNeutralForeground1)" }}>Enable query control</span>
<span style={{ color: "var(--colorNeutralForeground1)" }}>
{t(Keys.panes.settings.enableQueryControl)}
</span>
)}
/>
</div>
@@ -1127,14 +1125,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowParallelismOption && (
<AccordionItem value="10">
<AccordionHeader>
<div className={styles.header}>Max degree of parallelism</div>
<div className={styles.header}>{t(Keys.panes.settings.maxDegreeOfParallelism)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
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)}
</div>
<SpinButton
min={-1}
@@ -1150,8 +1146,8 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
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}
/>
</div>
@@ -1161,14 +1157,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowPriorityLevelOption && (
<AccordionItem value="11">
<AccordionHeader>
<div className={styles.header}>Priority Level</div>
<div className={styles.header}>{t(Keys.panes.settings.priorityLevel)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Sets the priority level for data-plane requests from Data Explorer when using Priority-Based
Execution. If &quot;None&quot; is selected, Data Explorer will not specify priority level, and the
server-side default priority level will be used.
{t(Keys.panes.settings.priorityLevelDescription)}
</div>
<ChoiceGroup
ariaLabelledBy="priorityLevel"
@@ -1184,19 +1178,18 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowGraphAutoVizOption && (
<AccordionItem value="12">
<AccordionHeader>
<div className={styles.header}>Display Gremlin query results as:&nbsp;</div>
<div className={styles.header}>{t(Keys.panes.settings.displayGremlinQueryResults)}&nbsp;</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Select Graph to automatically visualize the query results as a Graph or JSON to display the
results as JSON.
{t(Keys.panes.settings.displayGremlinQueryResultsDescription)}
</div>
<ChoiceGroup
selectedKey={graphAutoVizDisabled}
options={graphAutoOptionList}
onChange={handleOnGremlinChange}
aria-label="Graph Auto-visualization"
aria-label={t(Keys.panes.settings.graphAutoVisualization)}
/>
</div>
</AccordionPanel>
@@ -1205,25 +1198,25 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowCopilotSampleDBOption && (
<AccordionItem value="13">
<AccordionHeader>
<div className={styles.header}>Enable sample database</div>
<div className={styles.header}>{t(Keys.panes.settings.enableSampleDatabase)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
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)}
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="Enable sample db for query exploration"
ariaLabel={t(Keys.panes.settings.enableSampleDbAriaLabel)}
checked={copilotSampleDBEnabled}
onChange={handleSampleDatabaseChange}
onRenderLabel={() => (
<span style={{ color: "var(--colorNeutralForeground1)" }}>Enable sample database</span>
<span style={{ color: "var(--colorNeutralForeground1)" }}>
{t(Keys.panes.settings.enableSampleDatabase)}
</span>
)}
/>
</div>
@@ -1233,13 +1226,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{shouldShowMongoGuidRepresentationOption && (
<AccordionItem value="14">
<AccordionHeader>
<div className={styles.header}>Guid Representation</div>
<div className={styles.header}>{t(Keys.panes.settings.guidRepresentation)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
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)}
</div>
<Dropdown
aria-labelledby="mongoGuidRepresentation"
@@ -1253,7 +1245,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
)}
<AccordionItem value="15">
<AccordionHeader>
<div className={styles.header}>Advanced Settings</div>
<div className={styles.header}>{t(Keys.panes.settings.advancedSettings)}</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
@@ -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)}
/>
<InfoTooltip className={styles.headerIcon}>
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)}
</InfoTooltip>
</Stack>
</div>
@@ -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,
<>
<span>
This action will clear the all customizations for this account in this browser, including:
</span>
<span>{t(Keys.panes.settings.clearHistoryDescription)}</span>
<ul className={styles.bulletList}>
<li>Reset your customized tab layout, including the splitter positions</li>
<li>Erase your table column preferences, including any custom columns</li>
<li>Clear your filter history</li>
<li>Reset region selection to global</li>
<li>{t(Keys.panes.settings.clearHistoryTabLayout)}</li>
<li>{t(Keys.panes.settings.clearHistoryTableColumns)}</li>
<li>{t(Keys.panes.settings.clearHistoryFilters)}</li>
<li>{t(Keys.panes.settings.clearHistoryRegion)}</li>
</ul>
</>,
);
}}
>
Clear History
{t(Keys.panes.settings.clearHistory)}
</DefaultButton>
</div>
</div>
<div className="settingsSection">
<div className={`settingsSectionPart ${styles.settingsSectionContainer}`}>
<div className="settingsSectionLabel">Explorer Version</div>
<div className="settingsSectionLabel">{t(Keys.panes.settings.explorerVersion)}</div>
<div>{explorerVersion}</div>
</div>
</div>
<div className="settingsSection">
<div className="settingsSectionPart">
<div className="settingsSectionLabel">Session ID</div>
<div className="settingsSectionLabel">{t(Keys.panes.settings.sessionId)}</div>
<div>{sessionId}</div>
</div>
</div>

View File

@@ -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.
</div>
<StyledCheckboxBase
ariaLabel="Enable cross partition query"
ariaLabel="Enable cross-partition query"
checked={false}
className="padding"
onChange={[Function]}
@@ -705,7 +705,7 @@ exports[`Settings Pane should render Default properly 1`] = `
</a>
</div>
<StyledCheckboxBase
ariaLabel="EnableQueryControl"
ariaLabel="Enable query control"
checked={false}
className="padding"
onChange={[Function]}
@@ -1190,7 +1190,8 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
<div
className="___j7dlp70_0000000 fq02s40 f19n0e5"
>
Display Gremlin query results as: 
Display Gremlin query results as:
 
</div>
</AccordionHeader>
<AccordionPanel>

View File

@@ -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<TableColumnSelectionPaneProps> =
<CosmosFluentProvider>
<div className="panelFormWrapper">
<div className="panelMainContent" style={{ display: "flex", flexDirection: "column" }}>
<Text>Select which columns to display in your view of items in your container.</Text>
<Text>{t(Keys.panes.tableColumnSelection.selectColumns)}</Text>
<div /* Wrap <SearchBox> to avoid margin-bottom set by panelMainContent css */>
<SearchBox
className={styles.searchBox}
value={columnSearchText}
onChange={onSearchChange}
placeholder="Search fields"
placeholder={t(Keys.panes.tableColumnSelection.searchFields)}
/>
</div>
@@ -130,7 +132,9 @@ export const TableColumnSelectionPane: React.FC<TableColumnSelectionPaneProps> =
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<TableColumnSelectionPaneProps> =
))}
</div>
<Button appearance="secondary" size="small" onClick={() => setNewSelectedColumnIds(defaultSelection)}>
Reset
{t(Keys.panes.tableColumnSelection.reset)}
</Button>
</div>
<div className="panelFooter" style={{ display: "flex", gap: theme.spacingHorizontalS }}>
<Button appearance="primary" onClick={onSave}>
Save
{t(Keys.common.save)}
</Button>
<Button appearance="secondary" onClick={closeSidePanel}>
Cancel
{t(Keys.common.cancel)}
</Button>
</div>
</div>

View File

@@ -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<AddTableEntityPanelProps> =
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<AddTableEntityPanelProps> =
(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;
}

View File

@@ -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<EditTableEntityPanelProps>
}
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<EditTableEntityPanelProps>
(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<EditTableEntityPanelProps>
)}
</div>
<div className="panelNullWarning" style={{ padding: "20px", color: "red" }}>
Warning: Null fields will not be displayed for editing.
{t(Keys.panes.tables.nullFieldsWarning)}
</div>
</RightPaneForm>
);

View File

@@ -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<TableQuerySelectPanelProps
const props: RightPaneFormProps = {
formError: "",
isExecuting: false,
submitButtonText: "OK",
submitButtonText: t(Keys.common.ok),
onSubmit,
};
@@ -121,11 +123,11 @@ export const TableQuerySelectPanel: FunctionComponent<TableQuerySelectPanelProps
<RightPaneForm {...props}>
<div className="panelFormWrapper">
<div className="panelMainContent">
<Text>Select the columns that you want to query.</Text>
<Text>{t(Keys.panes.tableQuerySelect.selectColumns)}</Text>
<div className="column-select-view">
<Checkbox
id="availableCheckbox"
label="Available Columns"
label={t(Keys.panes.tableQuerySelect.availableColumns)}
checked={isAvailableColumnChecked}
onChange={availableColumnsCheckboxClick}
/>

View File

@@ -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<UploadItemsPaneProps> = ({ 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<UploadItemsPaneProps> = ({ 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<UploadItemsPaneProps> = ({ 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<UploadItemsPaneProps> = ({ 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 (
<TooltipHost
content={fieldContent}
@@ -197,12 +203,12 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpl
<div className="paneMainContent">
<Upload
key={reducer} // Force re-render on state change
label="Select JSON Files"
label={t(Keys.panes.uploadItems.selectJsonFiles)}
onUpload={updateSelectedFiles}
accept="application/json"
multiple
tabIndex={0}
tooltip="Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON documents. The combined size of all files in an individual upload operation must be less than 2 MB. You can perform multiple upload operations for larger data sets."
tooltip={t(Keys.panes.uploadItems.selectJsonFilesTooltip)}
/>
{uploadFileData?.length > 0 && (
<div className="fileUploadSummaryContainer" data-test="file-upload-status">

View File

@@ -714,9 +714,7 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
<span
className="css-126"
>
What is the reason why you are deleting this
Database
?
What is the reason why you are deleting this Database?
</span>
</Text>
<StyledTextFieldBase

View File

@@ -6,6 +6,8 @@ import { DocumentAddRegular, LinkMultipleRegular, OpenRegular } from "@fluentui/
import { SampleDataConfiguration, SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
import { SampleDataFile } from "Explorer/SplashScreen/SampleUtil";
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { Keys } from "Localization/Keys.generated";
import { t } from "Localization/t";
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
import * as React from "react";
import { userContext } from "UserContext";
@@ -159,8 +161,8 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
const getSplashScreenButtons = (): JSX.Element => {
const buttons: FabricHomeScreenButtonProps[] = [
{
title: "New container",
description: "Create a destination container to store your data",
title: t(Keys.splashScreen.fabric.newContainer.title),
description: t(Keys.splashScreen.fabric.newContainer.description),
icon: <DocumentAddRegular />,
onClick: () => {
const databaseId = isFabricNative() ? userContext.fabricContext?.databaseName : undefined;
@@ -168,8 +170,8 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
},
},
{
title: "Sample Data",
description: "Load sample data in your database",
title: t(Keys.splashScreen.fabric.sampleData.title),
description: t(Keys.splashScreen.fabric.sampleData.description),
icon: <img src={CosmosDbBlackIcon} alt={"Azure Cosmos DB icon"} aria-hidden="true" />,
onClick: () => {
setSelectedSampleDataConfiguration({
@@ -181,8 +183,8 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
},
},
{
title: "Sample Vector Data",
description: "Load sample vector data with text-embedding-ada-002",
title: t(Keys.splashScreen.fabric.sampleVectorData.title),
description: t(Keys.splashScreen.fabric.sampleVectorData.description),
icon: <img src={AzureOpenAiIcon} alt={"Azure Open AI icon"} aria-hidden="true" />,
onClick: () => {
setSelectedSampleDataConfiguration({
@@ -194,14 +196,14 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
},
},
{
title: "App development",
description: "Start here to use an SDK to build your apps",
title: t(Keys.splashScreen.fabric.appDevelopment.title),
description: t(Keys.splashScreen.fabric.appDevelopment.description),
icon: <LinkMultipleRegular />,
onClick: () => window.open("https://aka.ms/cosmosdbfabricsdk", "_blank"),
},
{
title: "Sample Gallery",
description: "Get real-world end-to-end samples",
title: t(Keys.splashScreen.fabric.sampleGallery.title),
description: t(Keys.splashScreen.fabric.sampleGallery.description),
icon: <img src={GithubIcon} alt={"GitHub icon"} aria-hidden="true" />,
onClick: () => window.open("https://aka.ms/CosmosFabricSamplesGallery", "_blank"),
},
@@ -222,7 +224,9 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
);
};
const title = isFabricNativeReadOnly() ? "Use your database" : "Build your database";
const title = isFabricNativeReadOnly()
? t(Keys.splashScreen.fabric.useTitle)
: t(Keys.splashScreen.fabric.buildTitle);
return (
<>
<CosmosFluentProvider className={styles.homeContainer}>
@@ -238,9 +242,9 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
{getSplashScreenButtons()}
{
<div className={styles.footer}>
Need help?{" "}
{t(Keys.splashScreen.sections.needHelp)}{" "}
<Link href="https://learn.microsoft.com/fabric/database/cosmos-db/overview" target="_blank">
Learn more <OpenRegular />
{t(Keys.common.learnMore)} <OpenRegular />
</Link>
</div>
}

View File

@@ -12,6 +12,8 @@ import {
} from "@fluentui/react-components";
import Explorer from "Explorer/Explorer";
import { checkContainerExists, createContainer, importData, SampleDataFile } from "Explorer/SplashScreen/SampleUtil";
import { Keys } from "Localization/Keys.generated";
import { t } from "Localization/t";
import React, { useEffect, useState } from "react";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -59,7 +61,7 @@ export const SampleDataImportDialog: React.FC<{
setStatus("creating");
const databaseName = props.sampleDataConfiguration.databaseName;
if (checkContainerExists(databaseName, containerName)) {
const msg = `The container "${containerName}" in database "${databaseName}" already exists. Please delete it and retry.`;
const msg = t(Keys.splashScreen.sampleDataDialog.errorContainerExists, { containerName, databaseName });
setStatus("error");
setErrorMessage(msg);
return;
@@ -75,7 +77,11 @@ export const SampleDataImportDialog: React.FC<{
);
} catch (error) {
setStatus("error");
setErrorMessage(`Failed to create container: ${error instanceof Error ? error.message : String(error)}`);
setErrorMessage(
t(Keys.splashScreen.sampleDataDialog.errorCreateContainer, {
error: error instanceof Error ? error.message : String(error),
}),
);
return;
}
@@ -86,7 +92,11 @@ export const SampleDataImportDialog: React.FC<{
setStatus("completed");
} catch (error) {
setStatus("error");
setErrorMessage(`Failed to import data: ${error instanceof Error ? error.message : String(error)}`);
setErrorMessage(
t(Keys.splashScreen.sampleDataDialog.errorImportData, {
error: error instanceof Error ? error.message : String(error),
}),
);
}
};
@@ -112,14 +122,26 @@ export const SampleDataImportDialog: React.FC<{
const renderContent = () => {
switch (status) {
case "idle":
return `Create a container "${containerName}" and import sample data into it. This may take a few minutes.`;
return t(Keys.splashScreen.sampleDataDialog.createPrompt, { containerName });
case "creating":
return <Spinner size="small" labelPosition="above" label={`Creating container "${containerName}"...`} />;
return (
<Spinner
size="small"
labelPosition="above"
label={t(Keys.splashScreen.sampleDataDialog.creatingContainer, { containerName })}
/>
);
case "importing":
return <Spinner size="small" labelPosition="above" label={`Importing data into "${containerName}"...`} />;
return (
<Spinner
size="small"
labelPosition="above"
label={t(Keys.splashScreen.sampleDataDialog.importingData, { containerName })}
/>
);
case "completed":
return `Successfully created "${containerName}" with sample data.`;
return t(Keys.splashScreen.sampleDataDialog.success, { containerName });
case "error":
return (
<div style={{ color: "red" }}>
@@ -132,14 +154,14 @@ export const SampleDataImportDialog: React.FC<{
const getButtonLabel = () => {
switch (status) {
case "idle":
return "Start";
return t(Keys.splashScreen.sampleDataDialog.startButton);
case "creating":
case "importing":
return "Close";
return t(Keys.common.close);
case "completed":
return "Close";
return t(Keys.common.close);
case "error":
return "Close";
return t(Keys.common.close);
}
};
@@ -147,7 +169,7 @@ export const SampleDataImportDialog: React.FC<{
<Dialog open={props.open} onOpenChange={(event, data) => props.setOpen(data.open)}>
<DialogSurface>
<DialogBody>
<DialogTitle>Sample Data</DialogTitle>
<DialogTitle>{t(Keys.splashScreen.sampleDataDialog.title)}</DialogTitle>
<DialogContent>
<div className={styles.dialogContent}>{renderContent()}</div>
</DialogContent>

View File

@@ -16,6 +16,8 @@ import { sendMessage } from "Common/MessageHandler";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { TerminalKind } from "Contracts/ViewModels";
import { SplashScreenButton } from "Explorer/SplashScreen/SplashScreenButton";
import { Keys } from "Localization/Keys.generated";
import { t } from "Localization/t";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { useCarousel } from "hooks/useCarousel";
@@ -169,16 +171,16 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
switch (userContext.apiType) {
case "Postgres":
title = "Welcome to Azure Cosmos DB for PostgreSQL";
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
title = t(Keys.splashScreen.title.postgres);
subtitle = t(Keys.splashScreen.subtitle.getStarted);
break;
case "VCoreMongo":
title = "Welcome to Azure DocumentDB (with MongoDB compatibility)";
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
title = t(Keys.splashScreen.title.vcoreMongo);
subtitle = t(Keys.splashScreen.subtitle.getStarted);
break;
default:
title = "Welcome to Azure Cosmos DB";
subtitle = "Globally distributed, multi-model database service for any scale";
title = t(Keys.splashScreen.title.default);
subtitle = t(Keys.splashScreen.subtitle.default);
}
React.useEffect(() => {
@@ -249,8 +251,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
<Stack className="splashStackRow" horizontal>
<SplashScreenButton
imgSrc={QuickStartIcon}
title={"Launch quick start"}
description={"Launch a quick start tutorial to get started with sample data"}
title={t(Keys.splashScreen.quickStart.title)}
description={t(Keys.splashScreen.quickStart.description)}
onClick={() => {
container.onNewCollectionClicked({ isQuickstart: true });
traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType });
@@ -258,8 +260,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
/>
<SplashScreenButton
imgSrc={ContainersIcon}
title={`New ${getCollectionName()}`}
description={"Create a new container for storage and throughput"}
title={t(Keys.splashScreen.newCollection.title, { collectionName: getCollectionName() })}
description={t(Keys.splashScreen.newCollection.description)}
onClick={() => {
container.onNewCollectionClicked();
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
@@ -270,10 +272,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
<SplashScreenButton
imgSrc={CosmosDBIcon}
imgSize={35}
title={"Azure Cosmos DB Samples Gallery"}
description={
"Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
}
title={t(Keys.splashScreen.samplesGallery.title)}
description={t(Keys.splashScreen.samplesGallery.description)}
onClick={() => {
window.open("https://azurecosmosdb.github.io/gallery/?tags=example", "_blank");
traceOpen(Action.LearningResourcesClicked, { apiType: userContext.apiType });
@@ -281,8 +281,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
/>
<SplashScreenButton
imgSrc={ConnectIcon}
title={"Connect"}
description={"Prefer using your own choice of tooling? Find the connection string you need to connect"}
title={t(Keys.splashScreen.connectCard.title)}
description={t(Keys.splashScreen.connectCard.description)}
onClick={() => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect)}
/>
</Stack>
@@ -297,7 +297,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
usePostgres.getState().showPostgreTeachingBubble &&
!usePostgres.getState().showResetPasswordBubble && (
<TeachingBubble
headline="New to Cosmos DB PGSQL?"
headline={t(Keys.splashScreen.teachingBubble.newToPostgres.headline)}
target={"#mainButton-quickstartDescription"}
hasCloseButton
onDismiss={() => usePostgres.getState().setShowPostgreTeachingBubble(false)}
@@ -309,15 +309,14 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
preventDismissOnScroll: true,
}}
primaryButtonProps={{
text: "Get started",
text: t(Keys.common.getStarted),
onClick: () => {
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
usePostgres.getState().setShowPostgreTeachingBubble(false);
},
}}
>
Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find
sample data, query.
{t(Keys.splashScreen.teachingBubble.newToPostgres.body)}
</TeachingBubble>
)}
{/*TODO: convert below to use SplashScreenButton */}
@@ -349,7 +348,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
))}
{userContext.apiType === "Postgres" && usePostgres.getState().showResetPasswordBubble && (
<TeachingBubble
headline="Create your password"
headline={t(Keys.splashScreen.teachingBubble.resetPassword.headline)}
target={"#mainButton-quickstartDescription"}
hasCloseButton
onDismiss={() => {
@@ -364,7 +363,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
preventDismissOnScroll: true,
}}
primaryButtonProps={{
text: "Create",
text: t(Keys.common.create),
onClick: () => {
localStorage.setItem(userContext.databaseAccount.id, "true");
sendMessage({
@@ -374,7 +373,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
},
}}
>
If you haven&apos;t changed your password yet, change it now.
{t(Keys.splashScreen.teachingBubble.resetPassword.body)}
</TeachingBubble>
)}
</div>
@@ -393,8 +392,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const launchQuickstartBtn = {
id: "quickstartDescription",
iconSrc: QuickStartIcon,
title: "Launch quick start",
description: "Launch a quick start tutorial to get started with sample data",
title: t(Keys.splashScreen.quickStart.title),
description: t(Keys.splashScreen.quickStart.description),
onClick: () => {
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
@@ -416,8 +415,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
if (userContext.apiType === "Postgres") {
return {
iconSrc: PowerShellIcon,
title: "PostgreSQL Shell",
description: "Create table and interact with data using PostgreSQL's shell interface",
title: t(Keys.splashScreen.shell.postgres.title),
description: t(Keys.splashScreen.shell.postgres.description),
onClick: () => container.openNotebookTerminal(TerminalKind.Postgres),
};
}
@@ -425,16 +424,16 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
if (userContext.apiType === "VCoreMongo") {
return {
iconSrc: PowerShellIcon,
title: "Mongo Shell",
description: "Create a collection and interact with data using MongoDB's shell interface",
title: t(Keys.splashScreen.shell.vcoreMongo.title),
description: t(Keys.splashScreen.shell.vcoreMongo.description),
onClick: () => container.openNotebookTerminal(TerminalKind.VCoreMongo),
};
}
return {
iconSrc: ContainersIcon,
title: `New ${getCollectionName()}`,
description: "Create a new container for storage and throughput",
title: t(Keys.splashScreen.newCollection.title, { collectionName: getCollectionName() }),
description: t(Keys.splashScreen.newCollection.description),
onClick: () => {
container.onNewCollectionClicked();
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
@@ -444,19 +443,19 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const getThirdCard = (): SplashScreenItem => {
let icon = ConnectIcon;
let title = "Connect";
let description = "Prefer using your own choice of tooling? Find the connection string you need to connect";
let title = t(Keys.splashScreen.connectCard.title);
let description = t(Keys.splashScreen.connectCard.description);
let onClick = () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect);
if (userContext.apiType === "Postgres") {
title = "Connect with pgAdmin";
description = "Prefer pgAdmin? Find your connection strings here";
title = t(Keys.splashScreen.connectCard.pgAdmin.title);
description = t(Keys.splashScreen.connectCard.pgAdmin.description);
}
if (userContext.apiType === "VCoreMongo") {
icon = VisualStudioIcon;
title = "Connect with VS Code";
description = "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code";
title = t(Keys.splashScreen.connectCard.vsCode.title);
description = t(Keys.splashScreen.connectCard.vsCode.description);
onClick = () => container?.openInVsCode && container.openInVsCode();
}
@@ -485,7 +484,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
info: activity.path,
iconSrc: NotebookIcon,
title: activity.name,
description: "Notebook",
description: t(Keys.splashScreen.sections.notebook),
onClick: () => {
const notebookItem = container.createNotebookContentItemFile(activity.name, activity.path);
notebookItem && container.openNotebook(notebookItem);
@@ -524,18 +523,18 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
items = [
{
link: "https://aka.ms/msl-modeling-partitioning-2",
title: "Advanced Modeling Patterns",
description: "Learn advanced strategies to optimize your database.",
title: t(Keys.splashScreen.top3Items.sql.advancedModeling.title),
description: t(Keys.splashScreen.top3Items.sql.advancedModeling.description),
},
{
link: "https://aka.ms/msl-modeling-partitioning-1",
title: "Partitioning Best Practices",
description: "Learn to apply data model and partitioning strategies.",
title: t(Keys.splashScreen.top3Items.sql.partitioning.title),
description: t(Keys.splashScreen.top3Items.sql.partitioning.description),
},
{
link: "https://aka.ms/msl-resource-planning",
title: "Plan Your Resource Requirements",
description: "Get to know the different configuration choices.",
title: t(Keys.splashScreen.top3Items.sql.resourcePlanning.title),
description: t(Keys.splashScreen.top3Items.sql.resourcePlanning.description),
},
];
break;
@@ -543,18 +542,18 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
items = [
{
link: "https://aka.ms/mongodbintro",
title: "What is the MongoDB API?",
description: "Understand Azure Cosmos DB for MongoDB and its features.",
title: t(Keys.splashScreen.top3Items.mongo.whatIsMongo.title),
description: t(Keys.splashScreen.top3Items.mongo.whatIsMongo.description),
},
{
link: "https://aka.ms/mongodbfeaturesupport",
title: "Features and Syntax",
description: "Discover the advantages and features",
title: t(Keys.splashScreen.top3Items.mongo.features.title),
description: t(Keys.splashScreen.top3Items.mongo.features.description),
},
{
link: "https://aka.ms/mongodbpremigration",
title: "Migrate Your Data",
description: "Pre-migration steps for moving data",
title: t(Keys.splashScreen.top3Items.mongo.migrate.title),
description: t(Keys.splashScreen.top3Items.mongo.migrate.description),
},
];
break;
@@ -562,18 +561,18 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
items = [
{
link: "https://aka.ms/cassandrajava",
title: "Build a Java App",
description: "Create a Java app using an SDK.",
title: t(Keys.splashScreen.top3Items.cassandra.buildJavaApp.title),
description: t(Keys.splashScreen.top3Items.cassandra.buildJavaApp.description),
},
{
link: "https://aka.ms/cassandrapartitioning",
title: "Partitioning Best Practices",
description: "Learn how partitioning works.",
title: t(Keys.splashScreen.top3Items.cassandra.partitioning.title),
description: t(Keys.splashScreen.top3Items.cassandra.partitioning.description),
},
{
link: "https://aka.ms/cassandraRu",
title: "Request Units (RUs)",
description: "Understand RU charges.",
title: t(Keys.splashScreen.top3Items.cassandra.requestUnits.title),
description: t(Keys.splashScreen.top3Items.cassandra.requestUnits.description),
},
];
break;
@@ -581,18 +580,18 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
items = [
{
link: "https://aka.ms/Graphdatamodeling",
title: "Data Modeling",
description: "Graph data modeling recommendations",
title: t(Keys.splashScreen.top3Items.gremlin.dataModeling.title),
description: t(Keys.splashScreen.top3Items.gremlin.dataModeling.description),
},
{
link: "https://aka.ms/graphpartitioning",
title: "Partitioning Best Practices",
description: "Learn how partitioning works",
title: t(Keys.splashScreen.top3Items.gremlin.partitioning.title),
description: t(Keys.splashScreen.top3Items.gremlin.partitioning.description),
},
{
link: "https://aka.ms/graphapiquery",
title: "Query Data",
description: "Querying data with Gremlin",
title: t(Keys.splashScreen.top3Items.gremlin.queryData.title),
description: t(Keys.splashScreen.top3Items.gremlin.queryData.description),
},
];
break;
@@ -600,18 +599,18 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
items = [
{
link: "https://aka.ms/tableintro",
title: "What is the Table API?",
description: "Understand Azure Cosmos DB for Table and its features",
title: t(Keys.splashScreen.top3Items.tables.whatIsTable.title),
description: t(Keys.splashScreen.top3Items.tables.whatIsTable.description),
},
{
link: "https://aka.ms/tableimport",
title: "Migrate your data",
description: "Learn how to migrate your data",
title: t(Keys.splashScreen.top3Items.tables.migrate.title),
description: t(Keys.splashScreen.top3Items.tables.migrate.description),
},
{
link: "https://aka.ms/tablefaq",
title: "Azure Cosmos DB for Table FAQs",
description: "Common questions about Azure Cosmos DB for Table",
title: t(Keys.splashScreen.top3Items.tables.faq.title),
description: t(Keys.splashScreen.top3Items.tables.faq.description),
},
];
break;
@@ -668,7 +667,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
</ul>
{recentItems.length > 0 && (
<Link onClick={() => clearMostRecent()} className={styles.listItemTitle}>
Clear Recents
{t(Keys.splashScreen.sections.clearRecents)}
</Link>
)}
</Stack>
@@ -683,15 +682,15 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
}
const cdbLiveTv: item = {
link: "https://developer.azurecosmosdb.com/tv",
title: "Learn the Fundamentals",
description: "Watch Azure Cosmos DB Live TV show introductory and how to videos.",
title: t(Keys.splashScreen.learningResources.liveTv.title),
description: t(Keys.splashScreen.learningResources.liveTv.description),
};
const commonItems: item[] = [
{
link: "https://learn.microsoft.com/azure/cosmos-db/data-explorer-shortcuts",
title: "Data Explorer keyboard shortcuts",
description: "Learn keyboard shortcuts to navigate Data Explorer.",
title: t(Keys.splashScreen.learningResources.shortcuts.title),
description: t(Keys.splashScreen.learningResources.shortcuts.description),
},
];
@@ -702,14 +701,14 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
apiItems = [
{
link: "https://aka.ms/msl-sdk-connect",
title: "Get Started using an SDK",
description: "Learn about the Azure Cosmos DB SDK.",
title: t(Keys.splashScreen.learningResources.sql.sdk.title),
description: t(Keys.splashScreen.learningResources.sql.sdk.description),
},
cdbLiveTv,
{
link: "https://aka.ms/msl-move-data",
title: "Migrate Your Data",
description: "Migrate data using Azure services and open-source solutions.",
title: t(Keys.splashScreen.learningResources.sql.migrate.title),
description: t(Keys.splashScreen.learningResources.sql.migrate.description),
},
];
break;
@@ -717,13 +716,13 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
apiItems = [
{
link: "https://aka.ms/mongonodejs",
title: "Build an app with Node.js",
description: "Create a Node.js app.",
title: t(Keys.splashScreen.learningResources.mongo.nodejs.title),
description: t(Keys.splashScreen.learningResources.mongo.nodejs.description),
},
{
link: "https://aka.ms/mongopython",
title: "Getting Started Guide",
description: "Learn the basics to get started.",
title: t(Keys.splashScreen.learningResources.mongo.gettingStarted.title),
description: t(Keys.splashScreen.learningResources.mongo.gettingStarted.description),
},
cdbLiveTv,
];
@@ -732,14 +731,14 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
apiItems = [
{
link: "https://aka.ms/cassandracontainer",
title: "Create a Container",
description: "Get to know the create a container options.",
title: t(Keys.splashScreen.learningResources.cassandra.createContainer.title),
description: t(Keys.splashScreen.learningResources.cassandra.createContainer.description),
},
cdbLiveTv,
{
link: "https://aka.ms/Cassandrathroughput",
title: "Provision Throughput",
description: "Learn how to configure throughput.",
title: t(Keys.splashScreen.learningResources.cassandra.throughput.title),
description: t(Keys.splashScreen.learningResources.cassandra.throughput.description),
},
];
break;
@@ -747,13 +746,13 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
apiItems = [
{
link: "https://aka.ms/graphquickstart",
title: "Get Started ",
description: "Create, query, and traverse using the Gremlin console",
title: t(Keys.splashScreen.learningResources.gremlin.getStarted.title),
description: t(Keys.splashScreen.learningResources.gremlin.getStarted.description),
},
{
link: "https://aka.ms/graphimport",
title: "Import Graph Data",
description: "Learn Bulk ingestion data using BulkExecutor",
title: t(Keys.splashScreen.learningResources.gremlin.importData.title),
description: t(Keys.splashScreen.learningResources.gremlin.importData.description),
},
cdbLiveTv,
];
@@ -762,13 +761,13 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
apiItems = [
{
link: "https://aka.ms/tabledotnet",
title: "Build a .NET App",
description: "How to access Azure Cosmos DB for Table from a .NET app.",
title: t(Keys.splashScreen.learningResources.tables.dotnet.title),
description: t(Keys.splashScreen.learningResources.tables.dotnet.description),
},
{
link: "https://aka.ms/Tablejava",
title: "Build a Java App",
description: "Create a Azure Cosmos DB for Table app with Java SDK ",
title: t(Keys.splashScreen.learningResources.tables.java.title),
description: t(Keys.splashScreen.learningResources.tables.java.description),
},
cdbLiveTv,
];
@@ -807,17 +806,17 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const postgresNextStepItems: { link: string; title: string; description: string }[] = [
{
link: "https://go.microsoft.com/fwlink/?linkid=2208312",
title: "Data Modeling",
title: t(Keys.splashScreen.nextStepItems.postgres.dataModeling),
description: "",
},
{
link: " https://go.microsoft.com/fwlink/?linkid=2206941 ",
title: "How to choose a Distribution Column",
title: t(Keys.splashScreen.nextStepItems.postgres.distributionColumn),
description: "",
},
{
link: "https://go.microsoft.com/fwlink/?linkid=2207425",
title: "Build Apps with Python/Java/Django",
title: t(Keys.splashScreen.nextStepItems.postgres.buildApps),
description: "",
},
];
@@ -825,17 +824,17 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const vcoreMongoNextStepItems: { link: string; title: string; description: string }[] = [
{
link: "https://learn.microsoft.com/azure/cosmos-db/mongodb/vcore/migration-options",
title: "Migrate Data",
title: t(Keys.splashScreen.nextStepItems.vcoreMongo.migrateData),
description: "",
},
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search-ai",
title: "Build AI apps with Vector Search",
title: t(Keys.splashScreen.nextStepItems.vcoreMongo.vectorSearch),
description: "",
},
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/tutorial-nodejs-web-app?tabs=github-codespaces",
title: "Build Apps with Nodejs",
title: t(Keys.splashScreen.nextStepItems.vcoreMongo.buildApps),
description: "",
},
];
@@ -863,17 +862,17 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const postgresLearnMoreItems: { link: string; title: string; description: string }[] = [
{
link: "https://go.microsoft.com/fwlink/?linkid=2207226",
title: "Performance Tuning",
title: t(Keys.splashScreen.learnMoreItems.postgres.performanceTuning),
description: "",
},
{
link: "https://go.microsoft.com/fwlink/?linkid=2208037",
title: "Useful Diagnostic Queries",
title: t(Keys.splashScreen.learnMoreItems.postgres.diagnosticQueries),
description: "",
},
{
link: "https://go.microsoft.com/fwlink/?linkid=2205270",
title: "Distributed SQL Reference",
title: t(Keys.splashScreen.learnMoreItems.postgres.sqlReference),
description: "",
},
];
@@ -881,17 +880,17 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const vcoreMongoLearnMoreItems: { link: string; title: string; description: string }[] = [
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search",
title: "Vector Search",
title: t(Keys.splashScreen.learnMoreItems.vcoreMongo.vectorSearch),
description: "",
},
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/how-to-create-text-index",
title: "Text Indexing",
title: t(Keys.splashScreen.learnMoreItems.vcoreMongo.textIndexing),
description: "",
},
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/troubleshoot-common-issues",
title: "Troubleshoot common issues",
title: t(Keys.splashScreen.learnMoreItems.vcoreMongo.troubleshoot),
description: "",
},
];
@@ -932,24 +931,25 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
persistentBeak
>
<TeachingBubbleContent
headline={`Start with sample ${getCollectionName().toLocaleLowerCase()}`}
headline={t(Keys.splashScreen.teachingBubble.coachMark.headline, {
collectionName: getCollectionName().toLocaleLowerCase(),
})}
hasCloseButton
closeButtonAriaLabel="Close"
closeButtonAriaLabel={t(Keys.common.close)}
primaryButtonProps={{
text: "Get started",
text: t(Keys.common.getStarted),
onClick: () => {
useCarousel.getState().setShowCoachMark(false);
container.onNewCollectionClicked({ isQuickstart: true });
},
}}
secondaryButtonProps={{
text: "Cancel",
text: t(Keys.common.cancel),
onClick: () => useCarousel.getState().setShowCoachMark(false),
}}
onDismiss={() => useCarousel.getState().setShowCoachMark(false)}
>
You will be guided to create a sample container with sample data, then we will give you a tour of data
explorer. You can also cancel launching this tour and explore yourself
{t(Keys.splashScreen.teachingBubble.coachMark.body)}
</TeachingBubbleContent>
</Coachmark>
)}
@@ -963,7 +963,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Next steps
{t(Keys.splashScreen.sections.nextSteps)}
</Text>
{getNextStepItems()}
</Stack>
@@ -975,7 +975,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Tips & learn more
{t(Keys.splashScreen.sections.tipsAndLearnMore)}
</Text>
{getTipsAndLearnMoreItems()}
</Stack>
@@ -984,15 +984,15 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
) : (
<div className={styles.moreStuffContainer}>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Recents</h2>
<h2 className={styles.columnTitle}>{t(Keys.splashScreen.sections.recents)}</h2>
{getRecentItems()}
</div>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Top 3 things you need to know</h2>
<h2 className={styles.columnTitle}>{t(Keys.splashScreen.sections.top3)}</h2>
{top3Items()}
</div>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Learning Resources</h2>
<h2 className={styles.columnTitle}>{t(Keys.splashScreen.sections.learningResources)}</h2>
{getLearningResourceItems()}
</div>
</div>

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
@@ -789,7 +792,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
);
let partitionKeyProperties = useMemo(() => {
return partitionKeyPropertyHeaders?.map((partitionKeyPropertyHeader) =>
partitionKeyPropertyHeader.replace(/[/]+/g, ".").substring(1).replace(/[']+/g, ""),
partitionKeyPropertyHeader.replace(/[/]+/g, ".").substring(1).replace(/[']+/g, "").replace(/["]+/g, ""),
);
}, [partitionKeyPropertyHeaders]);
@@ -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]);
@@ -1470,7 +1481,11 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const partitionKey = _partitionKey || (_collection && _collection.partitionKey);
const partitionKeyPropertyHeaders = _collection?.partitionKeyPropertyHeaders || partitionKey?.paths;
const partitionKeyProperties = partitionKeyPropertyHeaders?.map((partitionKeyPropertyHeader) =>
partitionKeyPropertyHeader.replace(/[/]+/g, ".").substring(1).replace(/[']+/g, ""),
partitionKeyPropertyHeader
.replace(/[/]+/g, ".")
.substring(1)
.replace(/[']+/g, "")
.replace(/["]+/g, ""),
);
return newDocumentId(rawDocument, partitionKeyProperties, partitionKeyValue);
@@ -1819,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,
@@ -1831,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");
}
@@ -1874,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,
{
@@ -1945,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,
{
@@ -2054,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],
@@ -2066,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 => {
@@ -2124,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,
});
}
@@ -2153,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"
@@ -2176,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
@@ -2223,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>
@@ -2243,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>
@@ -2255,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}
@@ -2263,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>
@@ -2272,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

@@ -1,3 +1,4 @@
import "./i18n";
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Arrow from "../images/Arrow.svg";

View File

@@ -0,0 +1,14 @@
{
"Projects": [
{
"LanguageSet": "Azure_LanguagesExt",
"LocItems": [
{
"SourceFile": "src\\Localization\\en\\Resources.json",
"CopyOption": "LangIDOnPath",
"OutputPath": "src\\Localization"
}
]
}
]
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Zrušit",
"close": "Zavřít",
"save": "Uložit",
"delete": "Odstranit",
"update": "Aktualizovat",
"discard": "Zahodit",
"execute": "Execute",
"loading": "Načítání",
"loadingEllipsis": "Načítání…",
"next": "Další",
"previous": "Předchozí",
"yes": "Ano",
"no": "Ne",
"result": "Výsledek",
"learnMore": "Další informace",
"getStarted": "Začínáme",
"retry": "Zkusit znovu",
"apply": "Použít",
"refresh": "Aktualizovat",
"copy": "Kopírovat",
"create": "Vytvořit",
"confirm": "Potvrdit",
"open": "Otevřít",
"rename": "Přejmenovat",
"download": "Stáhnout",
"upload": "Nahrát",
"connect": "Připojit",
"remove": "Odebrat",
"increaseValueBy1": "Zvýšit hodnotu o 1",
"decreaseValueBy1": "Snížit hodnotu o 1"
},
"splashScreen": {
"title": {
"default": "Vítá vás Azure Cosmos DB",
"postgres": "Vítá vás Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Vítá vás Azure DocumentDB (s kompatibilitou MongoDB)"
},
"subtitle": {
"default": "Globálně distribuovaná databázová služba s více modely pro libovolné škálování",
"getStarted": "Začněte s našimi ukázkovými datovými sadami, dokumentací a dalšími nástroji."
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "Připojení pomocí VS Code",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Abbrechen",
"close": "Schließen",
"save": "Speichern",
"delete": "Löschen",
"update": "Aktualisieren",
"discard": "Verwerfen",
"execute": "Ausführen",
"loading": "Wird geladen",
"loadingEllipsis": "Wird geladen...",
"next": "Weiter",
"previous": "Zurück",
"yes": "Ja",
"no": "Nein",
"result": "Ergebnis",
"learnMore": "Weitere Informationen",
"getStarted": "Erste Schritte",
"retry": "Wiederholen",
"apply": "Anwenden",
"refresh": "Aktualisieren",
"copy": "Kopieren",
"create": "Erstellen",
"confirm": "Bestätigen",
"open": "Öffnen",
"rename": "Umbenennen",
"download": "Herunterladen",
"upload": "Hochladen",
"connect": "Verbinden",
"remove": "Entfernen",
"increaseValueBy1": "Wert um 1 erhöhen",
"decreaseValueBy1": "Wert um 1 verringern"
},
"splashScreen": {
"title": {
"default": "Willkommen bei Azure Cosmos DB",
"postgres": "Willkommen bei Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Willkommen bei Azure DocumentDB (mit MongoDB-Kompatibilität)"
},
"subtitle": {
"default": "Global verteilter Datenbankdienst mit Unterstützung mehrerer Datenmodelle in jeder Größenordnung",
"getStarted": "Erste Schritte mit unseren Beispieldatensätzen, der Dokumentation und weiteren Tools."
},
"quickStart": {
"title": "Schnellstart starten",
"description": "Starten Sie ein Schnellstarttutorial, um mit Beispieldaten zu beginnen."
},
"newCollection": {
"title": "Neue {{collectionName}}",
"description": "Neuen Container für Speicher und Durchsatz erstellen"
},
"samplesGallery": {
"title": "Azure Cosmos DB-Beispielgalerie",
"description": "Entdecken Sie Beispiele, die skalierbare, intelligente App-Muster zeigen. Probieren Sie jetzt eines aus, um zu sehen, wie schnell Sie mit Cosmos DB vom Konzept zum Code gelangen."
},
"connectCard": {
"title": "Verbinden",
"description": "Bevorzugen Sie Ihre eigene Toolauswahl? Finden Sie die Verbindungszeichenfolge, die Sie für die Verbindung benötigen.",
"pgAdmin": {
"title": "Verbindung mit pgAdmin herstellen",
"description": "pgAdmin bevorzugen? Hier finden Sie Ihre Verbindungszeichenfolgen."
},
"vsCode": {
"title": "Mit VS Code verbinden",
"description": "Abfragen und Verwalten Ihrer MongoDB- und DocumentDB-Cluster in Visual Studio Code."
}
},
"shell": {
"postgres": {
"title": "PostgreSQL-Shell",
"description": "Erstellen Sie eine Tabelle und interagieren Sie mit Daten über die Shellschnittstelle von PostgreSQL."
},
"vcoreMongo": {
"title": "Mongo-Shell",
"description": "Erstellen Sie eine Sammlung und interagieren Sie mit Daten über die Shellschnittstelle von MongoDB."
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Neu bei Cosmos DB PGSQL?",
"body": "Willkommen! Wenn Sie neu bei Cosmos DB PGSQL sind und Hilfe beim Einstieg benötigen, finden Sie hier Beispieldaten und Abfragen."
},
"resetPassword": {
"headline": "Ihr Kennwort erstellen",
"body": "Wenn Sie Ihr Kennwort noch nicht geändert haben, ändern Sie es jetzt."
},
"coachMark": {
"headline": "Mit Beispiel „{{collectionName}}“ starten",
"body": "Sie werden angeleitet, einen Beispielcontainer mit Beispieldaten zu erstellen. Anschließend erhalten Sie eine Tour durch den Datenexplorer. Sie können die Tour auch abbrechen und selbst erkunden."
}
},
"sections": {
"recents": "Zuletzt verwendet",
"clearRecents": "Zuletzt verwendete Elemente löschen",
"top3": "Die drei wichtigsten Dinge, die Sie wissen sollten",
"learningResources": "Lernressourcen",
"nextSteps": "Nächste Schritte",
"tipsAndLearnMore": "Tipps und weitere Informationen",
"notebook": "Notebook",
"needHelp": "Benötigen Sie Hilfe?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Erweiterte Modellierungsmuster",
"description": "Erfahren Sie mehr über fortgeschrittene Strategien zur Optimierung Ihrer Datenbank."
},
"partitioning": {
"title": "Best Practices für die Partitionierung",
"description": "Lernen Sie, Datenmodell- und Partitionierungsstrategien anzuwenden."
},
"resourcePlanning": {
"title": "Ihre Ressourcenanforderungen planen",
"description": "Machen Sie sich mit den verschiedenen Konfigurationsmöglichkeiten vertraut."
}
},
"mongo": {
"whatIsMongo": {
"title": "Was ist die MongoDB-API?",
"description": "Verstehen Sie Azure Cosmos DB for MongoDB und seine Funktionen."
},
"features": {
"title": "Funktionen und Syntax",
"description": "Entdecken Sie die Vorteile und Funktionen"
},
"migrate": {
"title": "Ihre Daten migrieren",
"description": "Vorbereitende Schritte zur Datenmigration"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Java-App erstellen",
"description": "Java-App mithilfe eines SDK erstellen."
},
"partitioning": {
"title": "Best Practices für die Partitionierung",
"description": "Erfahren Sie, wie Partitionierung funktioniert."
},
"requestUnits": {
"title": "Anforderungseinheiten (RUs)",
"description": "RU-Gebühren verstehen"
}
},
"gremlin": {
"dataModeling": {
"title": "Datenmodellierung",
"description": "Empfehlungen zur Graphdatenmodellierung"
},
"partitioning": {
"title": "Best Practices für die Partitionierung",
"description": "Erfahren Sie, wie Partitionierung funktioniert"
},
"queryData": {
"title": "Daten abfragen",
"description": "Daten mit Gremlin abfragen"
}
},
"tables": {
"whatIsTable": {
"title": "Was ist die Table-API?",
"description": "Verstehen Sie Azure Cosmos DB for Table und seine Funktionen."
},
"migrate": {
"title": "Ihre Daten migrieren",
"description": "Erfahren Sie, wie Sie Ihre Daten migrieren."
},
"faq": {
"title": "Häufig gestellte Fragen zu Azure Cosmos DB for Table",
"description": "Häufige Fragen zu Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Tastenkombinationen im Datenexplorer",
"description": "Lernen Sie Tastenkombinationen zur Navigation im Datenexplorer."
},
"liveTv": {
"title": "Grundlagen erlernen",
"description": "Sehen Sie sich die Einführung und Anleitungsvideos der Azure Cosmos DB Live TV-Sendung an."
},
"sql": {
"sdk": {
"title": "Erste Schritte mit einem SDK",
"description": "Mehr über das Azure Cosmos DB SDK erfahren."
},
"migrate": {
"title": "Ihre Daten migrieren",
"description": "Migrieren Sie Daten mit Azure-Diensten und Open-Source-Lösungen."
}
},
"mongo": {
"nodejs": {
"title": "App mit Node.js erstellen",
"description": "Node.js-App erstellen."
},
"gettingStarted": {
"title": "Leitfaden für erste Schritte",
"description": "Lernen Sie die Grundlagen für den Einstieg kennen."
}
},
"cassandra": {
"createContainer": {
"title": "Container erstellen",
"description": "Erfahren Sie mehr über die Optionen zum Erstellen eines Containers."
},
"throughput": {
"title": "Durchsatz bereitstellen",
"description": "Erfahren Sie, wie Sie den Durchsatz konfigurieren."
}
},
"gremlin": {
"getStarted": {
"title": "Erste Schritte ",
"description": "Erstellen, Abfragen und Durchlaufen mithilfe der Gremlin-Konsole"
},
"importData": {
"title": "Graphdaten importieren",
"description": "Massendatenaufnahme mit BulkExecutor erlernen"
}
},
"tables": {
"dotnet": {
"title": ".NET-App erstellen",
"description": "So greifen Sie mit einer .NET-App auf Azure Cosmos DB for Table zu."
},
"java": {
"title": "Java-App erstellen",
"description": "Erstellen Sie eine Azure Cosmos DB for Table-App mit dem Java SDK. "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Datenmodellierung",
"distributionColumn": "So wählen Sie eine Verteilungsspalte aus.",
"buildApps": "Apps mit Python/Java/Django erstellen"
},
"vcoreMongo": {
"migrateData": "Daten migrieren",
"vectorSearch": "KI-Apps mit Vektorsuche erstellen",
"buildApps": "Apps mit Node.js erstellen"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Leistungsoptimierung",
"diagnosticQueries": "Nützliche Diagnoseabfragen",
"sqlReference": "Referenz für verteiltes SQL"
},
"vcoreMongo": {
"vectorSearch": "Vektorsuche",
"textIndexing": "Textindizierung",
"troubleshoot": "Häufige Probleme beheben"
}
},
"fabric": {
"buildTitle": "Ihre Datenbank erstellen",
"useTitle": "Ihre Datenbank nutzen",
"newContainer": {
"title": "Neuer Container",
"description": "Zielcontainer zum Speichern Ihrer Daten erstellen."
},
"sampleData": {
"title": "Beispieldaten",
"description": "Beispieldaten in Ihrer Datenbank laden"
},
"sampleVectorData": {
"title": "Beispielvektordaten",
"description": "Beispielvektordaten mit text-embedding-ada-002 laden"
},
"appDevelopment": {
"title": "App-Entwicklung",
"description": "Starten Sie hier, um ein SDK zum Erstellen Ihrer Apps zu verwenden."
},
"sampleGallery": {
"title": "Beispielgalerie",
"description": "End-to-End-Beispiele aus der Praxis ansehen"
}
},
"sampleDataDialog": {
"title": "Beispieldaten",
"startButton": "Starten",
"createPrompt": "Erstellen Sie einen Container „{{containerName}}“ und importieren Sie Beispieldaten. Dies kann einige Minuten dauern.",
"creatingContainer": "Container „{{containerName}}“ wird erstellt...",
"importingData": "Daten werden in „{{containerName}}“ importiert...",
"success": "„{{containerName}}“ wurde erfolgreich mit Beispieldaten erstellt.",
"errorContainerExists": "Der Container „{{containerName}}“ in der Datenbank „{{databaseName}}“ existiert bereits. Bitte löschen Sie ihn und versuchen Sie es erneut.",
"errorCreateContainer": "Fehler beim Erstellen des Containers: {{error}}",
"errorImportData": "Fehler beim Importieren von Daten: {{error}}"
}
}
}

View File

@@ -0,0 +1,727 @@
{
"common": {
"ok": "OK",
"cancel": "Cancel",
"close": "Close",
"save": "Save",
"delete": "Delete",
"update": "Update",
"discard": "Discard",
"execute": "Execute",
"loading": "Loading",
"loadingEllipsis": "Loading...",
"next": "Next",
"previous": "Previous",
"yes": "Yes",
"no": "No",
"result": "Result",
"learnMore": "Learn more",
"getStarted": "Get started",
"retry": "Retry",
"apply": "Apply",
"refresh": "Refresh",
"copy": "Copy",
"create": "Create",
"confirm": "Confirm",
"open": "Open",
"rename": "Rename",
"download": "Download",
"upload": "Upload",
"connect": "Connect",
"remove": "Remove",
"load": "Load",
"publish": "Publish",
"browse": "Browse",
"increaseValueBy1": "Increase value by 1",
"decreaseValueBy1": "Decrease value by 1"
},
"splashScreen": {
"title": {
"default": "Welcome to Azure Cosmos DB",
"postgres": "Welcome to Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Welcome to Azure DocumentDB (with MongoDB compatibility)"
},
"subtitle": {
"default": "Globally distributed, multi-model database service for any scale",
"getStarted": "Get started with our sample datasets, documentation, and additional tools."
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "Connect with VS Code",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"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"
}
},
"panes": {
"deleteDatabase": {
"panelTitle": "Delete {{databaseName}}",
"warningMessage": "Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.",
"confirmPrompt": "Confirm by typing the {{databaseName}} id (name)",
"inputMismatch": "Input {{databaseName}} name \"{{input}}\" does not match the selected {{databaseName}} \"{{selectedId}}\"",
"feedbackTitle": "Help us improve Azure Cosmos DB!",
"feedbackReason": "What is the reason why you are deleting this {{databaseName}}?"
},
"deleteCollection": {
"panelTitle": "Delete {{collectionName}}",
"confirmPrompt": "Confirm by typing the {{collectionName}} id",
"inputMismatch": "Input id {{input}} does not match the selected {{selectedId}}",
"feedbackTitle": "Help us improve Azure Cosmos DB!",
"feedbackReason": "What is the reason why you are deleting this {{collectionName}}?"
},
"addDatabase": {
"databaseLabel": "Database {{suffix}}",
"databaseIdLabel": "Database id",
"keyspaceIdLabel": "Keyspace id",
"databaseIdPlaceholder": "Type a new {{databaseLabel}} id",
"databaseTooltip": "A {{databaseLabel}} is a logical container of one or more {{collectionsLabel}}",
"shareThroughput": "Share throughput across {{collectionsLabel}}",
"shareThroughputTooltip": "Provisioned throughput at the {{databaseLabel}} level will be shared across all {{collectionsLabel}} within the {{databaseLabel}}.",
"greaterThanError": "Please enter a value greater than {{minValue}} for autopilot throughput",
"acknowledgeSpendError": "Please acknowledge the estimated {{period}} spend."
},
"addCollection": {
"createNew": "Create new",
"useExisting": "Use existing",
"databaseTooltip": "A database is analogous to a namespace. It is the unit of management for a set of {{collectionName}}.",
"shareThroughput": "Share throughput across {{collectionName}}",
"shareThroughputTooltip": "Throughput configured at the database level will be shared across all {{collectionName}} within the database.",
"collectionIdLabel": "{{collectionName}} id",
"collectionIdTooltip": "Unique identifier for the {{collectionName}} and used for id-based routing through REST and all SDKs.",
"collectionIdPlaceholder": "e.g., {{collectionName}}1",
"collectionIdAriaLabel": "{{collectionName}} id, Example {{collectionName}}1",
"existingDatabaseAriaLabel": "Choose existing {{databaseName}} id",
"existingDatabasePlaceholder": "Choose existing {{databaseName}} id",
"indexing": "Indexing",
"turnOnIndexing": "Turn on indexing",
"automatic": "Automatic",
"turnOffIndexing": "Turn off indexing",
"off": "Off",
"sharding": "Sharding",
"shardingTooltip": "Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data.",
"unsharded": "Unsharded",
"unshardedLabel": "Unsharded (20GB limit)",
"sharded": "Sharded",
"addPartitionKey": "Add hierarchical partition key",
"hierarchicalPartitionKeyInfo": "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.",
"provisionDedicatedThroughput": "Provision dedicated throughput for this {{collectionName}}",
"provisionDedicatedThroughputTooltip": "You can optionally provision dedicated throughput for a {{collectionName}} within a database that has throughput provisioned. This dedicated throughput amount will not be shared with other {{collectionNamePlural}} in the database and does not count towards the throughput you provisioned for the database. This throughput amount will be billed in addition to the throughput amount you provisioned at the database level.",
"uniqueKeysPlaceholderMongo": "Comma separated paths e.g. firstName,address.zipCode",
"uniqueKeysPlaceholderSql": "Comma separated paths e.g. /firstName,/address/zipCode",
"addUniqueKey": "Add unique key",
"enableAnalyticalStore": "Enable analytical store",
"disableAnalyticalStore": "Disable analytical store",
"on": "On",
"analyticalStoreSynapseLinkRequired": "Azure Synapse Link is required for creating an analytical store {{collectionName}}. Enable Synapse Link for this Cosmos DB account.",
"enable": "Enable",
"containerVectorPolicy": "Container Vector Policy",
"containerFullTextSearchPolicy": "Container Full Text Search Policy",
"advanced": "Advanced",
"mongoIndexingTooltip": "The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development.",
"createWildcardIndex": "Create a Wildcard Index on all fields",
"legacySdkCheckbox": "My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)",
"legacySdkInfo": "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.",
"indexingOnInfo": "All properties in your documents will be indexed by default for flexible and efficient queries.",
"indexingOffInfo": "Indexing will be turned off. Recommended if you don't need to run queries or only have key value operations.",
"indexingOffWarning": "By creating this container with indexing turned off, you will not be able to make any indexing policy changes. Indexing changes are only allowed on a container with a indexing policy.",
"acknowledgeSpendErrorMonthly": "Please acknowledge the estimated monthly spend.",
"acknowledgeSpendErrorDaily": "Please acknowledge the estimated daily spend.",
"unshardedMaxRuError": "Unsharded collections support up to 10,000 RUs",
"acknowledgeShareThroughputError": "Please acknowledge the estimated cost of this dedicated throughput.",
"vectorPolicyError": "Please fix errors in container vector policy",
"fullTextSearchPolicyError": "Please fix errors in container full text search policy",
"addingSampleDataSet": "Adding sample data set"
},
"addCollectionUtility": {
"shardKeyTooltip": "The shard key (field) is used to split your data across many replica sets (shards) to achieve unlimited scalability. It's critical to choose a field that will evenly distribute your data.",
"partitionKeyTooltip": "The {{partitionKeyName}} is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume.",
"partitionKeyTooltipSqlSuffix": " For small read-heavy workloads or write-heavy workloads of any size, id is often a good choice.",
"shardKeyLabel": "Shard key",
"partitionKeyLabel": "Partition key",
"shardKeyPlaceholder": "e.g., categoryId",
"partitionKeyPlaceholderDefault": "e.g., /address",
"partitionKeyPlaceholderFirst": "Required - first partition key e.g., /TenantId",
"partitionKeyPlaceholderSecond": "second partition key e.g., /UserId",
"partitionKeyPlaceholderThird": "third partition key e.g., /SessionId",
"partitionKeyPlaceholderGraph": "e.g., /address/zipCode",
"uniqueKeysTooltip": "Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key.",
"uniqueKeysLabel": "Unique keys",
"analyticalStoreLabel": "Analytical Store",
"analyticalStoreTooltip": "Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.",
"analyticalStoreDescription": "Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.",
"vectorPolicyTooltip": "Describe any properties in your data that contain vectors, so that they can be made available for similarity queries."
},
"settings": {
"pageOptions": "Page Options",
"pageOptionsDescription": "Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many query results per page.",
"queryResultsPerPage": "Query results per page",
"queryResultsPerPageTooltip": "Enter the number of query results that should be shown per page.",
"customQueryItemsPerPage": "Custom query items per page",
"custom": "Custom",
"unlimited": "Unlimited",
"entraIdRbac": "Enable Entra ID RBAC",
"entraIdRbacDescription": "Choose Automatic to enable Entra ID RBAC automatically. True/False to force enable/disable Entra ID RBAC.",
"true": "True",
"false": "False",
"regionSelection": "Region Selection",
"regionSelectionDescription": "Changes region the Cosmos Client uses to access account.",
"selectRegion": "Select Region",
"selectRegionTooltip": "Changes the account endpoint used to perform client operations.",
"globalDefault": "Global (Default)",
"readWrite": "(Read/Write)",
"read": "(Read)",
"queryTimeout": "Query Timeout",
"queryTimeoutDescription": "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.",
"enableQueryTimeout": "Enable query timeout",
"queryTimeoutMs": "Query timeout (ms)",
"automaticallyCancelQuery": "Automatically cancel query after timeout",
"ruLimit": "RU Limit",
"ruLimitDescription": "If a query exceeds a configured RU limit, the query will be aborted.",
"enableRuLimit": "Enable RU limit",
"ruLimitLabel": "RU Limit (RU)",
"defaultQueryResults": "Default Query Results View",
"defaultQueryResultsDescription": "Select the default view to use when displaying query results.",
"retrySettings": "Retry Settings",
"retrySettingsDescription": "Retry policy associated with throttled requests during CosmosDB queries.",
"maxRetryAttempts": "Max retry attempts",
"maxRetryAttemptsTooltip": "Max number of retries to be performed for a request. Default value 9.",
"fixedRetryInterval": "Fixed retry interval (ms)",
"fixedRetryIntervalTooltip": "Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned as part of the response. Default value is 0 milliseconds.",
"maxWaitTime": "Max wait time (s)",
"maxWaitTimeTooltip": "Max wait time in seconds to wait for a request while the retries are happening. Default value 30 seconds.",
"enableContainerPagination": "Enable container pagination",
"enableContainerPaginationDescription": "Load 50 containers at a time. Currently, containers are not pulled in alphanumeric order.",
"enableCrossPartitionQuery": "Enable cross-partition query",
"enableCrossPartitionQueryDescription": "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.",
"maxDegreeOfParallelism": "Max degree of parallelism",
"maxDegreeOfParallelismDescription": "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.",
"maxDegreeOfParallelismQuery": "Query up to the max degree of parallelism.",
"priorityLevel": "Priority Level",
"priorityLevelDescription": "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.",
"displayGremlinQueryResults": "Display Gremlin query results as:",
"displayGremlinQueryResultsDescription": "Select Graph to automatically visualize the query results as a Graph or JSON to display the results as JSON.",
"graph": "Graph",
"json": "JSON",
"graphAutoVisualization": "Graph Auto-visualization",
"enableSampleDatabase": "Enable sample database",
"enableSampleDatabaseDescription": "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.",
"enableSampleDbAriaLabel": "Enable sample db for query exploration",
"guidRepresentation": "Guid Representation",
"guidRepresentationDescription": "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.",
"advancedSettings": "Advanced Settings",
"ignorePartitionKey": "Ignore partition key on document update",
"ignorePartitionKeyTooltip": "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.",
"clearHistory": "Clear History",
"clearHistoryConfirm": "Are you sure you want to proceed?",
"clearHistoryDescription": "This action will clear the all customizations for this account in this browser, including:",
"clearHistoryTabLayout": "Reset your customized tab layout, including the splitter positions",
"clearHistoryTableColumns": "Erase your table column preferences, including any custom columns",
"clearHistoryFilters": "Clear your filter history",
"clearHistoryRegion": "Reset region selection to global",
"increaseValueBy1000": "Increase value by 1000",
"decreaseValueBy1000": "Decrease value by 1000",
"none": "None",
"low": "Low",
"high": "High",
"automatic": "Automatic",
"enhancedQueryControl": "Enhanced query control",
"enableQueryControl": "Enable query control",
"explorerVersion": "Explorer Version",
"accountId": "Account ID",
"sessionId": "Session ID",
"popupsDisabledError": "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",
"failedToAcquireTokenError": "Failed to acquire authorization token automatically. Please click on \"Login for Entra ID\" button to enable Entra ID RBAC operations"
},
"saveQuery": {
"panelTitle": "Save Query",
"setupCostMessage": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called \u201c{{databaseName}}\u201d. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.",
"completeSetup": "Complete setup",
"noQueryNameError": "No query name specified",
"invalidQueryContentError": "Invalid query content specified",
"failedToSaveQueryError": "Failed to save query {{queryName}}",
"failedToSetupContainerError": "Failed to setup a container for saved queries",
"accountNotSetupError": "Failed to save query: account not setup to save queries",
"name": "Name"
},
"loadQuery": {
"noFileSpecifiedError": "No file specified",
"failedToLoadQueryError": "Failed to load query",
"failedToLoadQueryFromFileError": "Failed to load query from file {{fileName}}",
"selectFilesToOpen": "Select a query document",
"browseFiles": "Browse"
},
"executeStoredProcedure": {
"enterInputParameters": "Enter input parameters (if any)",
"key": "Key",
"param": "Param",
"partitionKeyValue": "Partition key value",
"value": "Value",
"addNewParam": "Add New Param",
"addParam": "Add param",
"deleteParam": "Delete param",
"invalidParamError": "Invalid param specified: {{invalidParam}}",
"invalidParamConsoleError": "Invalid param specified: {{invalidParam}} is not a valid literal value",
"stringType": "String",
"customType": "Custom"
},
"uploadItems": {
"noFilesSpecifiedError": "No files were specified. Please input at least one file.",
"selectJsonFiles": "Select JSON Files",
"selectJsonFilesTooltip": "Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON documents. The combined size of all files in an individual upload operation must be less than 2 MB. You can perform multiple upload operations for larger data sets.",
"fileNameColumn": "FILE NAME",
"statusColumn": "STATUS",
"uploadStatus": "{{numSucceeded}} created, {{numThrottled}} throttled, {{numFailed}} errors",
"uploadedFiles": "Uploaded files"
},
"copyNotebook": {
"copyFailedError": "Failed to copy {{name}} to {{destination}}",
"uploadFailedError": "Failed to upload {{name}}",
"location": "Location",
"locationAriaLabel": "Location",
"selectLocation": "Select a notebook location to copy",
"name": "Name"
},
"publishNotebook": {
"publishFailedError": "Failed to publish {{notebookName}} to gallery",
"publishDescription": "When published, this notebook will appear in the Azure Cosmos DB notebooks public gallery. Make sure you have removed any sensitive data or output before publishing.",
"publishPrompt": "Would you like to publish and share \"{{name}}\" to the gallery?",
"coverImage": "Cover image",
"coverImageUrl": "Cover image url",
"name": "Name",
"description": "Description",
"tags": "Tags",
"tagsPlaceholder": "Optional tag 1, Optional tag 2",
"preview": "Preview",
"urlType": "URL",
"customImage": "Custom Image",
"takeScreenshot": "Take Screenshot",
"useFirstDisplayOutput": "Use First Display Output",
"failedToCaptureOutput": "Failed to capture first output",
"outputDoesNotExist": "Output does not exist for any of the cells.",
"failedToConvertError": "Failed to convert {{fileName}} to base64 format",
"failedToUploadError": "Failed to upload {{fileName}}"
},
"changePartitionKey": {
"failedToStartError": "Failed to start data transfer job",
"suboptimalPartitionKeyError": "Warning: The system has detected that your collection may be using a suboptimal partition key",
"description": "When changing a container\u2019s partition key, you will need to create a destination container with the correct partition key. You may also select an existing destination container.",
"sourceContainerId": "Source {{collectionName}} id",
"destinationContainerId": "Destination {{collectionName}} id",
"collectionIdTooltip": "Unique identifier for the {{collectionName}} and used for id-based routing through REST and all SDKs.",
"collectionIdPlaceholder": "e.g., {{collectionName}}1",
"collectionIdAriaLabel": "{{collectionName}} id, Example {{collectionName}}1",
"existingContainers": "Existing Containers",
"partitionKeyWarning": "The destination container must not already exist. Data Explorer will create a new destination container for you."
},
"cassandraAddCollection": {
"keyspaceLabel": "Keyspace name",
"keyspaceTooltip": "Select an existing keyspace or enter a new keyspace id.",
"tableIdLabel": "Enter CQL command to create the table.",
"enterTableId": "Enter table Id",
"tableSchemaAriaLabel": "Table schema",
"provisionDedicatedThroughput": "Provision dedicated throughput for this table",
"provisionDedicatedThroughputTooltip": "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."
},
"tables": {
"addProperty": "Add Property",
"addRow": "Add Row",
"addEntity": "Add Entity",
"back": "back",
"nullFieldsWarning": "Warning: Null fields will not be displayed for editing.",
"propertyEmptyError": "{{property}} cannot be empty. Please input a value for {{property}}",
"whitespaceError": "{{property}} cannot have whitespace. Please input a value for {{property}} without whitespace",
"propertyTypeEmptyError": "Property type cannot be empty. Please select a type from the dropdown for property {{property}}"
},
"tableQuerySelect": {
"selectColumns": "Select the columns that you want to query.",
"availableColumns": "Available Columns"
},
"tableColumnSelection": {
"selectColumns": "Select which columns to display in your view of items in your container.",
"searchFields": "Search fields",
"reset": "Reset",
"partitionKeySuffix": " (partition key)"
},
"newVertex": {
"addProperty": "Add Property"
},
"addGlobalSecondaryIndex": {
"globalSecondaryIndexId": "Global secondary index container id",
"globalSecondaryIndexIdPlaceholder": "e.g., indexbyEmailId",
"projectionQuery": "Projection query",
"projectionQueryPlaceholder": "SELECT c.email, c.accountId FROM c",
"projectionQueryTooltip": "Learn more about defining global secondary indexes.",
"disabledTitle": "A global secondary index is already being created. Please wait for it to complete before creating another one."
},
"stringInput": {
"inputMismatchError": "Input {{input}} does not match the selected {{selectedId}}"
},
"panelInfo": {
"information": "Information",
"moreDetails": "More details"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "Aceptar",
"cancel": "Cancelar",
"close": "Cerrar",
"save": "Guardar",
"delete": "Eliminar",
"update": "Actualizar",
"discard": "Descartar",
"execute": "Ejecutar",
"loading": "Cargando",
"loadingEllipsis": "Cargando…",
"next": "Siguiente",
"previous": "Anterior",
"yes": "Sí",
"no": "No",
"result": "Resultado",
"learnMore": "Más información",
"getStarted": "Comenzar",
"retry": "Reintentar",
"apply": "Aplicar",
"refresh": "Actualizar",
"copy": "Copiar",
"create": "Crear",
"confirm": "Confirmar",
"open": "Abrir",
"rename": "Cambiar nombre",
"download": "Descargar",
"upload": "Cargar",
"connect": "Conectar",
"remove": "Quitar",
"increaseValueBy1": "Aumentar valor en 1",
"decreaseValueBy1": "Disminuir valor en 1"
},
"splashScreen": {
"title": {
"default": "Le presentamos Azure Cosmos DB",
"postgres": "Le damos la bienvenida a Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Le damos la bienvenida a Azure DocumentDB (con compatibilidad con MongoDB)"
},
"subtitle": {
"default": "Servicio de base de datos multimodelo distribuido globalmente para cualquier escala",
"getStarted": "Introducción a nuestros conjuntos de datos de ejemplo, documentación y herramientas adicionales."
},
"quickStart": {
"title": "Inicio rápido",
"description": "Iniciar un tutorial de inicio rápido para empezar a trabajar con datos de ejemplo"
},
"newCollection": {
"title": "Nuevo elemento: {{collectionName}}",
"description": "Creación de un nuevo contenedor para el almacenamiento y el rendimiento"
},
"samplesGallery": {
"title": "Galería de ejemplos de Azure Cosmos DB",
"description": "Descubra ejemplos que muestran patrones de aplicaciones inteligentes y escalables. Pruebe una ahora para ver la rapidez con la que puede pasar del concepto al código con Cosmos DB"
},
"connectCard": {
"title": "Conectar",
"description": "¿Prefiere usar sus propias herramientas? Busque la cadena de conexión que necesita para conectarse.",
"pgAdmin": {
"title": "Conexión con pgAdmin",
"description": "¿Prefiere pgAdmin? Busque las cadenas de conexión aquí"
},
"vsCode": {
"title": "Conectar con VS Code",
"description": "Consulta y administración de los clústeres de MongoDB y DocumentDB en Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Creación de una tabla e interacción con datos mediante la interfaz de shell de PostgreSQL"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Creación de una colección e interacción con datos mediante la interfaz de shell de MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "¿Es nuevo en Cosmos DB PGSQL?",
"body": "Le damos la bienvenida Si no está familiarizado con Cosmos DB PGSQL y necesita ayuda para empezar, aquí puede encontrar datos de ejemplo y consultarlos."
},
"resetPassword": {
"headline": "Crear la contraseña",
"body": "Si aún no ha cambiado la contraseña, cámbiela ahora."
},
"coachMark": {
"headline": "Empezar con el ejemplo {{collectionName}}",
"body": "Se le guiará para crear un contenedor de ejemplo con datos de ejemplo y, a continuación, le ofreceremos un paseo por el explorador de datos. También puede cancelar la reserva de esta excursión y explorarla por su cuenta."
}
},
"sections": {
"recents": "Recientes",
"clearRecents": "Borrar recientes",
"top3": "3 cosas principales que debes saber",
"learningResources": "Recursos de aprendizaje",
"nextSteps": "Siguientes pasos",
"tipsAndLearnMore": "Sugerencias y más información",
"notebook": "Notebook",
"needHelp": "¿Necesita ayuda?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Patrones de modelado avanzados",
"description": "Obtenga información acerca de las estrategias avanzadas para optimizar la base de datos."
},
"partitioning": {
"title": "procedimientos recomendados de creación de particiones",
"description": "Aprenda a aplicar estrategias de creación de particiones y modelo de datos."
},
"resourcePlanning": {
"title": "Planear los requisitos de recursos",
"description": "Conozca las distintas opciones de configuración."
}
},
"mongo": {
"whatIsMongo": {
"title": "¿Qué es MongoDB API?",
"description": "Comprenda Azure Cosmos DB de MongoDB y sus características."
},
"features": {
"title": "Características y sintaxis",
"description": "Descubra las ventajas y características"
},
"migrate": {
"title": "Migrar los datos",
"description": "Pasos previos a la migración para mover datos"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Compilación de una aplicación java",
"description": "Cree una aplicación java mediante un SDK."
},
"partitioning": {
"title": "procedimientos recomendados de creación de particiones",
"description": "Obtenga información acerca de cómo funciona la creación de particiones."
},
"requestUnits": {
"title": "Unidades de solicitud (RU)",
"description": "Descripción de los cargos de RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Modelado de datos",
"description": "Recomendaciones de modelado de datos de grafos"
},
"partitioning": {
"title": "procedimientos recomendados de creación de particiones",
"description": "Obtenga información sobre cómo funciona la creación de particiones"
},
"queryData": {
"title": "Consultar datos",
"description": "Consulta de datos con Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "¿Cuál es el Table API?",
"description": "Descripción de Azure Cosmos DB para Table y sus características"
},
"migrate": {
"title": "Migrar los datos",
"description": "Obtenga información sobre cómo migrar sus datos"
},
"faq": {
"title": "Azure Cosmos DB para preguntas más frecuentes sobre tablas",
"description": "Preguntas comunes sobre Azure Cosmos DB para table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Explorador de datos métodos abreviados de teclado",
"description": "Obtenga información acerca de los métodos abreviados de teclado para navegar por Explorador de datos."
},
"liveTv": {
"title": "Conozca los aspectos básicos",
"description": "Vea Azure Cosmos DB introducción a programas de TV en vivo y vídeos de procedimientos."
},
"sql": {
"sdk": {
"title": "Introducción al uso de un SDK",
"description": "Obtenga información acerca del SDK de Azure Cosmos DB."
},
"migrate": {
"title": "Migrar los datos",
"description": "Migre datos mediante servicios de Azure y soluciones de código abierto."
}
},
"mongo": {
"nodejs": {
"title": "Compilación de una aplicación con Node.js",
"description": "Cree una aplicación Node.js."
},
"gettingStarted": {
"title": "Guía de introducción",
"description": "Conozca los conceptos básicos para empezar."
}
},
"cassandra": {
"createContainer": {
"title": "Crear un contenedor",
"description": "Conozca las opciones de creación de un contenedor."
},
"throughput": {
"title": "Aprovisionar rendimiento",
"description": "Aprenda a configurar el rendimiento."
}
},
"gremlin": {
"getStarted": {
"title": "Introducción ",
"description": "Creación, consulta y recorrido mediante la consola de Gremlin"
},
"importData": {
"title": "Importar datos del gráfico",
"description": "Información sobre los datos de ingesta masiva mediante BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Compilación de una aplicación .NET",
"description": "Cómo acceder a Azure Cosmos DB para Table desde una aplicación .NET."
},
"java": {
"title": "Compilación de una aplicación java",
"description": "Creación de una Azure Cosmos DB para la aplicación Table con el SDK de Java "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Modelado de datos",
"distributionColumn": "Cómo elegir una columna de distribución",
"buildApps": "Compilación de aplicaciones con Python, Java y Django"
},
"vcoreMongo": {
"migrateData": "Migrar datos",
"vectorSearch": "Creación de aplicaciones de inteligencia artificial con Vector Search",
"buildApps": "Compilación de aplicaciones con Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Ajuste de rendimiento",
"diagnosticQueries": "Consultas de diagnóstico útiles",
"sqlReference": "Referencia de SQL distribuida"
},
"vcoreMongo": {
"vectorSearch": "Vector de búsqueda",
"textIndexing": "Indización de texto",
"troubleshoot": "Solucionar problemas comunes"
}
},
"fabric": {
"buildTitle": "Compilación de la base de datos",
"useTitle": "Uso de la base de datos",
"newContainer": {
"title": "Nuevo contenedor",
"description": "Creación de un contenedor de destino para almacenar los datos"
},
"sampleData": {
"title": "Datos de ejemplo",
"description": "Carga de datos de ejemplo en la base de datos"
},
"sampleVectorData": {
"title": "Datos vectoriales de ejemplo",
"description": "Carga de datos vectoriales de ejemplo con text-embedding-ada-002"
},
"appDevelopment": {
"title": "Desarrollo de aplicaciones",
"description": "Empiece aquí para usar un SDK para compilar sus aplicaciones"
},
"sampleGallery": {
"title": "Galería de ejemplos",
"description": "Obtener ejemplos de un extremo a otro del mundo real"
}
},
"sampleDataDialog": {
"title": "Datos de ejemplo",
"startButton": "Inicio",
"createPrompt": "Cree un contenedor \"{{containerName}}\" e importe datos de ejemplo en él. Esto podría tardar unos minutos.",
"creatingContainer": "Creando contenedor \"{{containerName}}\"...",
"importingData": "Importando datos en \"{{containerName}}\"...",
"success": "Se ha creado correctamente \"{{containerName}}\" con datos de ejemplo.",
"errorContainerExists": "El contenedor \"{{containerName}}\" de la base de datos \"{{databaseName}}\" ya existe. Elimínelo y vuelva a intentarlo.",
"errorCreateContainer": "Error al crear el contenedor: {{error}}",
"errorImportData": "No se pudieron importar los datos: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Annuler",
"close": "Fermer",
"save": "Enregistrer",
"delete": "Supprimer",
"update": "Mettre à jour",
"discard": "Abandonner",
"execute": "Exécuter",
"loading": "Chargement",
"loadingEllipsis": "Chargement en cours...",
"next": "Suivant",
"previous": "Précédent",
"yes": "Oui",
"no": "Non",
"result": "Résultat",
"learnMore": "En savoir plus",
"getStarted": "Démarrage",
"retry": "Réessayer",
"apply": "Appliquer",
"refresh": "Actualiser",
"copy": "Copier",
"create": "Créer",
"confirm": "Confirmer",
"open": "Ouvrir",
"rename": "Renommer",
"download": "Télécharger",
"upload": "Charger",
"connect": "Connexion",
"remove": "Supprimer",
"increaseValueBy1": "Augmenter la valeur de 1",
"decreaseValueBy1": "Diminuer la valeur de 1"
},
"splashScreen": {
"title": {
"default": "Bienvenue dans Azure Cosmos DB",
"postgres": "Bienvenue à Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Bienvenue à Azure DocumentDB (avec compatibilité MongoDB)"
},
"subtitle": {
"default": "Service de base de données multimodèle, mondialement distribuée et disponible à toute échelle",
"getStarted": "Commencez avec nos jeux de données dexemple, notre documentation et nos outils supplémentaires."
},
"quickStart": {
"title": "Lancer le démarrage rapide",
"description": "Lancer un didacticiel de démarrage rapide pour prendre en main les données dexemple"
},
"newCollection": {
"title": "Nouvelle {{collectionName}}",
"description": "Créer un nouveau conteneur pour le stockage et le débit"
},
"samplesGallery": {
"title": "Galerie déchantillons Azure Cosmos DB",
"description": "Découvrez des exemples qui présentent des modèles dapplication évolutifs et intelligents. Essayez-en un tout de suite pour voir à quelle vitesse vous pouvez passer du concept au code grâce à Cosmos DB"
},
"connectCard": {
"title": "Connecter",
"description": "Vous préférez utiliser vos propres outils ? Trouvez la chaîne de connexion dont vous avez besoin pour vous connecter",
"pgAdmin": {
"title": "Se connecter avec pgAdmin",
"description": "Préférez-vous pgAdmin ? Trouvez vos chaînes de connexion ici"
},
"vsCode": {
"title": "Connecter avec VS Code",
"description": "Interroger et gérer vos clusters MongoDB et DocumentDB dans Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "Interpréteur de commandes PostgreSQL",
"description": "Créer un tableau et interagir avec les données à laide de linterface dinterpréteur PostgreSQL"
},
"vcoreMongo": {
"title": "Interpréteur de commandes Mongo",
"description": "Créer une collection et interagir avec les données à laide de linterface dinterpréteur MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Vous débutez avec Cosmos DB PGSQL ?",
"body": "Bienvenue ! Si vous débutez avec Cosmos DB PGSQL et avez besoin daide pour démarrer, voici où trouver des échantillons de données et des requêtes."
},
"resetPassword": {
"headline": "Créer votre mot de passe",
"body": "Si vous navez pas encore modifié votre mot de passe, faites-le maintenant."
},
"coachMark": {
"headline": "Commencer par lexemple {{collectionName}}",
"body": "Nous vous guiderons dans la création dun conteneur dexemple avec des échantillons de données, puis nous vous ferons visiter lexplorateur de données. Vous pouvez également annuler cette visite et explorer par vous-même"
}
},
"sections": {
"recents": "Récents",
"clearRecents": "Effacer les éléments récents",
"top3": "Les 3 principales choses à savoir",
"learningResources": "Ressources pédagogiques",
"nextSteps": "Étapes suivantes",
"tipsAndLearnMore": "Conseils et plus dinformations",
"notebook": "Bloc-notes",
"needHelp": "Avez-vous besoin daide ?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Motifs de modélisation avancés",
"description": "Découvrez des stratégies avancées pour optimiser votre base de données."
},
"partitioning": {
"title": "Les meilleures pratiques de partitionnement",
"description": "Apprenez à appliquer des stratégies de modélisation des données et de partitionnement."
},
"resourcePlanning": {
"title": "Planifier vos besoins en ressources",
"description": "Découvrez les différentes options de configuration."
}
},
"mongo": {
"whatIsMongo": {
"title": "Quest-ce que lAPI MongoDB ?",
"description": "Comprenez Azure Cosmos DB for MongoDB et ses fonctionnalités."
},
"features": {
"title": "Fonctionnalités et syntaxe",
"description": "Découvrir les avantages et les fonctionnalités"
},
"migrate": {
"title": "Migrer vos données",
"description": "Étapes pré-migration pour le déplacement des données"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Créer une application Java",
"description": "Créez une application Java avec un SDK."
},
"partitioning": {
"title": "Les meilleures pratiques de partitionnement",
"description": "Découvrez comment fonctionne le partitionnement."
},
"requestUnits": {
"title": "Unités de requêtes (RU)",
"description": "Comprendre les frais de RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Modélisation des données",
"description": "Recommandations pour la modélisation des données de Graph"
},
"partitioning": {
"title": "Les meilleures pratiques de partitionnement",
"description": "Découvrir comment fonctionne le partitionnement"
},
"queryData": {
"title": "Données de requête",
"description": "Interroger des données avec Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "Quest-ce que lAPI Table ?",
"description": "Comprendre Azure Cosmos DB for Table et ses fonctionnalités"
},
"migrate": {
"title": "Migrer vos données",
"description": "Découvrir comment migrer vos données"
},
"faq": {
"title": "FAQ Azure Cosmos DB for Table",
"description": "Questions courantes sur Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Raccourcis clavier pour Data Explorer",
"description": "Découvrez les raccourcis clavier pour naviguer dans Data Explorer."
},
"liveTv": {
"title": "Découvrir les principes de base",
"description": "Regardez lémission de télé en direct Azure Cosmos DB pour découvrir des vidéos dintroduction et des tutos."
},
"sql": {
"sdk": {
"title": "Prise en main dun SDK",
"description": "En savoir plus sur le SDK Azure Cosmos DB."
},
"migrate": {
"title": "Migrer vos données",
"description": "Migrez des données avec les services Azure et des solutions open source."
}
},
"mongo": {
"nodejs": {
"title": "Générer une application avec Node.js",
"description": "Créez une application Node.js."
},
"gettingStarted": {
"title": "Guide de prise en main",
"description": "Apprenez les notions de base pour démarrer."
}
},
"cassandra": {
"createContainer": {
"title": "Créer un conteneur",
"description": "Découvrez les options de création de conteneurs."
},
"throughput": {
"title": "Débit dapprovisionnement",
"description": "Découvrir comment configurer le débit."
}
},
"gremlin": {
"getStarted": {
"title": "Prise en main ",
"description": "Créer, interroger et parcourir avec la console Gremlin"
},
"importData": {
"title": "Importer les données de Graph",
"description": "Apprendre lingestion de données en bloc avec BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Créer une application .NET",
"description": "Comment accéder à Azure Cosmos DB for Table depuis une application .NET."
},
"java": {
"title": "Créer une application Java",
"description": "Créez une application Azure Cosmos DB for Table avec le SDK Java "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Modélisation des données",
"distributionColumn": "Comment choisir une colonne de distribution",
"buildApps": "Créer des applications avec Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrer des données",
"vectorSearch": "Créer des applications dIA avec la recherche vectorielle",
"buildApps": "Générer des applications avec Node.js"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Optimisation des performances",
"diagnosticQueries": "Requêtes de diagnostic utiles",
"sqlReference": "Référence SQL distribuée"
},
"vcoreMongo": {
"vectorSearch": "Recherche vectorielle",
"textIndexing": "Indexation de texte",
"troubleshoot": "Résoudre des problèmes courants"
}
},
"fabric": {
"buildTitle": "Générer votre base de données",
"useTitle": "Utiliser votre base de données",
"newContainer": {
"title": "Nouveau conteneur",
"description": "Créer un conteneur de destination pour stocker vos données"
},
"sampleData": {
"title": "Échantillons de données",
"description": "Charger des échantillons de données dans votre base de données"
},
"sampleVectorData": {
"title": "Échantillons de données vectorielles",
"description": "Charger des exemples de données vectorielles avec text-embedding-ada-002"
},
"appDevelopment": {
"title": "Développer des applications",
"description": "Commencer ici pour utiliser un SDK afin de créer vos applications"
},
"sampleGallery": {
"title": "Galerie déchantillons",
"description": "Obtenez des échantillons réels de bout en bout"
}
},
"sampleDataDialog": {
"title": "Échantillons de données",
"startButton": "Commencer",
"createPrompt": "Créez un conteneur « {{containerName}} » et importez-y des échantillons de données. Cela peut prendre quelques minutes.",
"creatingContainer": "Création en cours du conteneur « {{containerName}} »...",
"importingData": "Importation en cours de données dans « {{containerName}} »...",
"success": "Vous avez bien créé « {{containerName}} » avec des échantillons de données.",
"errorContainerExists": "Le conteneur « {{containerName}} » dans la base de données « {{databaseName}} » existe déjà. Supprimez-le, puis réessayez.",
"errorCreateContainer": "Nous navons pas pu créer le conteneur : {{error}}",
"errorImportData": "Nous navons pas pu importer les données : {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Mégse",
"close": "Bezárás",
"save": "Mentés",
"delete": "Törlés",
"update": "Frissítés",
"discard": "Elvetés",
"execute": "Execute",
"loading": "Betöltés folyamatban",
"loadingEllipsis": "Betöltés...",
"next": "Következő",
"previous": "Előző",
"yes": "Igen",
"no": "Nem",
"result": "Eredmény",
"learnMore": "További információ",
"getStarted": "Első lépések",
"retry": "Újrapróbálkozás",
"apply": "Alkalmaz",
"refresh": "Frissítés",
"copy": "Másolás",
"create": "Létrehozás",
"confirm": "Megerősítés",
"open": "Megnyitás",
"rename": "Átnevezés",
"download": "Letöltés",
"upload": "Feltöltés",
"connect": "Kapcsolódás",
"remove": "Eltávolítás",
"increaseValueBy1": "Érték növelése 1-gyel",
"decreaseValueBy1": "Érték csökkentése 1-gyel"
},
"splashScreen": {
"title": {
"default": "Üdvözli az Azure Cosmos DB",
"postgres": "Üdvözli az Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Üdvözli az Azure DocumentDB (MongoDB-kompatibilitással)"
},
"subtitle": {
"default": "Globálisan elosztott, többmodelles adatbázis-szolgáltatás bármilyen mérethez",
"getStarted": "Ismerje meg a minta adathalmazok, a dokumentáció és a további eszközök használatának első lépéseit."
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "Csatlakozás VS Code használatával",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Batal",
"close": "Tutup",
"save": "Simpan",
"delete": "Hapus",
"update": "Perbarui",
"discard": "Buang",
"execute": "Jalankan",
"loading": "Memuat",
"loadingEllipsis": "Memuat...",
"next": "Berikutnya",
"previous": "Sebelumnya",
"yes": "Ya",
"no": "Tidak",
"result": "Hasil",
"learnMore": "Pelajari selengkapnya",
"getStarted": "Mulai",
"retry": "Coba lagi",
"apply": "Terapkan",
"refresh": "Refresh",
"copy": "Salin",
"create": "Buat",
"confirm": "Konfirmasi",
"open": "Buka",
"rename": "Ganti nama",
"download": "Unduh",
"upload": "Unggah",
"connect": "Sambungkan",
"remove": "Hapus",
"increaseValueBy1": "Tambah nilai sebesar 1",
"decreaseValueBy1": "Kurangi nilai sebesar 1"
},
"splashScreen": {
"title": {
"default": "Selamat Datang di Azure Cosmos DB",
"postgres": "Selamat datang di Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Selamat datang di Azure DocumentDB (dengan kompatibilitas MongoDB)"
},
"subtitle": {
"default": "Layanan database multimodel yang didistribusikan secara global untuk skala apa saja",
"getStarted": "Mulai dengan himpunan data sampel, dokumentasi, dan alat tambahan kami."
},
"quickStart": {
"title": "Luncurkan mulai cepat",
"description": "Mulai tutorial cepat untuk memulai dengan data sampel"
},
"newCollection": {
"title": "{{collectionName}} baru",
"description": "Buat kontainer baru untuk penyimpanan dan throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Temukan sampel yang menampilkan pola aplikasi cerdas yang dapat diskalakan. Coba sekarang untuk melihat seberapa cepat Anda bisa beralih dari konsep ke kode dengan Cosmos DB"
},
"connectCard": {
"title": "Sambungkan",
"description": "Lebih suka menggunakan alat pilihan sendiri? Temukan string koneksi yang diperlukan untuk menyambung",
"pgAdmin": {
"title": "Sambungkan dengan pgAdmin",
"description": "Lebih suka pgAdmin? Temukan string koneksi di sini"
},
"vsCode": {
"title": "Sambungkan dengan Visual Studio Code",
"description": "Kueri dan kelola klaster MongoDB dan DocumentDB di Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Buat tabel dan interaksi dengan data menggunakan antarmuka shell PostgreSQL"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Buat koleksi dan interaksi dengan data menggunakan antarmuka shell MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Baru menggunakan Cosmos DB PGSQL?",
"body": "Selamat datang! Jika baru menggunakan Cosmos DB PGSQL dan butuh bantuan memulai, di sini Anda dapat menemukan data sampel dan kueri."
},
"resetPassword": {
"headline": "Buat kata sandi Anda",
"body": "Jika belum mengubah kata sandi, ubah sekarang."
},
"coachMark": {
"headline": "Mulai dengan sampel {{collectionName}}",
"body": "Anda akan dipandu membuat kontainer sampel dengan data sampel, lalu kami akan memberikan tur penjelajah data. Anda juga bisa membatalkan tur ini dan menjelajah sendiri"
}
},
"sections": {
"recents": "Terbaru",
"clearRecents": "Bersihkan Riwayat Terbaru",
"top3": "3 Hal Terpenting yang Perlu Diketahui",
"learningResources": "Sumber Daya Pembelajaran",
"nextSteps": "Langkah berikutnya",
"tipsAndLearnMore": "Tips & pelajari selengkapnya",
"notebook": "Buku catatan",
"needHelp": "Perlu bantuan?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Pola Pemodelan Lanjutan",
"description": "Pelajari strategi lanjutan untuk mengoptimalkan database."
},
"partitioning": {
"title": "Praktik Terbaik Pemartisian",
"description": "Pelajari cara menerapkan model data dan strategi pemartisian."
},
"resourcePlanning": {
"title": "Rencanakan Kebutuhan Sumber Daya Anda",
"description": "Kenali Berbagai Pilihan Konfigurasi."
}
},
"mongo": {
"whatIsMongo": {
"title": "Apa itu API MongoDB?",
"description": "Pahami Azure Cosmos DB for MongoDB dan fitur-fiturnya."
},
"features": {
"title": "Fitur dan Sintaks",
"description": "Temukan Keunggulan dan Fitur"
},
"migrate": {
"title": "Migrasikan Data",
"description": "Langkah Pra-migrasi untuk Memindahkan Data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Buat Aplikasi Java",
"description": "Buat aplikasi Java menggunakan SDK."
},
"partitioning": {
"title": "Praktik Terbaik Pemartisian",
"description": "Pelajari cara kerja pemartisian."
},
"requestUnits": {
"title": "Unit Permintaan (RU)",
"description": "Pahami Biaya RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Pemodelan Data",
"description": "Rekomendasi Pemodelan Data Grafik"
},
"partitioning": {
"title": "Praktik Terbaik Pemartisian",
"description": "Pelajari cara kerja partisi"
},
"queryData": {
"title": "Data Kueri",
"description": "Mengkueri data dengan Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "Apa itu Table API?",
"description": "Memahami Azure Cosmos DB for Table dan fiturnya"
},
"migrate": {
"title": "Migrasikan data Anda",
"description": "Pelajari cara memigrasikan data Anda"
},
"faq": {
"title": "Tanya Jawab Umum Azure Cosmos DB for Table",
"description": "Pertanyaan umum tentang Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Pintasan keyboard Data Explorer",
"description": "Pelajari pintasan keyboard untuk menavigasi Data Explorer."
},
"liveTv": {
"title": "Pelajari Dasar",
"description": "Tonton video perkenalan dan tutorial Azure Cosmos DB Live TV."
},
"sql": {
"sdk": {
"title": "Mulai menggunakan SDK",
"description": "Pelajari tentang Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrasikan Data",
"description": "Migrasikan data menggunakan layanan Azure dan solusi sumber terbuka."
}
},
"mongo": {
"nodejs": {
"title": "Buat aplikasi dengan Node.js",
"description": "Buat aplikasi Node.js."
},
"gettingStarted": {
"title": "Panduan Persiapan",
"description": "Pelajari dasar untuk memulai."
}
},
"cassandra": {
"createContainer": {
"title": "Buat Kontainer",
"description": "Kenali opsi buat kontainer."
},
"throughput": {
"title": "Throughput Penyediaan",
"description": "Pelajari cara mengonfigurasi throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Mulai ",
"description": "Buat, kueri, dan jelajahi menggunakan konsol Gremlin"
},
"importData": {
"title": "Impor Data Graf",
"description": "Pelajari penyerapan massal data menggunakan BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Buat Aplikasi .NET",
"description": "Cara mengakses Azure Cosmos DB for Table dari aplikasi .NET."
},
"java": {
"title": "Buat Aplikasi Java",
"description": "Buat aplikasi Azure Cosmos DB for Table dengan Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Pemodelan Data",
"distributionColumn": "Cara memilih Kolom Distribusi",
"buildApps": "Buat Aplikasi dengan Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrasikan Data",
"vectorSearch": "Buat aplikasi AI dengan Pencarian Vektor",
"buildApps": "Buat Aplikasi dengan Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Penyetelan Performa",
"diagnosticQueries": "Kueri Diagnostik yang Berguna",
"sqlReference": "Referensi SQL Terdistribusi"
},
"vcoreMongo": {
"vectorSearch": "Pencarian Vektor",
"textIndexing": "Pengindeksan Teks",
"troubleshoot": "Pecahkan masalah umum"
}
},
"fabric": {
"buildTitle": "Bangun database Anda",
"useTitle": "Gunakan database Anda",
"newContainer": {
"title": "Kontainer baru",
"description": "Buat kontainer tujuan untuk menyimpan data"
},
"sampleData": {
"title": "Data Sampel",
"description": "Muat data sampel ke database"
},
"sampleVectorData": {
"title": "Data Vektor Sampel",
"description": "Muat data vektor sampel dengan text-embedding-ada-002"
},
"appDevelopment": {
"title": "Pengembangan aplikasi",
"description": "Mulai di sini untuk menggunakan SDK membangun aplikasi"
},
"sampleGallery": {
"title": "Galeri Sampel",
"description": "Dapatkan sampel menyeluruh dunia nyata"
}
},
"sampleDataDialog": {
"title": "Data Sampel",
"startButton": "Mulai",
"createPrompt": "Buat kontainer \"{{containerName}}\" dan impor data sampel ke dalamnya. Proses ini memerlukan waktu beberapa menit.",
"creatingContainer": "Membuat kontainer \"{{containerName}}\"...",
"importingData": "Mengimpor data ke \"{{containerName}}\"...",
"success": "Berhasil membuat \"{{containerName}}\" dengan data sampel.",
"errorContainerExists": "Kontainer \"{{containerName}}\" di database \"{{databaseName}}\" sudah ada. Hapus dan coba lagi.",
"errorCreateContainer": "Gagal membuat kontainer: {{error}}",
"errorImportData": "Gagal mengimpor data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Annulla",
"close": "Chiudi",
"save": "Salva",
"delete": "Elimina",
"update": "Aggiorna",
"discard": "Rimuovi",
"execute": "Execute",
"loading": "Caricamento",
"loadingEllipsis": "Caricamento...",
"next": "Avanti",
"previous": "Indietro",
"yes": "Sì",
"no": "No",
"result": "Risultato",
"learnMore": "Altre informazioni",
"getStarted": "Attività iniziali",
"retry": "Riprova",
"apply": "Applica",
"refresh": "Aggiorna",
"copy": "Copia",
"create": "Crea",
"confirm": "Conferma",
"open": "Apri",
"rename": "Rinomina",
"download": "Scarica",
"upload": "Carica",
"connect": "Connetti",
"remove": "Rimuovi",
"increaseValueBy1": "Aumentare il valore di 1",
"decreaseValueBy1": "Diminuisci il valore di 1"
},
"splashScreen": {
"title": {
"default": "Benvenuto in Azure Cosmos DB",
"postgres": "Benvenuti in Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Benvenuti in Azure DocumentDB (con compatibilità MongoDB)"
},
"subtitle": {
"default": "Servizio database multimodello distribuito a livello globale a qualsiasi livello di scalabilità",
"getStarted": "Inizia con i nostri set di dati di esempio, la documentazione e gli strumenti aggiuntivi."
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "Connetti con VS Code",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "キャンセル",
"close": "閉じる",
"save": "保存",
"delete": "削除",
"update": "更新",
"discard": "破棄",
"execute": "Execute",
"loading": "読み込み中",
"loadingEllipsis": "読み込み中...",
"next": "次へ",
"previous": "前へ",
"yes": "はい",
"no": "いいえ",
"result": "結果",
"learnMore": "詳細情報",
"getStarted": "開始する",
"retry": "再試行",
"apply": "適用",
"refresh": "最新の情報に更新",
"copy": "コピー",
"create": "作成",
"confirm": "確認",
"open": "開く",
"rename": "名前の変更",
"download": "ダウンロード",
"upload": "アップロード",
"connect": "接続",
"remove": "削除",
"increaseValueBy1": "値を 1 増加",
"decreaseValueBy1": "値を 1 減少"
},
"splashScreen": {
"title": {
"default": "Azure Cosmos DB へようこそ",
"postgres": "Azure Cosmos DB for PostgreSQL へようこそ",
"vcoreMongo": "Azure DocumentDB (MongoDB 互換) へようこそ"
},
"subtitle": {
"default": "あらゆるスケールに対応するグローバル分散型のマルチモデル データベース サーバー",
"getStarted": "サンプル データセット、ドキュメント、追加ツールを使用して開始してください。"
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "VS Code で接続する",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "확인",
"cancel": "취소",
"close": "닫기",
"save": "저장",
"delete": "삭제",
"update": "업데이트",
"discard": "삭제",
"execute": "실행",
"loading": "로드 중",
"loadingEllipsis": "로드 중...",
"next": "다음",
"previous": "이전",
"yes": "예",
"no": "아니요",
"result": "결과",
"learnMore": "자세한 정보",
"getStarted": "시작",
"retry": "다시 시도",
"apply": "적용",
"refresh": "새로 고침",
"copy": "복사",
"create": "만들기",
"confirm": "확인",
"open": "열기",
"rename": "이름 바꾸기",
"download": "다운로드",
"upload": "업로드",
"connect": "연결",
"remove": "제거",
"increaseValueBy1": "값을 1만큼 늘리기",
"decreaseValueBy1": "값을 1만큼 줄이기"
},
"splashScreen": {
"title": {
"default": "Azure Cosmos DB 시작",
"postgres": "Azure Cosmos DB for PostgreSQL 시작",
"vcoreMongo": "Azure DocumentDB 시작(MongoDB 호환성 포함)"
},
"subtitle": {
"default": "모든 규모에 대해 전역적으로 분산된 다중 모델 데이터베이스 서비스",
"getStarted": "샘플 데이터 세트, 설명서 및 추가 도구를 시작하세요."
},
"quickStart": {
"title": "빠른 시작 개시",
"description": "빠른 시작 자습서를 시작하여 샘플 데이터 시작"
},
"newCollection": {
"title": "새 {{collectionName}}",
"description": "스토리지 및 처리량에 대한 새 컨테이너 만들기"
},
"samplesGallery": {
"title": "Azure Cosmos DB 샘플 갤러리",
"description": "확장성 있는 인텔리전트 앱 패턴을 보여 주는 샘플을 검색합니다. Cosmos DB 사용하여 개념에서 코드로 얼마나 빨리 이동할 수 있는지 확인해 보세요."
},
"connectCard": {
"title": "연결",
"description": "원하는 도구 사용을 선호하시나요? 연결해야 하는 연결 문자열 찾기",
"pgAdmin": {
"title": "pgAdmin으로 연결",
"description": "pgAdmin을 선호하시나요? 여기에서 연결 문자열 찾기"
},
"vsCode": {
"title": "VS Code와 연결",
"description": "Visual Studio Code MongoDB 및 DocumentDB 클러스터 쿼리 및 관리"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL 셸",
"description": "PostgreSQL의 셸 인터페이스를 사용하여 테이블 만들기 및 데이터 조작"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "MongoDB의 셸 인터페이스를 사용하여 컬렉션 만들기 및 데이터 조작"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "PGSQL을 Cosmos DB?",
"body": "환영합니다. PGSQL을 Cosmos DB 시작에 대한 도움이 필요한 경우 샘플 데이터, 쿼리를 찾을 수 있는 위치는 다음과 같습니다."
},
"resetPassword": {
"headline": "암호 만들기",
"body": "암호를 아직 변경하지 않은 경우 지금 변경하세요."
},
"coachMark": {
"headline": "샘플 {{collectionName}} 시작",
"body": "샘플 데이터를 사용하여 샘플 컨테이너를 만드는 과정을 안내한 다음 데이터 탐색기를 둘러보겠습니다. 이 둘러보기 시작을 취소하고 직접 탐색할 수도 있습니다."
}
},
"sections": {
"recents": "최근 항목",
"clearRecents": "최근 항목 지우기",
"top3": "꼭 알아야 할 3가지",
"learningResources": "학습 리소스",
"nextSteps": "다음 단계",
"tipsAndLearnMore": "팁 및 자세한 정보",
"notebook": "Notebook",
"needHelp": "도움이 필요하세요?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "고급 모델링 패턴",
"description": "데이터베이스를 최적화하기 위한 고급 전략을 알아봅니다."
},
"partitioning": {
"title": "분할 모범 사례",
"description": "데이터 모델 및 분할 전략을 적용하는 방법을 알아봅니다."
},
"resourcePlanning": {
"title": "리소스 요구 사항 계획",
"description": "다양한 구성 선택 사항에 대해 알아보세요."
}
},
"mongo": {
"whatIsMongo": {
"title": "MongoDB API란?",
"description": "MongoDB 및 해당 기능에 대한 Azure Cosmos DB 이해합니다."
},
"features": {
"title": "기능 및 구문",
"description": "장점 및 기능 살펴보기"
},
"migrate": {
"title": "데이터 마이그레이션",
"description": "데이터 이동을 위한 마이그레이션 전 단계"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Java 앱 빌드",
"description": "SDK를 사용하여 Java 앱을 만듭니다."
},
"partitioning": {
"title": "분할 모범 사례",
"description": "분할의 작동 방식을 알아봅니다."
},
"requestUnits": {
"title": "RU(요청 단위)",
"description": "RU 요금을 이해합니다."
}
},
"gremlin": {
"dataModeling": {
"title": "데이터 모델링",
"description": "그래프 데이터 모델링 권장 사항"
},
"partitioning": {
"title": "분할 모범 사례",
"description": "분할의 작동 방식 알아보기"
},
"queryData": {
"title": "데이터 쿼리",
"description": "Gremlin을 사용하여 데이터 쿼리"
}
},
"tables": {
"whatIsTable": {
"title": "Table API 무엇인가요?",
"description": "테이블 및 해당 기능에 대한 Azure Cosmos DB 이해"
},
"migrate": {
"title": "데이터 마이그레이션",
"description": "데이터 마이그레이션 방법 알아보기"
},
"faq": {
"title": "테이블 FAQ에 대한 Azure Cosmos DB",
"description": "테이블 Azure Cosmos DB 대한 일반적인 질문"
}
}
},
"learningResources": {
"shortcuts": {
"title": "바로 가기 키 데이터 탐색기",
"description": "Data Explorer를 탐색하는 키보드 단축키를 배워보세요."
},
"liveTv": {
"title": "기본 사항 알아보기",
"description": "Azure Cosmos DB Live TV 쇼 소개 및 비디오 방법을 시청하세요."
},
"sql": {
"sdk": {
"title": "SDK 사용 시작",
"description": "Azure Cosmos DB SDK에 대해 알아봅니다."
},
"migrate": {
"title": "데이터 마이그레이션",
"description": "Azure 서비스 및 오픈 소스 솔루션을 사용하여 데이터를 마이그레이션합니다."
}
},
"mongo": {
"nodejs": {
"title": "Node.js 사용하여 앱 빌드",
"description": "Node.js 앱을 만듭니다."
},
"gettingStarted": {
"title": "시작 가이드",
"description": "시작하는 데 필요한 기본 사항을 알아보세요."
}
},
"cassandra": {
"createContainer": {
"title": "컨테이너 만들기",
"description": "컨테이너 만들기 옵션을 알아보세요."
},
"throughput": {
"title": "처리량 프로비저닝",
"description": "처리량을 구성하는 방법을 알아봅니다."
}
},
"gremlin": {
"getStarted": {
"title": "시작 ",
"description": "Gremlin 콘솔을 사용하여 만들기, 쿼리 및 트래버스"
},
"importData": {
"title": "Graph 데이터 가져오기",
"description": "BulkExecutor를 사용하여 대량 수집 데이터 알아보기"
}
},
"tables": {
"dotnet": {
"title": ".NET 앱 만들기",
"description": ".NET 앱에서 Table용 Azure Cosmos DB에 액세스하는 방법입니다."
},
"java": {
"title": "Java 앱 빌드",
"description": "Java SDK를 사용하여 Table 앱에 대한 Azure Cosmos DB 만들기 "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "데이터 모델링",
"distributionColumn": "배포 열을 선택하는 방법",
"buildApps": "Python/Java/Django를 사용하여 앱 빌드"
},
"vcoreMongo": {
"migrateData": "데이터 마이그레이션",
"vectorSearch": "벡터 검색을 사용하여 AI 앱 빌드",
"buildApps": "Nodejs를 사용하여 앱 빌드"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "성능 튜닝",
"diagnosticQueries": "유용한 진단 쿼리",
"sqlReference": "분산 SQL 참조"
},
"vcoreMongo": {
"vectorSearch": "벡터 검색",
"textIndexing": "텍스트 인덱싱",
"troubleshoot": "일반적인 문제 해결"
}
},
"fabric": {
"buildTitle": "데이터베이스 빌드",
"useTitle": "데이터베이스 사용",
"newContainer": {
"title": "새 컨테이너",
"description": "데이터를 저장할 대상 컨테이너 만들기"
},
"sampleData": {
"title": "샘플 데이터",
"description": "데이터베이스에 샘플 데이터 로드"
},
"sampleVectorData": {
"title": "샘플 벡터 데이터",
"description": "text-embedding-ada-002를 사용하여 샘플 벡터 데이터 로드"
},
"appDevelopment": {
"title": "앱 개발",
"description": "SDK를 사용하여 앱을 빌드하려면 여기에서 시작하세요."
},
"sampleGallery": {
"title": "샘플 갤러리",
"description": "실제 엔드 투 엔드 샘플 가져오기"
}
},
"sampleDataDialog": {
"title": "샘플 데이터",
"startButton": "시작",
"createPrompt": "\"{{containerName}}\" 컨테이너를 만들고 샘플 데이터를 해당 컨테이너로 가져옵니다. 몇 분 정도 걸릴 수 있습니다.",
"creatingContainer": "컨테이너 \"{{containerName}}\"을(를) 만드는 중...",
"importingData": "\"{{containerName}}\"로 데이터를 가져오는 중...",
"success": "샘플 데이터를 사용하여 \"{{containerName}}\"을 만들었습니다.",
"errorContainerExists": "데이터베이스 \"{{databaseName}}\"의 컨테이너 \"{{containerName}}\"이(가) 이미 있습니다. 삭제하고 다시 시도하세요.",
"errorCreateContainer": "컨테이너를 만들지 못했습니다. {{error}}",
"errorImportData": "데이터를 가져오지 못했습니다. {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Annuleren",
"close": "Sluiten",
"save": "Opslaan",
"delete": "Verwijderen",
"update": "Bijwerken",
"discard": "Negeren",
"execute": "Execute",
"loading": "Laden",
"loadingEllipsis": "Laden...",
"next": "Volgende",
"previous": "Vorige",
"yes": "Ja",
"no": "Nee",
"result": "Resultaat",
"learnMore": "Meer informatie",
"getStarted": "Aan de slag",
"retry": "Opnieuw proberen",
"apply": "Toepassen",
"refresh": "Vernieuwen",
"copy": "Kopiëren",
"create": "Maken",
"confirm": "Bevestigen",
"open": "Open",
"rename": "Naam wijzigen",
"download": "Downloaden",
"upload": "Uploaden",
"connect": "Verbinding maken",
"remove": "Verwijderen",
"increaseValueBy1": "Waarde verhogen met 1",
"decreaseValueBy1": "Waarde verlagen met 1"
},
"splashScreen": {
"title": {
"default": "Welkom bij Azure Cosmos DB",
"postgres": "Welkom bij Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Welkom bij Azure DocumentDB (met MongoDB-compatibiliteit)"
},
"subtitle": {
"default": "Wereldwijd gedistribueerde, multi-modeldatabase-service voor elke schaalgrootte",
"getStarted": "Ga aan de slag met onze voorbeelddatasets, documentatie en extra tools."
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "Verbinding maken met VS Code",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Anuluj",
"close": "Zamknij",
"save": "Zapisz",
"delete": "Usuń",
"update": "Aktualizuj",
"discard": "Odrzuć",
"execute": "Wykonaj",
"loading": "Ładowanie",
"loadingEllipsis": "Trwa ładowanie...",
"next": "Dalej",
"previous": "Wstecz",
"yes": "Tak",
"no": "Nie",
"result": "Wynik",
"learnMore": "Dowiedz się więcej",
"getStarted": "Wprowadzenie",
"retry": "Ponów próbę",
"apply": "Zastosuj",
"refresh": "Odśwież",
"copy": "Kopiuj",
"create": "Utwórz",
"confirm": "Potwierdź",
"open": "Otwarte",
"rename": "Zmień nazwę",
"download": "Pobierz",
"upload": "Przekaż",
"connect": "Połącz",
"remove": "Usuń",
"increaseValueBy1": "Zwiększ wartość o 1",
"decreaseValueBy1": "Zmniejsz wartość o 1"
},
"splashScreen": {
"title": {
"default": "Azure Cosmos DB — Zapraszamy!",
"postgres": "Azure Cosmos DB for PostgreSQL — Zapraszamy!",
"vcoreMongo": "Witamy w usłudze Azure DocumentDB (ze zgodnością z bazą danych MongoDB)"
},
"subtitle": {
"default": "Globalnie rozproszona, wielomodelowa usługa bazy danych na dowolną skalę",
"getStarted": "Rozpocznij pracę z naszymi przykładowymi zestawami danych, dokumentacją i dodatkowymi narzędziami."
},
"quickStart": {
"title": "Uruchom przewodnik Szybki start",
"description": "Uruchom samouczek Szybki start, aby rozpocząć pracę z przykładowymi danymi"
},
"newCollection": {
"title": "Nowy element {{collectionName}}",
"description": "Utwórz nowy kontener na potrzeby magazynu i przepływności"
},
"samplesGallery": {
"title": "Galeria przykładów usługi Azure Cosmos DB",
"description": "Odkryj przykłady przedstawiające skalowalne, inteligentne wzorce aplikacji. Wypróbuj ją teraz, aby zobaczyć, jak szybko możesz przejść od koncepcji do kodu za pomocą usługi Cosmos DB"
},
"connectCard": {
"title": "Połącz",
"description": "Wolisz korzystać z własnych narzędzi? Znajdź parametr połączenia potrzebny do nawiązania połączenia",
"pgAdmin": {
"title": "Nawiąż połączenie za pomocą narzędzia pgAdmin",
"description": "Wolisz pgAdmin? Tutaj znajdziesz parametry połączenia"
},
"vsCode": {
"title": "Nawiązywanie połączenia z edytorem VS Code",
"description": "Wykonuj zapytania dotyczące klastrów MongoDB i DocumentDB oraz zarządzaj nimi w programie Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "Powłoka PostgreSQL",
"description": "Utwórz tabelę i pracuj z danymi za pomocą powłoki interfejsu PostgreSQL"
},
"vcoreMongo": {
"title": "Powłoka Mongo",
"description": "Utwórz kolekcję i pracuj z danymi przy użyciu interfejsu powłoki bazy danych MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Jesteś nowym użytkownikiem usługi Cosmos DB PGSQL?",
"body": "Witaj! Jeśli dopiero zaczynasz pracę z Cosmos DB PGSQL i potrzebujesz pomocy przy rozpoczynaniu pracy, tutaj znajdziesz przykładowe dane i zapytania."
},
"resetPassword": {
"headline": "Utwórz hasło",
"body": "Jeśli hasło nie zostało jeszcze zmienione, zmień je teraz."
},
"coachMark": {
"headline": "Rozpocznij od przykładu {{collectionName}}",
"body": "Zostanie wyświetlony przewodnik, aby utworzyć przykładowy kontener z przykładowymi danymi, a następnie udostępnimy Ci przewodnik po eksploratorze danych. Możesz również anulować uruchamianie tego przewodnika i eksplorować siebie"
}
},
"sections": {
"recents": "Ostatnie",
"clearRecents": "Wyczyść ostatnie",
"top3": "3 najważniejsze rzeczy, które musisz wiedzieć",
"learningResources": "Zasoby szkoleniowe",
"nextSteps": "Następne kroki",
"tipsAndLearnMore": "Porady i dowiedz się więcej",
"notebook": "Notes",
"needHelp": "Potrzebujesz pomocy?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Zaawansowane wzorce modelowania",
"description": "Poznaj zaawansowane strategie optymalizowania bazy danych."
},
"partitioning": {
"title": "Najlepsze rozwiązania dotyczące partycjonowania",
"description": "Dowiedz się, jak stosować model danych i strategie partycjonowania."
},
"resourcePlanning": {
"title": "Planowanie wymagań dotyczących zasobów",
"description": "Poznaj różne opcje konfiguracji."
}
},
"mongo": {
"whatIsMongo": {
"title": "Co to jest interfejs API bazy danych MongoDB?",
"description": "Omówienie usługi Azure Cosmos DB dla bazy danych MongoDB i jej funkcji."
},
"features": {
"title": "Funkcje i składnia",
"description": "Odkryj zalety i funkcje"
},
"migrate": {
"title": "Migrowanie danych",
"description": "Kroki przed migracją na potrzeby przenoszenia danych"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Utwórz aplikację Java",
"description": "Utwórz aplikację Java przy użyciu zestawu SDK."
},
"partitioning": {
"title": "Najlepsze rozwiązania dotyczące partycjonowania",
"description": "Dowiedz się, jak działa partycjonowanie."
},
"requestUnits": {
"title": "Jednostki żądania (jednostki RU)",
"description": "Informacje o opłatach za jednostki RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Modelowanie danych",
"description": "Zalecenia dotyczące modelowania danych programu Graph"
},
"partitioning": {
"title": "najlepszymi rozwiązaniami dotyczącymi partycjonowania",
"description": "Dowiedz się, jak działa partycjonowanie"
},
"queryData": {
"title": "Wykonaj zapytanie dotyczące danych",
"description": "Wykonywanie zapytań o dane przy użyciu języka Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "Co to jest interfejs API tabel?",
"description": "Omówienie usługi Azure Cosmos DB for Table i jej funkcji"
},
"migrate": {
"title": "Migrowanie danych",
"description": "Dowiedz się, jak migrować dane"
},
"faq": {
"title": "Azure Cosmos DB for Table — często zadawane pytania",
"description": "Typowe pytania dotyczące usługi Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Skróty klawiaturowe w Eksploratorze danych",
"description": "Poznaj skróty klawiaturowe umożliwiające nawigowanie w Eksploratorze danych."
},
"liveTv": {
"title": "Poznaj podstawy",
"description": "Obejrzyj wprowadzenie do programu telewizyjnego na żywo w usłudze Azure Cosmos DB i obejrzyj wideo."
},
"sql": {
"sdk": {
"title": "Wprowadzenie do korzystania z zestawu SDK",
"description": "Dowiedz się więcej o zestawie SDK usługi Azure Cosmos DB."
},
"migrate": {
"title": "Migrowanie danych",
"description": "Migruj dane przy użyciu usług platformy Azure i rozwiązań typu open source."
}
},
"mongo": {
"nodejs": {
"title": "Tworzenie aplikacji za pomocą Node.js",
"description": "Utwórz aplikację Node.js."
},
"gettingStarted": {
"title": "Przewodnik Wprowadzenie",
"description": "Zapoznaj się z podstawowymi informacjami dotyczącymi rozpoczynania pracy."
}
},
"cassandra": {
"createContainer": {
"title": "Utwórz kontener",
"description": "Zapoznaj się z opcjami tworzenia kontenera."
},
"throughput": {
"title": "Zaaprowizuj przepływność",
"description": "Dowiedz się, jak skonfigurować przepływność."
}
},
"gremlin": {
"getStarted": {
"title": "Wprowadzenie ",
"description": "Twórz, wykonuj zapytania i przechodź przy użyciu konsoli Gremlin"
},
"importData": {
"title": "Importuj dane wykresu",
"description": "Dowiedz się więcej o zbiorczym pozyskiwaniu danych przy użyciu narzędzia BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Utwórz aplikację platformy .NET",
"description": "Jak uzyskać dostęp do usługi Azure Cosmos DB for Table z poziomu aplikacji platformy .NET."
},
"java": {
"title": "Utwórz aplikację Java",
"description": "Utwórz aplikację usługi Azure Cosmos DB for Table przy użyciu zestawu Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Modelowanie danych",
"distributionColumn": "Jak wybrać kolumnę dystrybucji",
"buildApps": "Twórz aplikacje przy użyciu języka Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migruj dane",
"vectorSearch": "Twórz aplikacje AI za pomocą funkcji wyszukiwania wektorowego",
"buildApps": "Twórz aplikacje za pomocą środowiska Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Dostrajanie wydajności",
"diagnosticQueries": "Przydatne zapytania diagnostyczne",
"sqlReference": "Rozproszona dokumentacja SQL"
},
"vcoreMongo": {
"vectorSearch": "Wyszukiwanie wektorowe",
"textIndexing": "Indeksowanie tekstu",
"troubleshoot": "Rozwiązywanie typowych problemów"
}
},
"fabric": {
"buildTitle": "Utwórz bazę danych",
"useTitle": "Wykonaj „diff” dla swojej bazy danych",
"newContainer": {
"title": "Nowy kontener",
"description": "Utwórz kontener docelowy do przechowywania danych"
},
"sampleData": {
"title": "Dane przykładowe",
"description": "Załaduj przykładowe dane w bazie danych"
},
"sampleVectorData": {
"title": "Przykładowe dane wektorowe",
"description": "Załaduj przykładowe dane wektorowe za pomocą polecenia text-embedding-ada-002"
},
"appDevelopment": {
"title": "Opracowywanie aplikacji",
"description": "Zacznij tutaj, aby używać zestawu SDK do kompilowania aplikacji"
},
"sampleGallery": {
"title": "Galeria przykładów",
"description": "Uzyskaj rzeczywiste, kompleksowe przykłady"
}
},
"sampleDataDialog": {
"title": "Dane przykładowe",
"startButton": "Start",
"createPrompt": "Utwórz kontener „{{containerName}}” i zaimportuj do niego przykładowe dane. Może to potrwać kilka minut.",
"creatingContainer": "Tworzenie kontenera „{{containerName}}”...",
"importingData": "Trwa importowanie danych do folderu „{{containerName}}”...",
"success": "Pomyślnie utworzono „{{containerName}}” z przykładowymi danymi.",
"errorContainerExists": "Kontener „{{containerName}}” w bazie danych „{{databaseName}}” już istnieje. Usuń go i spróbuj ponownie.",
"errorCreateContainer": "Nie można utworzyć kontenera: {{error}}",
"errorImportData": "Nie można zaimportować danych: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Cancelar",
"close": "Fechar",
"save": "Salvar",
"delete": "Excluir",
"update": "Atualizar",
"discard": "Descartar",
"execute": "Executar",
"loading": "Carregando",
"loadingEllipsis": "Carregando...",
"next": "Avançar",
"previous": "Anterior",
"yes": "Sim",
"no": "Não",
"result": "Resultado",
"learnMore": "Saiba mais",
"getStarted": "Introdução",
"retry": "Repetir",
"apply": "Aplicar",
"refresh": "Atualizar",
"copy": "Copiar",
"create": "Criar",
"confirm": "Confirmar",
"open": "Aberto",
"rename": "Renomear",
"download": "Baixar",
"upload": "Carregar",
"connect": "Conectar",
"remove": "Remover",
"increaseValueBy1": "Aumentar o valor em 1",
"decreaseValueBy1": "Diminuir valor em 1"
},
"splashScreen": {
"title": {
"default": "Bem-vindo ao Azure Cosmos DB",
"postgres": "Bem-vindo ao Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Bem-vindo ao Azure DocumentDB (com compatibilidade com MongoDB)"
},
"subtitle": {
"default": "Serviço de multimodelo de banco de dados globalmente distribuído para qualquer escala",
"getStarted": "Comece com nossos conjuntos de dados de exemplo, documentação e ferramentas adicionais."
},
"quickStart": {
"title": "Iniciar o início rápido",
"description": "Iniciar um tutorial de início rápido para começar a usar os dados de exemplo"
},
"newCollection": {
"title": "Novo {{collectionName}}",
"description": "Crie um novo contêiner para armazenamento e taxa de transferência"
},
"samplesGallery": {
"title": "Galeria de exemplos do Azure Cosmos DB",
"description": "Descubra exemplos que demonstram padrões de aplicativos inteligentes e escalonáveis. Experimente um agora para ver a rapidez com que você pode passar do conceito para o código com o Cosmos DB"
},
"connectCard": {
"title": "Conectar",
"description": "Prefere usar suas próprias ferramentas? Localize a cadeia de conexão que você precisa para conectar",
"pgAdmin": {
"title": "Conectar com o pgAdmin",
"description": "Prefere pgAdmin? Encontre suas cadeias de conexão aqui"
},
"vsCode": {
"title": "Conectar-se com o VS Code",
"description": "Consultar e gerenciar seus clusters do MongoDB e do DocumentDB no Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "Shell do PostgreSQL",
"description": "Criar tabela e interagir com dados usando a interface de shell do PostgreSQL"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Criar uma coleção e interagir com os dados usando a interface de shell do MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Novo no Cosmos DB PGSQL?",
"body": "Bem-vindo(a)! Se você é novo no Cosmos DB PGSQL e precisa de ajuda para começar, aqui é onde você pode encontrar dados de exemplo e consultas."
},
"resetPassword": {
"headline": "Criar a sua senha",
"body": "Se você ainda não alterou sua senha, altere-a agora."
},
"coachMark": {
"headline": "Comece com o exemplo {{collectionName}}",
"body": "Você será guiado a criar um contêiner de exemplo com dados de exemplo e, em seguida, faremos um tour pelo Data Explorer. Você também pode cancelar o tour e explorar por conta própria"
}
},
"sections": {
"recents": "Recentes",
"clearRecents": "Limpar Recentes",
"top3": "As três principais coisas que você precisa saber",
"learningResources": "Recursos de aprendizado",
"nextSteps": "Próximas etapas",
"tipsAndLearnMore": "Dicas e saiba mais",
"notebook": "Notebook",
"needHelp": "Precisa de ajuda?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Padrões avançados de modelagem",
"description": "Aprenda estratégias avançadas para otimizar seu banco de dados."
},
"partitioning": {
"title": "Melhores práticas de particionamento",
"description": "Aprenda a aplicar estratégias de particionamento e modelo de dados."
},
"resourcePlanning": {
"title": "Planejar seus requisitos de recursos",
"description": "Conheça as diferentes opções de configuração."
}
},
"mongo": {
"whatIsMongo": {
"title": "O que é a API do MongoDB?",
"description": "Entenda o Azure Cosmos DB for MongoDB e seus recursos."
},
"features": {
"title": "Recursos e sintaxe",
"description": "Descubra as vantagens e os recursos"
},
"migrate": {
"title": "Migrar seus dados",
"description": "Etapas de pré-migração para mover dados"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Criar um aplicativo Java",
"description": "Crie um aplicativo Java usando um SDK."
},
"partitioning": {
"title": "Melhores práticas de particionamento",
"description": "Saiba como funciona o particionamento."
},
"requestUnits": {
"title": "RUs (Unidades de Solicitação)",
"description": "Entenda os encargos de RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Modelagem de Dados",
"description": "Recomendações de modelagem de dados do Graph"
},
"partitioning": {
"title": "Melhores práticas de particionamento",
"description": "Saiba como funciona o particionamento"
},
"queryData": {
"title": "Consultar dados",
"description": "Consultando dados com Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "Qual é o API de Tabela?",
"description": "Entenda o Azure Cosmos DB for Table e seus recursos"
},
"migrate": {
"title": "Migrar seus dados",
"description": "Saiba como migrar seus dados"
},
"faq": {
"title": "Perguntas frequentes do Azure Cosmos DB for Table",
"description": "Perguntas comuns sobre o Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Atalhos de teclado do Data Explorer",
"description": "Aprenda atalhos de teclado para navegar no Data Explorer."
},
"liveTv": {
"title": "Aprender os Conceitos Básicos",
"description": "Assista aos vídeos introdutórios e tutoriais do programa de TV ao vivo do Azure Cosmos DB."
},
"sql": {
"sdk": {
"title": "Introdução ao uso de um SDK",
"description": "Saiba mais sobre o SDK do Azure Cosmos DB."
},
"migrate": {
"title": "Migrar seus dados",
"description": "Migre dados usando serviços do Azure e soluções de código aberto."
}
},
"mongo": {
"nodejs": {
"title": "Criar um aplicativo com Node.js",
"description": "Crie um aplicativo Node.js."
},
"gettingStarted": {
"title": "Guia de Introdução",
"description": "Aprenda as noções básicas para começar."
}
},
"cassandra": {
"createContainer": {
"title": "Criar um Contêiner",
"description": "Conheça as opções de criação de um contêiner."
},
"throughput": {
"title": "Provisionar Taxa de Transferência",
"description": "Saiba como configurar a taxa de transferência."
}
},
"gremlin": {
"getStarted": {
"title": "Introdução ",
"description": "Crie, consulte e navegue usando o console do Gremlin"
},
"importData": {
"title": "Importar Dados do Graph",
"description": "Aprenda sobre ingestão em massa usando o BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Criar um aplicativo .NET",
"description": "Como acessar o Azure Cosmos DB for Table a partir de um aplicativo .NET."
},
"java": {
"title": "Criar um aplicativo Java",
"description": "Crie um aplicativo Azure Cosmos DB for Table com o SDK Java "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Modelagem de Dados",
"distributionColumn": "Como escolher uma Coluna de Distribuição",
"buildApps": "Criar aplicativos com Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrar Dados",
"vectorSearch": "Crie aplicativos de IA com Busca em vetores",
"buildApps": "Criar aplicativos com Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Ajuste de Desempenho",
"diagnosticQueries": "Consultas úteis para diagnóstico",
"sqlReference": "Referência de SQL distribuído"
},
"vcoreMongo": {
"vectorSearch": "Busca em Vetores",
"textIndexing": "Indexação de Texto",
"troubleshoot": "Solucionar problemas comuns"
}
},
"fabric": {
"buildTitle": "Criar seu banco de dados",
"useTitle": "Usar seu banco de dados",
"newContainer": {
"title": "Novo contêiner",
"description": "Criar um contêiner de destino para armazenar seus dados"
},
"sampleData": {
"title": "Dados de Exemplo",
"description": "Carregar dados de exemplo em seu banco de dados"
},
"sampleVectorData": {
"title": "Dados vetoriais de exemplo",
"description": "Carregar dados vetoriais de exemplo com text-embedding-ada-002"
},
"appDevelopment": {
"title": "Desenvolvimento de aplicativos",
"description": "Comece aqui para usar um SDK para criar seus aplicativos"
},
"sampleGallery": {
"title": "Galeria de exemplos",
"description": "Obtenha exemplos completos do mundo real"
}
},
"sampleDataDialog": {
"title": "Dados de Exemplo",
"startButton": "Iniciar",
"createPrompt": "Crie um contêiner \"{{containerName}}\" e importe dados de exemplo para ele. Isso pode levar alguns minutos.",
"creatingContainer": "Criando contêiner \"{{containerName}}\"...",
"importingData": "Importando dados para \"{{containerName}}\"...",
"success": "\"{{containerName}}\" criado com sucesso com dados de exemplo.",
"errorContainerExists": "O contêiner \"{{containerName}}\" no banco de dados \"{{databaseName}}\" já existe. Exclua-o e tente novamente.",
"errorCreateContainer": "Falha ao criar o contêiner: {{error}}",
"errorImportData": "Falha ao importar dados: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Cancelar",
"close": "Fechar",
"save": "Guardar",
"delete": "Eliminar",
"update": "Atualizar",
"discard": "Eliminar",
"execute": "Executar",
"loading": "A carregar",
"loadingEllipsis": "A carregar...",
"next": "Seguinte",
"previous": "Anterior",
"yes": "Sim",
"no": "Não",
"result": "Resultado",
"learnMore": "Saber mais",
"getStarted": "Começar",
"retry": "Tentar novamente",
"apply": "Aplicar",
"refresh": "Atualizar",
"copy": "Copiar",
"create": "Criar",
"confirm": "Confirmar",
"open": "Abrir",
"rename": "Mudar o Nome",
"download": "Transferir",
"upload": "Carregar",
"connect": "Ligar",
"remove": "Remover",
"increaseValueBy1": "Aumentar valor em 1",
"decreaseValueBy1": "Diminuir valor em 1"
},
"splashScreen": {
"title": {
"default": "Bem-vindo ao Azure Cosmos DB",
"postgres": "Bem-vindo ao Azure Cosmos DB para PostgreSQL",
"vcoreMongo": "Bem-vindo ao Azure DocumentDB (com compatibilidade do MongoDB)"
},
"subtitle": {
"default": "Serviço de base de dados com múltiplos modelos distribuído globalmente para qualquer dimensionamento",
"getStarted": "Comece a trabalhar com os nossos conjuntos de dados de exemplo, documentação e ferramentas adicionais."
},
"quickStart": {
"title": "Iniciar início rápido",
"description": "Inicie um tutorial de início rápido para começar a utilizar dados de exemplo"
},
"newCollection": {
"title": "Novo {{collectionName}}",
"description": "Criar um novo contentor para armazenamento e débito"
},
"samplesGallery": {
"title": "Galeria de Exemplos do Azure Cosmos DB",
"description": "Descubra exemplos que apresentam padrões de aplicações escaláveis e inteligentes. Experimente um agora para ver a rapidez com que pode passar do conceito ao código com o Cosmos DB"
},
"connectCard": {
"title": "Ligar",
"description": "Prefere usar as suas próprias ferramentas? Encontre a cadeia de ligação de que precisa para comunicar",
"pgAdmin": {
"title": "Comunicar com o pgAdmin",
"description": "Prefere o pgAdmin? Encontre as suas cadeias de ligação aqui"
},
"vsCode": {
"title": "Ligar ao VS Code",
"description": "Consulte e gerencie seus clusters MongoDB e DocumentDB no Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Criar uma tabela e interagir com dados usando a interface shell do PostgreSQL"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Criar uma coleção e interagir com dados usando a interface shell do MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Novo no Cosmos DB PGSQL?",
"body": "Damos-lhe as boas-vindas! Se é novo no Cosmos DB PGSQL e precisa de ajuda para começar, aqui pode encontrar dados de exemplo e consultas."
},
"resetPassword": {
"headline": "Criar a sua palavra-passe",
"body": "Se ainda não alterou a sua palavra-passe, altere-a agora."
},
"coachMark": {
"headline": "Começar com amostra {{collectionName}}",
"body": "Será orientado para criar um contentor de exemplo com dados de exemplo e, em seguida, faremos uma visita guiada ao explorador de dados. Também pode cancelar o lançamento desta visita e explorar por si próprio."
}
},
"sections": {
"recents": "Recentes",
"clearRecents": "Limpar recentes",
"top3": "As três principais coisas que precisa de saber",
"learningResources": "Recursos de Aprendizagem",
"nextSteps": "Passos seguintes",
"tipsAndLearnMore": "Sugestões e saiba mais",
"notebook": "Bloco de notas",
"needHelp": "Precisa de ajuda?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Padrões Avançados de Modelação",
"description": "Aprenda estratégias avançadas para otimizar a sua base de dados."
},
"partitioning": {
"title": "Melhores práticas para a criação de partições",
"description": "Aprenda a aplicar o modelo de dados e as estratégias de partição."
},
"resourcePlanning": {
"title": "Planear os seus Requisitos de Recursos",
"description": "Conheça as diferentes opções de configuração."
}
},
"mongo": {
"whatIsMongo": {
"title": "O que é a API MongoDB?",
"description": "Compreenda o Azure Cosmos DB for MongoDB e as suas funcionalidades."
},
"features": {
"title": "Funcionalidades e Sintaxe",
"description": "Descubra as vantagens e funcionalidades"
},
"migrate": {
"title": "Migrar os seus dados",
"description": "Passos de pré-migração para fazer a migração de dados"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Criar uma Aplicação Java",
"description": "Crie uma aplicação Java com um SDK."
},
"partitioning": {
"title": "Melhores práticas para a criação de partições",
"description": "Saiba como funciona a partição."
},
"requestUnits": {
"title": "Unidades de Pedido (RUs)",
"description": "Compreenda as cobranças de RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Modelação de dados",
"description": "Recomendações para modelação de dados do Graph"
},
"partitioning": {
"title": "Melhores práticas para a criação de partições",
"description": "Saiba como funciona a partição"
},
"queryData": {
"title": "Consultar dados",
"description": "Consultar dados com o Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "O que é a API Table?",
"description": "Compreenda o Azure Cosmos DB for Table e as suas funcionalidades"
},
"migrate": {
"title": "Migrar os seus dados",
"description": "Saiba como migrar dados"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Perguntas comuns sobre o Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Atalhos de teclado do Data Explorer",
"description": "Aprenda atalhos de teclado para navegar no Data Explorer."
},
"liveTv": {
"title": "Aprenda as Noções Básicas",
"description": "Veja o programa de TV em direto do Azure Cosmos DB e vídeos de introdução e tutoriais."
},
"sql": {
"sdk": {
"title": "Começar a utilizar um SDK",
"description": "Saiba mais sobre o Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrar os seus dados",
"description": "Migre dados utilizando serviços Azure e soluções open-source."
}
},
"mongo": {
"nodejs": {
"title": "Criar uma aplicação com Node.js",
"description": "Criar uma aplicação Node.js."
},
"gettingStarted": {
"title": "Guia de Introdução",
"description": "Aprenda o básico para começar."
}
},
"cassandra": {
"createContainer": {
"title": "Criar um Contentor",
"description": "Conheça as opções para criar um contentor."
},
"throughput": {
"title": "Débito de Aprovisionamento",
"description": "Saiba como configurar o débito."
}
},
"gremlin": {
"getStarted": {
"title": "Começar a Utilizar ",
"description": "Criar, consultar e percorrer utilizando a consola Gremlin"
},
"importData": {
"title": "Importar Dados do Grafo",
"description": "Saiba como fazer a ingestão em massa de dados com o BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Criar uma Aplicação .NET",
"description": "Como aceder ao Azure Cosmos DB for Table a partir de uma aplicação .NET."
},
"java": {
"title": "Criar uma Aplicação Java",
"description": "Crie uma aplicação Azure Cosmos DB for Table com o Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Modelação de dados",
"distributionColumn": "Como escolher uma Coluna de Distribuição",
"buildApps": "Criar Aplicações com Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrar dados",
"vectorSearch": "Criar aplicações de IA com a Pesquisa de Vectores",
"buildApps": "Criar Aplicações com Node.js"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Ajuste do Desempenho",
"diagnosticQueries": "Consultas de Diagnóstico Úteis",
"sqlReference": "Referência de SQL Distribuído"
},
"vcoreMongo": {
"vectorSearch": "Pesquisa de Vectores",
"textIndexing": "Indexação de Texto",
"troubleshoot": "Resolver problemas comuns"
}
},
"fabric": {
"buildTitle": "Crie a sua base de dados",
"useTitle": "Utilizar a sua base de dados",
"newContainer": {
"title": "Novo contentor",
"description": "Crie um contentor de destino para armazenar os seus dados"
},
"sampleData": {
"title": "Dados de Amostra",
"description": "Carregar dados de exemplo na sua base de dados"
},
"sampleVectorData": {
"title": "Dados Vetoriais de Exemplo",
"description": "Carregar dados de vetor de exemplo com text-embedding-ada-002"
},
"appDevelopment": {
"title": "Desenvolvimento de aplicações",
"description": "Comece aqui para utilizar um SDK para criar as suas aplicações"
},
"sampleGallery": {
"title": "Galeria de Exemplos",
"description": "Obter exemplos completos do mundo real"
}
},
"sampleDataDialog": {
"title": "Dados de Amostra",
"startButton": "Início",
"createPrompt": "Crie um contentor \"{{containerName}}\" e importe dados de exemplo para o mesmo. Esta operação pode demorar alguns minutos.",
"creatingContainer": "A criar o contentor \"{{containerName}}\"...",
"importingData": "A importar dados para \"{{containerName}}\"...",
"success": "\"{{containerName}}\" criado com êxito com dados de exemplo.",
"errorContainerExists": "O contentor \"{{containerName}}\" na base de dados \"{{databaseName}}\" já existe. Elimine-o e tente novamente.",
"errorCreateContainer": "Falha ao criar o contentor: {{error}}",
"errorImportData": "Falha ao importar dados: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "ОК",
"cancel": "Отмена",
"close": "Закрыть",
"save": "Сохранить",
"delete": "Удалить",
"update": "Обновить",
"discard": "Отменить",
"execute": "Выполнить",
"loading": "Загрузка",
"loadingEllipsis": "Идет загрузка…",
"next": "Далее",
"previous": "Назад",
"yes": "Да",
"no": "Нет",
"result": "Результат",
"learnMore": "Подробнее",
"getStarted": "Начало работы",
"retry": "Повторить",
"apply": "Применить",
"refresh": "Обновить",
"copy": "Копировать",
"create": "Создать",
"confirm": "Подтвердить",
"open": "Открыть",
"rename": "Переименовать",
"download": "Скачать",
"upload": "Отправить",
"connect": "Подключить",
"remove": "Удалить",
"increaseValueBy1": "Увеличить значение на 1",
"decreaseValueBy1": "Уменьшить значение на 1"
},
"splashScreen": {
"title": {
"default": "Вас приветствует Azure Cosmos DB",
"postgres": "Добро пожаловать в Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Добро пожаловать в Azure DocumentDB (с совместимостью с MongoDB)"
},
"subtitle": {
"default": "Глобально распределенная многомодельная служба базы данных для использования в любом масштабе",
"getStarted": "Начните работу с нашими примерами наборов данных, документацией и дополнительными инструментами."
},
"quickStart": {
"title": "Начать быстрый запуск",
"description": "Запустите краткое обучающее руководство, чтобы начать работу с примерами данных"
},
"newCollection": {
"title": "Новый {{collectionName}}",
"description": "Создайте новый контейнер для хранения данных и обеспечения пропускной способности"
},
"samplesGallery": {
"title": "Галерея примеров Azure Cosmos DB",
"description": "Ознакомьтесь с примерами, демонстрирующими масштабируемые и интеллектуальные шаблоны приложений. Попробуйте прямо сейчас и убедитесь, как быстро вы можете перейти от концепции к коду с Cosmos DB"
},
"connectCard": {
"title": "Подключить",
"description": "Предпочитаете использовать собственные инструменты? Найдите строку подключения, необходимую для соединения.",
"pgAdmin": {
"title": "Свяжитесь с pgAdmin",
"description": "Предпочитаете pgAdmin? Здесь вы найдете строки подключения"
},
"vsCode": {
"title": "Подключиться к VS Code",
"description": "Запрашивайте данные и управляйте кластерами MongoDB и DocumentDB в Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "Оболочка PostgreSQL",
"description": "Создайте таблицу и взаимодействуйте с данными, используя командный интерфейс PostgreSQL"
},
"vcoreMongo": {
"title": "Оболочка Mongo",
"description": "Создайте коллекцию и взаимодействуйте с данными, используя командный интерфейс MongoDB"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Впервые работаете с Cosmos DB PGSQL?",
"body": "Добро пожаловать! Если вы новичок в Cosmos DB PGSQL и вам нужна помощь в начале работы, здесь вы можете найти примеры данных и запросов."
},
"resetPassword": {
"headline": "Создайте пароль",
"body": "Если вы еще не сменили пароль, сделайте это прямо сейчас."
},
"coachMark": {
"headline": "Начнём с образца {{collectionName}}",
"body": "Вам будет предложено создать тестовый контейнер с тестовыми данными, после чего мы проведем для вас ознакомительный обзор инструмента \"Обозреватель данных\". Вы также можете отменить запуск этого тура и исследовать местность самостоятельно"
}
},
"sections": {
"recents": "Последние",
"clearRecents": "Очистить последние",
"top3": "Три главных вещи, которые вам нужно знать",
"learningResources": "Учебные материалы",
"nextSteps": "Дальнейшие действия",
"tipsAndLearnMore": "Советы и дополнительная информация",
"notebook": "Записная книжка",
"needHelp": "Нужна помощь?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Расширенные шаблоны моделирования",
"description": "Изучите передовые стратегии оптимизации вашей базы данных."
},
"partitioning": {
"title": "Передовые методы разделения данных",
"description": "Научитесь применять стратегии моделирования данных и их разделения на разделы."
},
"resourcePlanning": {
"title": "Спланируйте свои потребности в ресурсах",
"description": "Ознакомьтесь с различными вариантами конфигурации."
}
},
"mongo": {
"whatIsMongo": {
"title": "Что такое API MongoDB?",
"description": "Разберитесь в Azure Cosmos DB для MongoDB и его возможностях."
},
"features": {
"title": "Особенности и синтаксис",
"description": "Узнайте о преимуществах и особенностях"
},
"migrate": {
"title": "Перенесите свои данные",
"description": "Этапы подготовки к миграции данных"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Создайте Java-приложение",
"description": "Создайте Java-приложение, используя SDK."
},
"partitioning": {
"title": "Передовые методы разделения данных",
"description": "Узнайте, как работает разбиение на разделы."
},
"requestUnits": {
"title": "Единицы запроса (ЕЗ)",
"description": "Разберитесь в тарифах RU."
}
},
"gremlin": {
"dataModeling": {
"title": "Моделирование данных",
"description": "Рекомендации по моделированию графовых данных"
},
"partitioning": {
"title": "Передовые методы разделения данных",
"description": "Узнайте, как работает разбиение на разделы"
},
"queryData": {
"title": "Запрос данных",
"description": "Запросы к данным с помощью Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "Что такое API таблиц?",
"description": "Разберитесь в Azure Cosmos DB for Table и его возможностях"
},
"migrate": {
"title": "Перенесите свои данные",
"description": "Сведения о миграции данных"
},
"faq": {
"title": "Часто задаваемые вопросы об Azure Cosmos DB для таблиц",
"description": "Часто задаваемые вопросы об Azure Cosmos DB для таблиц"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Клавиатурные сочетания в Data Explorer",
"description": "Изучите сочетания клавиш для навигации по обозревателю данных."
},
"liveTv": {
"title": "Изучение основ",
"description": "Смотрите вводные и обучающие видеоролики о Azure Cosmos DB Live TV."
},
"sql": {
"sdk": {
"title": "Начните работу с SDK",
"description": "Узнайте больше об SDK Azure Cosmos DB."
},
"migrate": {
"title": "Перенесите свои данные",
"description": "Перенесите данные, используя службы Azure и решения с открытым исходным кодом."
}
},
"mongo": {
"nodejs": {
"title": "Создайте приложение с помощью Node.js",
"description": "Создайте приложение Node.js."
},
"gettingStarted": {
"title": "Руководство по началу работы",
"description": "Изучите основы, чтобы начать."
}
},
"cassandra": {
"createContainer": {
"title": "Создать контейнер",
"description": "Ознакомьтесь с параметрами создания контейнера."
},
"throughput": {
"title": "Пропускная способность предоставления",
"description": "Узнайте, как настроить пропускную способность."
}
},
"gremlin": {
"getStarted": {
"title": "Начать ",
"description": "Создавайте, запрашивайте и обходите данные с помощью консоли Gremlin"
},
"importData": {
"title": "Импорт графических данных",
"description": "Узнайте, как осуществлять массовую загрузку данных с помощью BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Создайте приложение на платформе .NET",
"description": "Как получить доступ к Azure Cosmos DB for Table из приложения .NET."
},
"java": {
"title": "Создайте Java-приложение",
"description": "Создайте приложение Azure Cosmos DB for Table с помощью Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Моделирование данных",
"distributionColumn": "Как выбрать столбец распределения",
"buildApps": "Создавайте приложения с помощью Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Перенести данные",
"vectorSearch": "Создавайте приложения с искусственным интеллектом с помощью Vector Search",
"buildApps": "Создавайте приложения с помощью Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Настройка производительности",
"diagnosticQueries": "Полезные диагностические запросы",
"sqlReference": "Справочник по распределенному SQL"
},
"vcoreMongo": {
"vectorSearch": "Поиск векторов",
"textIndexing": "Индексирование текста",
"troubleshoot": "Устранение распространенных неполадок"
}
},
"fabric": {
"buildTitle": "Создание базы данных",
"useTitle": "Используйте свою базу данных",
"newContainer": {
"title": "Новый контейнер",
"description": "Создайте целевой контейнер для хранения ваших данных"
},
"sampleData": {
"title": "Пример данных",
"description": "Загрузите примеры данных в свою базу данных"
},
"sampleVectorData": {
"title": "Пример векторных данных",
"description": "Загрузите примеры векторных данных с помощью text-embedding-ada-002"
},
"appDevelopment": {
"title": "Разработка приложений",
"description": "Начните здесь, чтобы использовать SDK для создания своих приложений"
},
"sampleGallery": {
"title": "Образцы галереи",
"description": "Получите реальные примеры комплексного решения"
}
},
"sampleDataDialog": {
"title": "Пример данных",
"startButton": "Начать",
"createPrompt": "Создайте контейнер \" {{containerName}} \" и импортируйте в него примерные данные. Это может занять несколько минут.",
"creatingContainer": "Создание контейнера \" {{containerName}} \"...",
"importingData": "Импорт данных в \" {{containerName}} \"...",
"success": "Успешно создан \" {{containerName}} \" с примерами данных.",
"errorContainerExists": "Контейнер \" {{containerName}} \" в базе данных \" {{databaseName}} \" уже существует. Удалите его и попробуйте снова.",
"errorCreateContainer": "Не удалось создать контейнер: {{error}}",
"errorImportData": "Не удалось импортировать данные: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "OK",
"cancel": "Avbryt",
"close": "Stäng",
"save": "Spara",
"delete": "Ta bort",
"update": "Uppdatera",
"discard": "Ta bort",
"execute": "Kör",
"loading": "Läser in",
"loadingEllipsis": "Läser in...",
"next": "Nästa",
"previous": "Föregående",
"yes": "Ja",
"no": "Nej",
"result": "Resultat",
"learnMore": "Mer information",
"getStarted": "Kom igång",
"retry": "Försök igen",
"apply": "Använd",
"refresh": "Uppdatera",
"copy": "Kopiera",
"create": "Skapa",
"confirm": "Bekräfta",
"open": "Öppna",
"rename": "Byt namn",
"download": "Ladda ned",
"upload": "Ladda upp",
"connect": "Anslut",
"remove": "Ta bort",
"increaseValueBy1": "Öka värdet med 1",
"decreaseValueBy1": "Minska värdet med 1"
},
"splashScreen": {
"title": {
"default": "Välkommen till Azure Cosmos DB",
"postgres": "Välkommen till Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "Välkommen till Azure DocumentDB (med MongoDB-kompatibilitet)"
},
"subtitle": {
"default": "Globalt distribuerad databas för flera datamodeller oavsett skala",
"getStarted": "Kom igång med våra exempeldatamängder, dokumentation och extra verktyg."
},
"quickStart": {
"title": "Starta snabbstart",
"description": "Starta en snabbstartsguide för att komma igång med exempeldata"
},
"newCollection": {
"title": "Ny {{collectionName}}",
"description": "Skapa en ny container för lagring och dataflöde"
},
"samplesGallery": {
"title": "Galleri för Azure Cosmos DB-exempel",
"description": "Upptäck exempel som visar skalbara, intelligenta appmönster. Prova en nu för att se hur snabbt du kan gå från koncept till kod med Cosmos DB"
},
"connectCard": {
"title": "Anslut",
"description": "Föredrar du att använda ditt eget val av verktyg? Hitta anslutningssträngen du behöver för att ansluta",
"pgAdmin": {
"title": "Anslut med pgAdmin",
"description": "Föredrar du pgAdmin? Hitta dina anslutningssträngar här"
},
"vsCode": {
"title": "Anslut med VS Code",
"description": "Fråga och hantera dina MongoDB- och DocumentDB-kluster i Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL-gränssnitt",
"description": "Skapa tabell och interagera med data med postgreSQL-gränssnittet"
},
"vcoreMongo": {
"title": "Mongo-gränssnitt",
"description": "Skapa en samling och interagera med data med hjälp av MongoDB:s gränssnitt"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "Är Cosmos DB PGSQL nytt för dig?",
"body": "Välkommen! Om du är nybörjare på Cosmos DB PGSQL och behöver hjälp med att komma igång hittar du exempeldata och frågor här."
},
"resetPassword": {
"headline": "Skapa ett lösenord",
"body": "Om du inte har ändrat ditt lösenord ännu ändrar du det nu."
},
"coachMark": {
"headline": "Börja med exempel {{collectionName}}",
"body": "Du får vägledning för att skapa en exempelcontainer med exempeldata, och sedan får du en rundtur i datautforskaren. Du kan också avbryta lanseringen av den här rundturen och utforska dig själv"
}
},
"sections": {
"recents": "Senaste",
"clearRecents": "Rensa senaste",
"top3": "De 3 viktigaste sakerna du behöver veta",
"learningResources": "Utbildningsresurser",
"nextSteps": "Nästa steg",
"tipsAndLearnMore": "Tips och läs mer",
"notebook": "Anteckningsbok",
"needHelp": "Behöver du hjälp?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Avancerade modelleringsmönster",
"description": "Lär dig avancerade strategier för att optimera din databas."
},
"partitioning": {
"title": "Metodtipsen för partitionering",
"description": "Lär dig hur du tillämpar strategier för datamodeller och partitionering."
},
"resourcePlanning": {
"title": "Planera dina resurskrav",
"description": "Lär känna de olika konfigurationsalternativen."
}
},
"mongo": {
"whatIsMongo": {
"title": "Vad är MongoDB-API:et?",
"description": "Förstå Azure Cosmos DB for MongoDB och dess funktioner."
},
"features": {
"title": "Funktioner och syntax",
"description": "Upptäck fördelarna och funktionerna"
},
"migrate": {
"title": "Migrera dina data",
"description": "Steg före migrering för att flytta data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Skapa en Java-app",
"description": "Skapa en Java-app med hjälp av en SDK."
},
"partitioning": {
"title": "Metodtipsen för partitionering",
"description": "Lär dig hur partitionering fungerar."
},
"requestUnits": {
"title": "Enheter för programbegäran (RU)",
"description": "Förstå RU-avgifter."
}
},
"gremlin": {
"dataModeling": {
"title": "Datamodellering",
"description": "Rekommendationer för diagramdatamodellering"
},
"partitioning": {
"title": "Metodtipsen för partitionering",
"description": "Lär dig hur partitionering fungerar"
},
"queryData": {
"title": "Efterfråga data",
"description": "Köra frågor mot data med Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "Vad är Table API?",
"description": "Förstå Azure Cosmos DB for Table och dess funktioner"
},
"migrate": {
"title": "Migrera dina data",
"description": "Läs hur du migrerar dina data"
},
"faq": {
"title": "Vanliga frågor och svar om Azure Cosmos DB for Table",
"description": "Vanliga frågor om Azure Cosmos DB för tabell"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Datautforskaren kortkommandon",
"description": "Lär dig kortkommandon för att navigera Datautforskaren."
},
"liveTv": {
"title": "Lär dig grunderna",
"description": "Titta på Azure Cosmos DB introduktion till live-TV-program och hur du gör videor."
},
"sql": {
"sdk": {
"title": "Kom igång med ett SDK",
"description": "Läs mer om Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrera dina data",
"description": "Migrera data med Hjälp av Azure-tjänster och lösningar med öppen källkod."
}
},
"mongo": {
"nodejs": {
"title": "Skapa en app med Node.js",
"description": "Skapa en Node.js app."
},
"gettingStarted": {
"title": "Komma igång-guide",
"description": "Lär dig grunderna för att komma igång."
}
},
"cassandra": {
"createContainer": {
"title": "Skapa en container",
"description": "Lär känna alternativen för att skapa en container."
},
"throughput": {
"title": "Etablera dataflöde",
"description": "Lär dig hur du konfigurerar dataflöde."
}
},
"gremlin": {
"getStarted": {
"title": "Kom igång ",
"description": "Skapa, fråga och bläddra med Gremlin-konsolen"
},
"importData": {
"title": "Importera diagramdata",
"description": "Lär dig massinmatning av data med BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Skapa en .NET-app",
"description": "Så här kommer du åt Azure Cosmos DB for Table från en .NET-app."
},
"java": {
"title": "Skapa en Java-app",
"description": "Skapa en Azure Cosmos DB for Table-appen med Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Datamodellering",
"distributionColumn": "Så här väljer du en distributionskolumn",
"buildApps": "Skapa appar med Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrera data",
"vectorSearch": "Skapa AI-appar med Vector Search",
"buildApps": "Skapa appar med Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Prestandajustering",
"diagnosticQueries": "Användbara diagnostikfrågor",
"sqlReference": "Distribuerad SQL-referens"
},
"vcoreMongo": {
"vectorSearch": "Vektorssökning",
"textIndexing": "Textindexering",
"troubleshoot": "Felsök vanliga problem"
}
},
"fabric": {
"buildTitle": "Skapa din databas",
"useTitle": "Använda databasen",
"newContainer": {
"title": "Ny container",
"description": "Skapa en målcontainer för att lagra dina data"
},
"sampleData": {
"title": "Exempeldata",
"description": "Läs in exempeldata i databasen"
},
"sampleVectorData": {
"title": "Exempelvektordata",
"description": "Läs in exempelvektordata med textinbäddning-ada-002"
},
"appDevelopment": {
"title": "Apputveckling",
"description": "Börja här om du vill använda en SDK för att skapa dina appar"
},
"sampleGallery": {
"title": "Exempelgalleri",
"description": "Få verkliga exempel från slutpunkt till slutpunkt"
}
},
"sampleDataDialog": {
"title": "Exempeldata",
"startButton": "Starta",
"createPrompt": "Skapa en container \"{{containerName}}\" och importera exempeldata till den. Det här kan ta några minuter.",
"creatingContainer": "Skapar container \"{{containerName}}\"...",
"importingData": "Importerar data till \"{{containerName}}\"...",
"success": "\"{{containerName}}\" med exempeldata har skapats.",
"errorContainerExists": "Containern \"{{containerName}}\" i databasen \"{{databaseName}}\" finns redan. Ta bort den och försök igen.",
"errorCreateContainer": "Det gick inte att skapa containern: {{error}}",
"errorImportData": "Det gick inte att importera data: {{error}}"
}
}
}

24
src/Localization/t.ts Normal file
View File

@@ -0,0 +1,24 @@
import i18n from "../i18n";
import type enResources from "./en/Resources.json";
/**
* Derives a union of all dot-notation key paths from a nested JSON object type.
* e.g. { buttons: { save: "Save" } } → "buttons.save"
*/
type NestedKeyOf<T, P extends string = ""> = {
[K in keyof T & string]: T[K] extends Record<string, unknown>
? NestedKeyOf<T[K], P extends "" ? K : `${P}.${K}`>
: P extends ""
? K
: `${P}.${K}`;
}[keyof T & string];
/** All valid translation keys derived from en/Resources.json */
export type ResourceKey = NestedKeyOf<typeof enResources>;
/**
* Type-safe translation function bound to the "Resources" namespace.
* Use this everywhere—class components, functional components, and non-React code.
*/
export const t = (key: ResourceKey, options?: Record<string, unknown>): string =>
(i18n.t as (key: string, options?: unknown) => string)(key, { ns: "Resources", ...options });

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "Tamam",
"cancel": "İptal",
"close": "Kapat",
"save": "Kaydet",
"delete": "Sil",
"update": "Güncelleştir",
"discard": "At",
"execute": "Execute",
"loading": "Yükleniyor",
"loadingEllipsis": "Yükleniyor...",
"next": "Sonraki",
"previous": "Önceki",
"yes": "Evet",
"no": "Hayır",
"result": "Sonuç",
"learnMore": "Daha fazla bilgi edinin",
"getStarted": "Kullanmaya başlayın",
"retry": "Yeniden dene",
"apply": "Uygula",
"refresh": "Yenile",
"copy": "Kopyala",
"create": "Oluştur",
"confirm": "Onayla",
"open": "Aç",
"rename": "Yeniden adlandır",
"download": "İndir",
"upload": "Karşıya yükle",
"connect": "Bağlan",
"remove": "Kaldır",
"increaseValueBy1": "Değeri 1 artır",
"decreaseValueBy1": "Değeri 1 azalt"
},
"splashScreen": {
"title": {
"default": "Azure Cosmos DB'ye hoş geldiniz",
"postgres": "Azure Cosmos DB for PostgreSQL'e hoş geldiniz",
"vcoreMongo": "Azure DocumentDB'ye (MongoDB uyumluluğu ile) hoş geldiniz"
},
"subtitle": {
"default": "Her ölçeğe uygun, global olarak dağıtılan çok modelli veritabanı hizmeti",
"getStarted": "Örnek veri kümelerimizi, belgelerimizi ve ek araçlarımızı kullanmaya başlayın."
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "VS Code ile Bağlan",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "确定",
"cancel": "取消",
"close": "关闭",
"save": "保存",
"delete": "删除",
"update": "更新",
"discard": "放弃",
"execute": "执行",
"loading": "正在加载",
"loadingEllipsis": "正在加载...",
"next": "下一步",
"previous": "上一步",
"yes": "是",
"no": "否",
"result": "结果",
"learnMore": "了解详细信息",
"getStarted": "开始使用",
"retry": "重试",
"apply": "应用",
"refresh": "刷新",
"copy": "复制",
"create": "创建",
"confirm": "确认",
"open": "打开",
"rename": "重命名",
"download": "下载",
"upload": "上传",
"connect": "连接",
"remove": "删除",
"increaseValueBy1": "将值增加 1",
"decreaseValueBy1": "将值减少 1"
},
"splashScreen": {
"title": {
"default": "欢迎使用 Azure Cosmos DB",
"postgres": "欢迎使用 Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "欢迎使用 Azure DocumentDB (具有 MongoDB 兼容性)"
},
"subtitle": {
"default": "任何规模的全球分布式多模型数据库服务",
"getStarted": "开始使用我们的示例数据集、文档和其他工具。"
},
"quickStart": {
"title": "启动快速入门",
"description": "启动快速入门教程,开始使用示例数据"
},
"newCollection": {
"title": "新建 {{collectionName}}",
"description": "创建用于存储和吞吐量的新容器"
},
"samplesGallery": {
"title": "Azure Cosmos DB 示例库",
"description": "发现展示可扩展智能应用模式的示例。立即试用,体验如何通过 Cosmos DB 快速从概念转变到代码"
},
"connectCard": {
"title": "连接",
"description": "更喜欢使用自己选择的工具?查找需要连接的连接字符串",
"pgAdmin": {
"title": "使用 pgAdmin 连接",
"description": "首选 pgAdmin?在此处查找连接字符串"
},
"vsCode": {
"title": "与 VS Code 连接",
"description": "在 Visual Studio Code 中查询和管理 MongoDB 和 DocumentDB 群集"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "使用 PostgreSQL shell 接口创建表并与数据交互"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "使用 MongoDB shell 接口创建集合并与数据交互"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "不熟悉 Cosmos DB PGSQL?",
"body": "欢迎!如果你不熟悉 Cosmos DB PGSQL需要入门帮助可在此处找到示例数据和查询。"
},
"resetPassword": {
"headline": "创建密码",
"body": "如果尚未更改密码,请立即更改。"
},
"coachMark": {
"headline": "从示例 {{collectionName}} 开始",
"body": "系统将引导你创建包含示例数据的示例容器,然后带你了解数据资源管理器。你也可以取消此教程,改为自行探索"
}
},
"sections": {
"recents": "最近使用",
"clearRecents": "清除最近使用记录",
"top3": "你需要了解的三大要点",
"learningResources": "学习资源",
"nextSteps": "后续步骤",
"tipsAndLearnMore": "提示和详细信息",
"notebook": "笔记本",
"needHelp": "需要帮助?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "高级建模模式",
"description": "了解用于优化数据库的高级策略。"
},
"partitioning": {
"title": "分区最佳做法",
"description": "了解如何应用数据模型和分区策略。"
},
"resourcePlanning": {
"title": "规划资源需求",
"description": "了解不同的配置选择。"
}
},
"mongo": {
"whatIsMongo": {
"title": "什么是 MongoDB API?",
"description": "了解 Azure Cosmos DB for MongoDB 及其功能。"
},
"features": {
"title": "功能和语法",
"description": "了解优势和功能"
},
"migrate": {
"title": "迁移数据",
"description": "用于移动数据的迁移前步骤"
}
},
"cassandra": {
"buildJavaApp": {
"title": "构建 Java 应用",
"description": "使用 SDK 创建 Java 应用。"
},
"partitioning": {
"title": "分区最佳做法",
"description": "了解分区的工作原理。"
},
"requestUnits": {
"title": "请求单位(RU)",
"description": "了解 RU 费用。"
}
},
"gremlin": {
"dataModeling": {
"title": "数据建模",
"description": "图形数据建模建议"
},
"partitioning": {
"title": "分区最佳做法",
"description": "了解分区的工作原理"
},
"queryData": {
"title": "查询数据",
"description": "使用 Gremlin 查询数据"
}
},
"tables": {
"whatIsTable": {
"title": "什么是 Table API?",
"description": "了解 Azure Cosmos DB for Table 及其功能"
},
"migrate": {
"title": "迁移数据",
"description": "了解如何迁移数据"
},
"faq": {
"title": "Azure Cosmos DB for Table 常见问题解答",
"description": "有关 Azure Cosmos DB for Table 的常见问题"
}
}
},
"learningResources": {
"shortcuts": {
"title": "数据资源管理器键盘快捷方式",
"description": "了解浏览数据资源管理器的键盘快捷方式。"
},
"liveTv": {
"title": "了解基础知识",
"description": "观看 Azure Cosmos DB 直播节目介绍及操作方法视频。"
},
"sql": {
"sdk": {
"title": "开始使用 SDK",
"description": "了解 Azure Cosmos DB SDK。"
},
"migrate": {
"title": "迁移数据",
"description": "使用 Azure 服务和开源解决方案迁移数据。"
}
},
"mongo": {
"nodejs": {
"title": "使用 Node.js 构建应用",
"description": "创建 Node.js 应用。"
},
"gettingStarted": {
"title": "入门指南",
"description": "了解入门的基础知识。"
}
},
"cassandra": {
"createContainer": {
"title": "创建容器",
"description": "了解创建容器的选项。"
},
"throughput": {
"title": "预配吞吐量",
"description": "了解如何配置吞吐量。"
}
},
"gremlin": {
"getStarted": {
"title": "开始 ",
"description": "使用 Gremlin 控制台执行创建、查询和遍历"
},
"importData": {
"title": "导入图形数据",
"description": "了解如何使用 BulkExecutor 批量引入数据"
}
},
"tables": {
"dotnet": {
"title": "构建 .NET 应用",
"description": "如何从 .NET 应用访问 Azure Cosmos DB for Table。"
},
"java": {
"title": "构建 Java 应用",
"description": "使用 Java SDK 创建 Azure Cosmos DB for Table 应用 "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "数据建模",
"distributionColumn": "如何选择分布列",
"buildApps": "使用 Python/Java/Django 构建应用"
},
"vcoreMongo": {
"migrateData": "迁移数据",
"vectorSearch": "使用矢量搜索功能构建 AI 应用",
"buildApps": "使用 Nodejs 构建应用"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "性能优化",
"diagnosticQueries": "有用的诊断查询",
"sqlReference": "分布式 SQL 参考"
},
"vcoreMongo": {
"vectorSearch": "矢量搜索",
"textIndexing": "文本索引",
"troubleshoot": "对常见问题进行故障排除"
}
},
"fabric": {
"buildTitle": "生成数据库",
"useTitle": "使用你的数据库",
"newContainer": {
"title": "新容器",
"description": "创建用于存储数据的目标容器"
},
"sampleData": {
"title": "示例数据",
"description": "在数据库中加载示例数据"
},
"sampleVectorData": {
"title": "示例矢量数据",
"description": "使用 text-embedding-ada-002 加载示例矢量数据"
},
"appDevelopment": {
"title": "应用开发",
"description": "从此处开始使用 SDK 来构建应用"
},
"sampleGallery": {
"title": "示例库",
"description": "获取真实的端到端示例"
}
},
"sampleDataDialog": {
"title": "示例数据",
"startButton": "开始",
"createPrompt": "创建容器“{{containerName}}”,并将示例数据导入其中。这可能需要几分钟时间。",
"creatingContainer": "正在创建容器“{{containerName}}”...",
"importingData": "正在将数据导入“{{containerName}}”...",
"success": "已成功使用示例数据创建“{{containerName}}”。",
"errorContainerExists": "数据库“{{databaseName}}”中的容器“{{containerName}}”已存在。请将其删除,然后重试。",
"errorCreateContainer": "未能创建容器: {{error}}",
"errorImportData": "未能导入数据: {{error}}"
}
}
}

View File

@@ -0,0 +1,295 @@
{
"common": {
"ok": "確定",
"cancel": "取消",
"close": "關閉",
"save": "儲存",
"delete": "刪除",
"update": "更新",
"discard": "捨棄",
"execute": "Execute",
"loading": "正在載入",
"loadingEllipsis": "正在載入...",
"next": "下一個",
"previous": "上一步",
"yes": "是",
"no": "否",
"result": "結果",
"learnMore": "深入了解",
"getStarted": "開始使用",
"retry": "重試",
"apply": "套用",
"refresh": "重新整理",
"copy": "複製",
"create": "建立",
"confirm": "確認",
"open": "開啟",
"rename": "重新命名",
"download": "下載",
"upload": "上傳",
"connect": "連線",
"remove": "移除",
"increaseValueBy1": "將值增加 1",
"decreaseValueBy1": "將值減少 1"
},
"splashScreen": {
"title": {
"default": "歡迎使用 Azure Cosmos DB",
"postgres": "歡迎使用 Azure Cosmos DB for PostgreSQL",
"vcoreMongo": "歡迎使用 Azure DocumentDB (具 MongoDB 相容性)"
},
"subtitle": {
"default": "適用於任何規模的全域散發、多模型資料庫服務",
"getStarted": "開始使用我們的樣本資料集、文件和其他工具。"
},
"quickStart": {
"title": "Launch quick start",
"description": "Launch a quick start tutorial to get started with sample data"
},
"newCollection": {
"title": "New {{collectionName}}",
"description": "Create a new container for storage and throughput"
},
"samplesGallery": {
"title": "Azure Cosmos DB Samples Gallery",
"description": "Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
},
"connectCard": {
"title": "Connect",
"description": "Prefer using your own choice of tooling? Find the connection string you need to connect",
"pgAdmin": {
"title": "Connect with pgAdmin",
"description": "Prefer pgAdmin? Find your connection strings here"
},
"vsCode": {
"title": "與 VS Code 連線",
"description": "Query and Manage your MongoDB and DocumentDB clusters in Visual Studio Code"
}
},
"shell": {
"postgres": {
"title": "PostgreSQL Shell",
"description": "Create table and interact with data using PostgreSQL's shell interface"
},
"vcoreMongo": {
"title": "Mongo Shell",
"description": "Create a collection and interact with data using MongoDB's shell interface"
}
},
"teachingBubble": {
"newToPostgres": {
"headline": "New to Cosmos DB PGSQL?",
"body": "Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can find sample data, query."
},
"resetPassword": {
"headline": "Create your password",
"body": "If you haven't changed your password yet, change it now."
},
"coachMark": {
"headline": "Start with sample {{collectionName}}",
"body": "You will be guided to create a sample container with sample data, then we will give you a tour of data explorer. You can also cancel launching this tour and explore yourself"
}
},
"sections": {
"recents": "Recents",
"clearRecents": "Clear Recents",
"top3": "Top 3 things you need to know",
"learningResources": "Learning Resources",
"nextSteps": "Next steps",
"tipsAndLearnMore": "Tips & learn more",
"notebook": "Notebook",
"needHelp": "Need help?"
},
"top3Items": {
"sql": {
"advancedModeling": {
"title": "Advanced Modeling Patterns",
"description": "Learn advanced strategies to optimize your database."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn to apply data model and partitioning strategies."
},
"resourcePlanning": {
"title": "Plan Your Resource Requirements",
"description": "Get to know the different configuration choices."
}
},
"mongo": {
"whatIsMongo": {
"title": "What is the MongoDB API?",
"description": "Understand Azure Cosmos DB for MongoDB and its features."
},
"features": {
"title": "Features and Syntax",
"description": "Discover the advantages and features"
},
"migrate": {
"title": "Migrate Your Data",
"description": "Pre-migration steps for moving data"
}
},
"cassandra": {
"buildJavaApp": {
"title": "Build a Java App",
"description": "Create a Java app using an SDK."
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works."
},
"requestUnits": {
"title": "Request Units (RUs)",
"description": "Understand RU charges."
}
},
"gremlin": {
"dataModeling": {
"title": "Data Modeling",
"description": "Graph data modeling recommendations"
},
"partitioning": {
"title": "Partitioning Best Practices",
"description": "Learn how partitioning works"
},
"queryData": {
"title": "Query Data",
"description": "Querying data with Gremlin"
}
},
"tables": {
"whatIsTable": {
"title": "What is the Table API?",
"description": "Understand Azure Cosmos DB for Table and its features"
},
"migrate": {
"title": "Migrate your data",
"description": "Learn how to migrate your data"
},
"faq": {
"title": "Azure Cosmos DB for Table FAQs",
"description": "Common questions about Azure Cosmos DB for Table"
}
}
},
"learningResources": {
"shortcuts": {
"title": "Data Explorer keyboard shortcuts",
"description": "Learn keyboard shortcuts to navigate Data Explorer."
},
"liveTv": {
"title": "Learn the Fundamentals",
"description": "Watch Azure Cosmos DB Live TV show introductory and how to videos."
},
"sql": {
"sdk": {
"title": "Get Started using an SDK",
"description": "Learn about the Azure Cosmos DB SDK."
},
"migrate": {
"title": "Migrate Your Data",
"description": "Migrate data using Azure services and open-source solutions."
}
},
"mongo": {
"nodejs": {
"title": "Build an app with Node.js",
"description": "Create a Node.js app."
},
"gettingStarted": {
"title": "Getting Started Guide",
"description": "Learn the basics to get started."
}
},
"cassandra": {
"createContainer": {
"title": "Create a Container",
"description": "Get to know the create a container options."
},
"throughput": {
"title": "Provision Throughput",
"description": "Learn how to configure throughput."
}
},
"gremlin": {
"getStarted": {
"title": "Get Started ",
"description": "Create, query, and traverse using the Gremlin console"
},
"importData": {
"title": "Import Graph Data",
"description": "Learn Bulk ingestion data using BulkExecutor"
}
},
"tables": {
"dotnet": {
"title": "Build a .NET App",
"description": "How to access Azure Cosmos DB for Table from a .NET app."
},
"java": {
"title": "Build a Java App",
"description": "Create a Azure Cosmos DB for Table app with Java SDK "
}
}
},
"nextStepItems": {
"postgres": {
"dataModeling": "Data Modeling",
"distributionColumn": "How to choose a Distribution Column",
"buildApps": "Build Apps with Python/Java/Django"
},
"vcoreMongo": {
"migrateData": "Migrate Data",
"vectorSearch": "Build AI apps with Vector Search",
"buildApps": "Build Apps with Nodejs"
}
},
"learnMoreItems": {
"postgres": {
"performanceTuning": "Performance Tuning",
"diagnosticQueries": "Useful Diagnostic Queries",
"sqlReference": "Distributed SQL Reference"
},
"vcoreMongo": {
"vectorSearch": "Vector Search",
"textIndexing": "Text Indexing",
"troubleshoot": "Troubleshoot common issues"
}
},
"fabric": {
"buildTitle": "Build your database",
"useTitle": "Use your database",
"newContainer": {
"title": "New container",
"description": "Create a destination container to store your data"
},
"sampleData": {
"title": "Sample Data",
"description": "Load sample data in your database"
},
"sampleVectorData": {
"title": "Sample Vector Data",
"description": "Load sample vector data with text-embedding-ada-002"
},
"appDevelopment": {
"title": "App development",
"description": "Start here to use an SDK to build your apps"
},
"sampleGallery": {
"title": "Sample Gallery",
"description": "Get real-world end-to-end samples"
}
},
"sampleDataDialog": {
"title": "Sample Data",
"startButton": "Start",
"createPrompt": "Create a container \"{{containerName}}\" and import sample data into it. This may take a few minutes.",
"creatingContainer": "Creating container \"{{containerName}}\"...",
"importingData": "Importing data into \"{{containerName}}\"...",
"success": "Successfully created \"{{containerName}}\" with sample data.",
"errorContainerExists": "The container \"{{containerName}}\" in database \"{{databaseName}}\" already exists. Please delete it and retry.",
"errorCreateContainer": "Failed to create container: {{error}}",
"errorImportData": "Failed to import data: {{error}}"
}
}
}

View File

@@ -119,6 +119,9 @@ const App = (): JSX.Element => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [explorer]);
// Track interactive phase for both ContainerCopyPanel and DivExplorer paths
useInteractive(MetricScenario.ApplicationLoad, !!config);
if (!explorer) {
return <LoadingExplorer />;
}
@@ -145,7 +148,6 @@ const App = (): JSX.Element => {
const DivExplorer: React.FC<{ explorer: Explorer }> = ({ explorer }) => {
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
const isCopilotCarouselOpen = useCarousel((state) => state.showCopilotCarousel);
useInteractive(MetricScenario.ApplicationLoad);
return (
<div

View File

@@ -0,0 +1,182 @@
import { ARMError } from "../Utils/arm/request";
import { isExpectedError } from "./ErrorClassification";
describe("ErrorClassification", () => {
describe("isExpectedError", () => {
describe("ARMError with expected codes", () => {
it("returns true for AuthorizationFailed code", () => {
const error = new ARMError("Authorization failed");
error.code = "AuthorizationFailed";
expect(isExpectedError(error)).toBe(true);
});
it("returns true for Forbidden code", () => {
const error = new ARMError("Forbidden");
error.code = "Forbidden";
expect(isExpectedError(error)).toBe(true);
});
it("returns true for Unauthorized code", () => {
const error = new ARMError("Unauthorized");
error.code = "Unauthorized";
expect(isExpectedError(error)).toBe(true);
});
it("returns true for InvalidAuthenticationToken code", () => {
const error = new ARMError("Invalid token");
error.code = "InvalidAuthenticationToken";
expect(isExpectedError(error)).toBe(true);
});
it("returns true for ExpiredAuthenticationToken code", () => {
const error = new ARMError("Token expired");
error.code = "ExpiredAuthenticationToken";
expect(isExpectedError(error)).toBe(true);
});
it("returns true for numeric 401 code", () => {
const error = new ARMError("Unauthorized");
error.code = 401;
expect(isExpectedError(error)).toBe(true);
});
it("returns true for numeric 403 code", () => {
const error = new ARMError("Forbidden");
error.code = 403;
expect(isExpectedError(error)).toBe(true);
});
it("returns false for unexpected ARM error code", () => {
const error = new ARMError("Internal error");
error.code = "InternalServerError";
expect(isExpectedError(error)).toBe(false);
});
it("returns false for numeric 500 code", () => {
const error = new ARMError("Server error");
error.code = 500;
expect(isExpectedError(error)).toBe(false);
});
});
describe("MSAL AuthError with expected errorCodes", () => {
it("returns true for popup_window_error", () => {
const error = { errorCode: "popup_window_error", message: "Popup blocked" };
expect(isExpectedError(error)).toBe(true);
});
it("returns true for interaction_required", () => {
const error = { errorCode: "interaction_required", message: "User interaction required" };
expect(isExpectedError(error)).toBe(true);
});
it("returns true for user_cancelled", () => {
const error = { errorCode: "user_cancelled", message: "User cancelled" };
expect(isExpectedError(error)).toBe(true);
});
it("returns true for consent_required", () => {
const error = { errorCode: "consent_required", message: "Consent required" };
expect(isExpectedError(error)).toBe(true);
});
it("returns true for login_required", () => {
const error = { errorCode: "login_required", message: "Login required" };
expect(isExpectedError(error)).toBe(true);
});
it("returns true for no_account_error", () => {
const error = { errorCode: "no_account_error", message: "No account" };
expect(isExpectedError(error)).toBe(true);
});
it("returns false for unexpected MSAL error code", () => {
const error = { errorCode: "unknown_error", message: "Unknown" };
expect(isExpectedError(error)).toBe(false);
});
});
describe("HTTP status codes", () => {
it("returns true for error with status 401", () => {
const error = { status: 401, message: "Unauthorized" };
expect(isExpectedError(error)).toBe(true);
});
it("returns true for error with status 403", () => {
const error = { status: 403, message: "Forbidden" };
expect(isExpectedError(error)).toBe(true);
});
it("returns false for error with status 500", () => {
const error = { status: 500, message: "Internal Server Error" };
expect(isExpectedError(error)).toBe(false);
});
it("returns false for error with status 404", () => {
const error = { status: 404, message: "Not Found" };
expect(isExpectedError(error)).toBe(false);
});
});
describe("Firewall error message pattern", () => {
it("returns true for firewall error in Error message", () => {
const error = new Error("Request blocked by firewall");
expect(isExpectedError(error)).toBe(true);
});
it("returns true for IP not allowed error", () => {
const error = new Error("Client IP address is not allowed");
expect(isExpectedError(error)).toBe(true);
});
it("returns true for ip not allowed (no 'address')", () => {
const error = new Error("Your ip not allowed to access this resource");
expect(isExpectedError(error)).toBe(true);
});
it("returns true for string error with firewall", () => {
expect(isExpectedError("firewall rules prevent access")).toBe(true);
});
it("returns true for case-insensitive firewall match", () => {
const error = new Error("FIREWALL blocked request");
expect(isExpectedError(error)).toBe(true);
});
it("returns false for unrelated error message", () => {
const error = new Error("Database connection failed");
expect(isExpectedError(error)).toBe(false);
});
});
describe("Edge cases", () => {
it("returns false for null", () => {
expect(isExpectedError(null)).toBe(false);
});
it("returns false for undefined", () => {
expect(isExpectedError(undefined)).toBe(false);
});
it("returns false for empty object", () => {
expect(isExpectedError({})).toBe(false);
});
it("returns false for plain Error without expected patterns", () => {
const error = new Error("Something went wrong");
expect(isExpectedError(error)).toBe(false);
});
it("returns false for string without firewall pattern", () => {
expect(isExpectedError("Generic error occurred")).toBe(false);
});
it("handles error with multiple matching criteria", () => {
// ARMError with both code and firewall message
const error = new ARMError("Request blocked by firewall");
error.code = "Forbidden";
expect(isExpectedError(error)).toBe(true);
});
});
});
});

View File

@@ -0,0 +1,109 @@
import { ARMError } from "../Utils/arm/request";
/**
* Expected error codes that should not mark scenarios as unhealthy.
* These represent expected failures like auth issues, permission errors, and user actions.
*/
// ARM error codes (string)
const EXPECTED_ARM_ERROR_CODES: Set<string> = new Set([
"AuthorizationFailed",
"Forbidden",
"Unauthorized",
"AuthenticationFailed",
"InvalidAuthenticationToken",
"ExpiredAuthenticationToken",
"AuthorizationPermissionMismatch",
]);
// HTTP status codes that indicate expected failures
const EXPECTED_HTTP_STATUS_CODES: Set<number> = new Set([
401, // Unauthorized
403, // Forbidden
]);
// MSAL error codes (string)
const EXPECTED_MSAL_ERROR_CODES: Set<string> = new Set([
"popup_window_error",
"interaction_required",
"user_cancelled",
"consent_required",
"login_required",
"no_account_error",
"monitor_window_timeout",
"empty_window_error",
]);
// Firewall error message pattern (only case where we check message content)
const FIREWALL_ERROR_PATTERN = /firewall|ip\s*(address)?\s*(is\s*)?not\s*allowed/i;
/**
* Interface for MSAL AuthError-like objects
*/
interface MsalAuthError {
errorCode?: string;
}
/**
* Interface for errors with HTTP status
*/
interface HttpError {
status?: number;
}
/**
* Determines if an error is an expected failure that should not mark the scenario as unhealthy.
*
* Expected failures include:
* - Authentication/authorization errors (user not logged in, permissions)
* - Firewall blocking errors
* - User-cancelled operations
*
* @param error - The error to classify
* @returns true if the error is expected and should not affect health metrics
*/
export function isExpectedError(error: unknown): boolean {
if (!error) {
return false;
}
// Check ARMError code
if (error instanceof ARMError && error.code !== undefined) {
if (typeof error.code === "string" && EXPECTED_ARM_ERROR_CODES.has(error.code)) {
return true;
}
if (typeof error.code === "number" && EXPECTED_HTTP_STATUS_CODES.has(error.code)) {
return true;
}
}
// Check for MSAL AuthError (has errorCode property)
const msalError = error as MsalAuthError;
if (msalError.errorCode && typeof msalError.errorCode === "string") {
if (EXPECTED_MSAL_ERROR_CODES.has(msalError.errorCode)) {
return true;
}
}
// Check HTTP status on generic errors
const httpError = error as HttpError;
if (httpError.status && typeof httpError.status === "number") {
if (EXPECTED_HTTP_STATUS_CODES.has(httpError.status)) {
return true;
}
}
// Check for firewall error in message (the only message-based check)
if (error instanceof Error && error.message) {
if (FIREWALL_ERROR_PATTERN.test(error.message)) {
return true;
}
}
// Check for string errors with firewall pattern
if (typeof error === "string" && FIREWALL_ERROR_PATTERN.test(error)) {
return true;
}
return false;
}

View File

@@ -15,6 +15,11 @@ export const reportUnhealthy = (scenario: MetricScenario, platform: Platform, ap
send({ platform, api, scenario, healthy: false });
const send = async (event: MetricEvent): Promise<Response> => {
// Skip metrics emission during local development
if (process.env.NODE_ENV === "development") {
return Promise.resolve(new Response(null, { status: 200 }));
}
const url = createUri(configContext?.PORTAL_BACKEND_ENDPOINT, RELATIVE_PATH);
const authHeader = getAuthorizationHeader();

View File

@@ -0,0 +1,231 @@
/**
* @jest-environment jsdom
*/
import { configContext } from "../ConfigContext";
import { updateUserContext } from "../UserContext";
import MetricScenario, { reportHealthy, reportUnhealthy } from "./MetricEvents";
import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
import { scenarioMonitor } from "./ScenarioMonitor";
// Mock the MetricEvents module
jest.mock("./MetricEvents", () => ({
__esModule: true,
default: {
ApplicationLoad: "ApplicationLoad",
DatabaseLoad: "DatabaseLoad",
},
reportHealthy: jest.fn().mockResolvedValue({ ok: true }),
reportUnhealthy: jest.fn().mockResolvedValue({ ok: true }),
}));
// Mock configContext
jest.mock("../ConfigContext", () => ({
configContext: {
platform: "Portal",
PORTAL_BACKEND_ENDPOINT: "https://test.portal.azure.com",
},
Platform: {
Portal: "Portal",
Hosted: "Hosted",
Emulator: "Emulator",
Fabric: "Fabric",
},
}));
describe("ScenarioMonitor", () => {
beforeEach(() => {
jest.clearAllMocks();
// Use legacy fake timers to avoid conflicts with performance API
jest.useFakeTimers({ legacyFakeTimers: true });
// Ensure performance mock is available (setupTests.ts sets this but fake timers may override)
if (typeof performance.mark !== "function") {
Object.defineProperty(global, "performance", {
writable: true,
configurable: true,
value: {
mark: jest.fn(),
measure: jest.fn(),
clearMarks: jest.fn(),
clearMeasures: jest.fn(),
getEntriesByName: jest.fn().mockReturnValue([{ startTime: 0 }]),
getEntriesByType: jest.fn().mockReturnValue([]),
now: jest.fn(() => Date.now()),
timeOrigin: Date.now(),
},
});
}
// Reset userContext
updateUserContext({
apiType: "SQL",
});
// Reset the scenario monitor to clear any previous state
scenarioMonitor.reset();
});
afterEach(() => {
// Reset scenarios before switching to real timers
scenarioMonitor.reset();
jest.useRealTimers();
});
describe("markExpectedFailure", () => {
it("sets hasExpectedFailure flag on active scenarios", () => {
// Start a scenario
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Mark expected failure
scenarioMonitor.markExpectedFailure();
// Let timeout fire - should emit healthy because of expected failure
jest.advanceTimersByTime(10000);
expect(reportHealthy).toHaveBeenCalledWith(MetricScenario.ApplicationLoad, configContext.platform, "SQL");
expect(reportUnhealthy).not.toHaveBeenCalled();
});
it("sets flag on multiple active scenarios", () => {
// Start two scenarios
scenarioMonitor.start(MetricScenario.ApplicationLoad);
scenarioMonitor.start(MetricScenario.DatabaseLoad);
// Mark expected failure - should affect both
scenarioMonitor.markExpectedFailure();
// Let timeouts fire
jest.advanceTimersByTime(10000);
expect(reportHealthy).toHaveBeenCalledTimes(2);
expect(reportUnhealthy).not.toHaveBeenCalled();
});
it("does not affect already emitted scenarios", () => {
// Start scenario
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Complete all phases to emit
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, ApplicationMetricPhase.ExplorerInitialized);
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, CommonMetricPhase.Interactive);
// Now mark expected failure - should not change anything
scenarioMonitor.markExpectedFailure();
// Healthy was called when phases completed
expect(reportHealthy).toHaveBeenCalledTimes(1);
});
});
describe("timeout behavior", () => {
it("emits unhealthy on timeout without expected failure", () => {
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Let timeout fire without marking expected failure
jest.advanceTimersByTime(10000);
expect(reportUnhealthy).toHaveBeenCalledWith(MetricScenario.ApplicationLoad, configContext.platform, "SQL");
expect(reportHealthy).not.toHaveBeenCalled();
});
it("emits healthy on timeout with expected failure", () => {
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Mark expected failure
scenarioMonitor.markExpectedFailure();
// Let timeout fire
jest.advanceTimersByTime(10000);
expect(reportHealthy).toHaveBeenCalledWith(MetricScenario.ApplicationLoad, configContext.platform, "SQL");
expect(reportUnhealthy).not.toHaveBeenCalled();
});
it("emits healthy even with partial phase completion and expected failure", () => {
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Complete one phase
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, ApplicationMetricPhase.ExplorerInitialized);
// Mark expected failure
scenarioMonitor.markExpectedFailure();
// Let timeout fire (Interactive phase not completed)
jest.advanceTimersByTime(10000);
expect(reportHealthy).toHaveBeenCalled();
expect(reportUnhealthy).not.toHaveBeenCalled();
});
});
describe("failPhase behavior", () => {
it("emits unhealthy immediately on unexpected failure", () => {
scenarioMonitor.start(MetricScenario.DatabaseLoad);
// Fail a phase (simulating unexpected error)
scenarioMonitor.failPhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabasesFetched);
// Should emit unhealthy immediately, not wait for timeout
expect(reportUnhealthy).toHaveBeenCalledWith(MetricScenario.DatabaseLoad, configContext.platform, "SQL");
});
it("does not emit twice after failPhase and timeout", () => {
scenarioMonitor.start(MetricScenario.DatabaseLoad);
// Fail a phase
scenarioMonitor.failPhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabasesFetched);
// Let timeout fire
jest.advanceTimersByTime(10000);
// Should only have emitted once (from failPhase)
expect(reportUnhealthy).toHaveBeenCalledTimes(1);
});
});
describe("completePhase behavior", () => {
it("emits healthy when all phases complete", () => {
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Complete all required phases
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, ApplicationMetricPhase.ExplorerInitialized);
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, CommonMetricPhase.Interactive);
expect(reportHealthy).toHaveBeenCalledWith(MetricScenario.ApplicationLoad, configContext.platform, "SQL");
});
it("does not emit until all phases complete", () => {
scenarioMonitor.start(MetricScenario.ApplicationLoad);
// Complete only one phase
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, ApplicationMetricPhase.ExplorerInitialized);
expect(reportHealthy).not.toHaveBeenCalled();
expect(reportUnhealthy).not.toHaveBeenCalled();
});
});
describe("scenario isolation", () => {
it("expected failure on one scenario does not affect others after completion", () => {
// Start both scenarios
scenarioMonitor.start(MetricScenario.ApplicationLoad);
scenarioMonitor.start(MetricScenario.DatabaseLoad);
// Complete ApplicationLoad
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, ApplicationMetricPhase.ExplorerInitialized);
scenarioMonitor.completePhase(MetricScenario.ApplicationLoad, CommonMetricPhase.Interactive);
// Now mark expected failure - should only affect DatabaseLoad
scenarioMonitor.markExpectedFailure();
// Let DatabaseLoad timeout
jest.advanceTimersByTime(10000);
// ApplicationLoad emitted healthy on completion
// DatabaseLoad emits healthy on timeout (expected failure)
expect(reportHealthy).toHaveBeenCalledTimes(2);
expect(reportUnhealthy).not.toHaveBeenCalled();
});
});
});

View File

@@ -21,6 +21,7 @@ interface InternalScenarioContext {
phases: Map<MetricPhase, PhaseContext>; // Track start/end for each phase
timeoutId?: number;
emitted: boolean;
hasExpectedFailure: boolean; // Flag for expected failures (auth, firewall, etc.)
}
class ScenarioMonitor {
@@ -55,6 +56,13 @@ class ScenarioMonitor {
});
}
private devLog(msg: string) {
if (process.env.NODE_ENV === "development") {
// eslint-disable-next-line no-console
console.log(`[Metrics] ${msg}`);
}
}
start(scenario: MetricScenario) {
if (this.contexts.has(scenario)) {
return;
@@ -75,6 +83,7 @@ class ScenarioMonitor {
failed: new Set<MetricPhase>(),
phases: new Map<MetricPhase, PhaseContext>(),
emitted: false,
hasExpectedFailure: false,
};
// Start all required phases at scenario start time
@@ -84,6 +93,10 @@ class ScenarioMonitor {
ctx.phases.set(phase, { startMarkName: phaseStartMarkName });
});
this.devLog(
`scenario_start: ${scenario} | phases=${config.requiredPhases.join(", ")} | timeout=${config.timeoutMs}ms`,
);
traceMark(Action.MetricsScenario, {
event: "scenario_start",
scenario,
@@ -91,7 +104,28 @@ class ScenarioMonitor {
timeoutMs: config.timeoutMs,
});
ctx.timeoutId = window.setTimeout(() => this.emit(ctx, false, true), config.timeoutMs);
ctx.timeoutId = window.setTimeout(() => {
const missingPhases = ctx.config.requiredPhases.filter((p) => !ctx.completed.has(p));
this.devLog(
`timeout: ${scenario} | missing=[${missingPhases.join(", ")}] | completed=[${Array.from(ctx.completed).join(
", ",
)}] | documentHidden=${document.hidden} | hasExpectedFailure=${ctx.hasExpectedFailure}`,
);
traceMark(Action.MetricsScenario, {
event: "scenario_timeout",
scenario,
missingPhases: missingPhases.join(","),
completedPhases: Array.from(ctx.completed).join(","),
documentHidden: document.hidden,
hasExpectedFailure: ctx.hasExpectedFailure,
});
// If an expected failure occurred (auth, firewall, etc.), emit healthy instead of unhealthy
const healthy = ctx.hasExpectedFailure;
this.emit(ctx, healthy, true);
}, config.timeoutMs);
this.contexts.set(scenario, ctx);
}
@@ -130,6 +164,12 @@ class ScenarioMonitor {
const endTimeISO = endEntry ? new Date(navigationStart + endEntry.startTime).toISOString() : undefined;
const durationMs = startEntry && endEntry ? endEntry.startTime - startEntry.startTime : undefined;
this.devLog(
`phase_complete: ${scenario}.${phase} | ${
durationMs !== null && durationMs !== undefined ? `${Math.round(durationMs)}ms` : "?"
} | ${ctx.completed.size}/${ctx.config.requiredPhases.length} phases`,
);
traceSuccess(Action.MetricsScenario, {
event: "phase_complete",
scenario,
@@ -149,6 +189,13 @@ class ScenarioMonitor {
return;
}
// If an expected failure was flagged (auth, firewall, etc.), treat as success.
if (ctx.hasExpectedFailure) {
this.devLog(`phase_fail: ${scenario}.${phase} — expected failure, completing as healthy`);
this.completePhase(scenario, phase);
return;
}
// Mark the explicitly failed phase
performance.mark(`scenario_${scenario}_${phase}_failed`);
ctx.failed.add(phase);
@@ -163,6 +210,12 @@ class ScenarioMonitor {
// Build a snapshot with failure info
const failureSnapshot = this.buildSnapshot(ctx, { final: false, timedOut: false });
this.devLog(
`phase_fail: ${scenario}.${phase} | failed=[${Array.from(ctx.failed).join(", ")}] | completed=[${Array.from(
ctx.completed,
).join(", ")}]`,
);
traceFailure(Action.MetricsScenario, {
event: "phase_fail",
scenario,
@@ -171,10 +224,28 @@ class ScenarioMonitor {
completedPhases: Array.from(ctx.completed).join(","),
});
// Emit unhealthy immediately
// Emit unhealthy immediately for unexpected failures
this.emit(ctx, false, false, failureSnapshot);
}
/**
* Marks that an expected failure occurred (auth, firewall, permissions, etc.).
* When the scenario times out with this flag set, it will emit healthy instead of unhealthy.
* This is called automatically from handleError when an expected error is detected.
*/
markExpectedFailure() {
// Set the flag on all active (non-emitted) scenarios
this.contexts.forEach((ctx) => {
if (!ctx.emitted) {
ctx.hasExpectedFailure = true;
traceMark(Action.MetricsScenario, {
event: "expected_failure_marked",
scenario: ctx.scenario,
});
}
});
}
private tryEmitIfReady(ctx: InternalScenarioContext) {
const allDone = ctx.config.requiredPhases.every((p) => ctx.completed.has(p));
if (!allDone) {
@@ -234,6 +305,7 @@ class ScenarioMonitor {
scenario: ctx.scenario,
healthy,
timedOut,
documentHidden: document.hidden,
platform,
api,
durationMs: finalSnapshot.durationMs,
@@ -246,8 +318,22 @@ class ScenarioMonitor {
ttfb: finalSnapshot.vitals?.ttfb,
});
this.devLog(
`scenario_end: ${ctx.scenario} | ${healthy ? "healthy" : "unhealthy"} | ${
timedOut ? "timed out" : `${Math.round(finalSnapshot.durationMs)}ms`
} | ${JSON.stringify({
completedPhases: finalSnapshot.completed.join(", "),
failedPhases: finalSnapshot.failedPhases?.join(", ") || "none",
platform,
api,
phaseTimings: finalSnapshot.phaseTimings,
vitals: finalSnapshot.vitals,
})}`,
);
// Call portal backend health metrics endpoint
if (healthy && !timedOut) {
// If healthy is true (either completed successfully or timeout with expected failure), report healthy
if (healthy) {
reportHealthy(ctx.scenario, platform, api);
} else {
reportUnhealthy(ctx.scenario, platform, api);
@@ -302,6 +388,19 @@ class ScenarioMonitor {
phaseTimings,
};
}
/**
* Reset all scenarios (for testing purposes only).
* Clears all active contexts and their timeouts.
*/
reset() {
this.contexts.forEach((ctx) => {
if (ctx.timeoutId) {
clearTimeout(ctx.timeoutId);
}
});
this.contexts.clear();
}
}
export const scenarioMonitor = new ScenarioMonitor();

View File

@@ -1,20 +1,26 @@
import React from "react";
import MetricScenario from "./MetricEvents";
import { useMetricScenario } from "./MetricScenarioProvider";
import { scenarioMonitor } from "./ScenarioMonitor";
import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
/**
* Hook to automatically complete the Interactive phase when the component becomes interactive.
* Uses requestAnimationFrame to complete after the browser has painted.
*
* Calls scenarioMonitor directly (not via React context) so that the effect dependencies
* are only [scenario, enabled] — both stable primitives. This prevents re-renders from
* cancelling the pending rAF due to an unstable context function reference.
*/
export function useInteractive(scenario: MetricScenario) {
const { completePhase } = useMetricScenario();
export function useInteractive(scenario: MetricScenario, enabled = true) {
React.useEffect(() => {
requestAnimationFrame(() => {
completePhase(scenario, CommonMetricPhase.Interactive);
if (!enabled) {
return undefined;
}
const id = requestAnimationFrame(() => {
scenarioMonitor.completePhase(scenario, CommonMetricPhase.Interactive);
});
}, [scenario, completePhase]);
return () => cancelAnimationFrame(id);
}, [scenario, enabled]);
}
/**
@@ -22,18 +28,20 @@ export function useInteractive(scenario: MetricScenario) {
* Tracks tree rendering and completes Interactive phase.
* Only completes DatabaseTreeRendered if the database fetch was successful.
* Note: Scenario must be started before databases are fetched (in refreshExplorer).
*
* Calls scenarioMonitor directly (not via React context) for the same stability reason
* as useInteractive — avoids effect re-runs from unstable context function references.
*/
export function useDatabaseLoadScenario(databaseTreeNodes: unknown[], fetchSucceeded: boolean) {
const { completePhase } = useMetricScenario();
const hasCompletedTreeRenderRef = React.useRef(false);
// Track DatabaseTreeRendered phase (only if fetch succeeded)
React.useEffect(() => {
if (!hasCompletedTreeRenderRef.current && fetchSucceeded) {
hasCompletedTreeRenderRef.current = true;
completePhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabaseTreeRendered);
scenarioMonitor.completePhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabaseTreeRendered);
}
}, [databaseTreeNodes, fetchSucceeded, completePhase]);
}, [databaseTreeNodes, fetchSucceeded]);
// Track Interactive phase
useInteractive(MetricScenario.DatabaseLoad);

View File

@@ -37,7 +37,7 @@ const requestFabricToken = async (): Promise<void> => {
scheduleRefreshFabricToken();
} catch (error) {
logConsoleError(error as string);
logConsoleError(error instanceof Error ? error.message : String(error));
throw error;
} finally {
lastRequestTimestamp = undefined;

View File

@@ -9,7 +9,6 @@ import {
Stack,
Text,
} from "@fluentui/react";
import { TFunction } from "i18next";
import promiseRetry, { AbortError, Options } from "p-retry";
import React from "react";
import { WithTranslation } from "react-i18next";
@@ -81,7 +80,7 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
private smartUiGeneratorClassName: string;
private retryIntervalInMs: number;
private retryOptions: Options;
private translationFunction: TFunction;
private translationFunction: (key: string) => string;
componentDidMount(): void {
this.performRefresh().then(() => {
@@ -119,7 +118,7 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
this.retryOptions = { forever: true, maxTimeout: this.retryIntervalInMs, minTimeout: this.retryIntervalInMs };
// translation function passed to SelfServeComponent
this.translationFunction = this.props.t;
this.translationFunction = this.props.t as (key: string) => string;
}
private onError = (hasErrors: boolean): void => {

View File

@@ -8,6 +8,8 @@ import * as Logger from "../Common/Logger";
import { configContext } from "../ConfigContext";
import { DatabaseAccount } from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { isExpectedError } from "../Metrics/ErrorClassification";
import { scenarioMonitor } from "../Metrics/ScenarioMonitor";
import { trace, traceFailure } from "../Shared/Telemetry/TelemetryProcessor";
import { UserContext, userContext } from "../UserContext";
@@ -127,6 +129,10 @@ export async function acquireMsalTokenForAccount(
acquireTokenType: silent ? "silent" : "interactive",
errorMessage: JSON.stringify(error),
});
// Mark expected failure for health metrics so timeout emits healthy
if (isExpectedError(error)) {
scenarioMonitor.markExpectedFailure();
}
throw error;
}
} else {
@@ -169,7 +175,10 @@ export async function acquireTokenWithMsal(
acquireTokenType: "interactive",
errorMessage: JSON.stringify(interactiveError),
});
// Mark expected failure for health metrics so timeout emits healthy
if (isExpectedError(interactiveError)) {
scenarioMonitor.markExpectedFailure();
}
throw interactiveError;
}
} else {
@@ -178,7 +187,10 @@ export async function acquireTokenWithMsal(
acquireTokenType: "silent",
errorMessage: JSON.stringify(silentError),
});
// Mark expected failure for health metrics so timeout emits healthy
if (isExpectedError(silentError)) {
scenarioMonitor.markExpectedFailure();
}
throw silentError;
}
}

View File

@@ -4,7 +4,12 @@ import * as sinon from "sinon";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import * as QueryUtils from "./QueryUtils";
import { defaultQueryFields, extractPartitionKeyValues, getValueForPath } from "./QueryUtils";
import {
defaultQueryFields,
extractPartitionKeyValues,
getValueForPath,
stripDoubleQuotesFromSegment,
} from "./QueryUtils";
const documentContent = {
"Volcano Name": "Adams",
@@ -279,5 +284,97 @@ describe("Query Utils", () => {
expect(partitionKeyValues.length).toBe(2);
expect(partitionKeyValues).toEqual([null, {}]);
});
it("should extract partition key value when path has enclosing double quotes", () => {
const docWithSpecialKey = {
id: "test-id",
"partition-key": "some-value",
};
const partitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.Hash,
paths: ['/"partition-key"'],
};
const partitionKeyValues: PartitionKey[] = extractPartitionKeyValues(docWithSpecialKey, partitionKeyDefinition);
expect(partitionKeyValues.length).toBe(1);
expect(partitionKeyValues[0]).toEqual("some-value");
});
it("should extract nested partition key value when path segments have enclosing double quotes", () => {
const docWithSpecialKey = {
id: "test-id",
"my-field": {
"sub-field": 42,
},
};
const partitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.Hash,
paths: ['/"my-field"/"sub-field"'],
};
const partitionKeyValues: PartitionKey[] = extractPartitionKeyValues(docWithSpecialKey, partitionKeyDefinition);
expect(partitionKeyValues.length).toBe(1);
expect(partitionKeyValues[0]).toEqual(42);
});
it("should return {} for missing double-quoted partition key", () => {
const docWithSpecialKey = {
id: "test-id",
};
const partitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.Hash,
paths: ['/"partition-key"'],
};
const partitionKeyValues: PartitionKey[] = extractPartitionKeyValues(docWithSpecialKey, partitionKeyDefinition);
expect(partitionKeyValues.length).toBe(1);
expect(partitionKeyValues[0]).toEqual({});
});
it("should handle multi-hash with mixed quoted and unquoted paths", () => {
const doc = {
id: "test-id",
Country: "Japan",
"partition-key": "hello",
};
const partitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.MultiHash,
paths: ["/Country", '/"partition-key"'],
};
const partitionKeyValues: PartitionKey[] = extractPartitionKeyValues(doc, partitionKeyDefinition);
expect(partitionKeyValues.length).toBe(2);
expect(partitionKeyValues).toEqual(["Japan", "hello"]);
});
});
describe("stripDoubleQuotesFromSegment", () => {
it("should strip enclosing double quotes", () => {
expect(stripDoubleQuotesFromSegment('"partition-key"')).toBe("partition-key");
});
it("should not strip if only opening quote", () => {
expect(stripDoubleQuotesFromSegment('"partition-key')).toBe('"partition-key');
});
it("should not strip if only closing quote", () => {
expect(stripDoubleQuotesFromSegment('partition-key"')).toBe('partition-key"');
});
it("should return empty string when stripping quotes from empty quoted string", () => {
expect(stripDoubleQuotesFromSegment('""')).toBe("");
});
it("should not modify unquoted segments", () => {
expect(stripDoubleQuotesFromSegment("Country")).toBe("Country");
});
it("should not strip single quotes", () => {
expect(stripDoubleQuotesFromSegment("'partition-key'")).toBe("'partition-key'");
});
});
});

View File

@@ -116,6 +116,17 @@ export const queryPagesUntilContentPresent = async (
return await doRequest(firstItemIndex);
};
/**
* Strips enclosing double quotes from a partition key path segment.
* e.g., '"partition-key"' -> 'partition-key'
*/
export const stripDoubleQuotesFromSegment = (segment: string): string => {
if (segment.length >= 2 && segment.charAt(0) === '"' && segment.charAt(segment.length - 1) === '"') {
return segment.slice(1, -1);
}
return segment;
};
/* eslint-disable @typescript-eslint/no-explicit-any */
export const getValueForPath = (content: any, pathSegments: string[]): any => {
if (pathSegments.length === 0) {
@@ -146,7 +157,7 @@ export const extractPartitionKeyValues = (
const partitionKeyValues: PartitionKey[] = [];
partitionKeyDefinition.paths.forEach((partitionKeyPath: string) => {
const pathSegments: string[] = partitionKeyPath.substring(1).split("/");
const pathSegments: string[] = partitionKeyPath.substring(1).split("/").map(stripDoubleQuotesFromSegment);
const value = getValueForPath(documentContent, pathSegments);
if (value !== undefined) {

View File

@@ -1,7 +1,7 @@
import fetch, { Headers } from "node-fetch";
import { AuthType } from "../../AuthType";
import { updateUserContext } from "../../UserContext";
import { armRequest } from "./request";
import fetch from "node-fetch";
import { updateUserContext } from "../../UserContext";
import { AuthType } from "../../AuthType";
interface Global {
Headers: unknown;

View File

@@ -1,16 +1,21 @@
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import resourcesToBackend from "i18next-resources-to-backend";
import { initReactI18next } from "react-i18next";
i18n
.use(LanguageDetector)
.use(resourcesToBackend((lng: string, ns: string) => import(`./Localization/${lng}/${ns}.json`)))
.use(initReactI18next)
.init({
fallbackLng: "en",
defaultNS: "Resources",
ns: ["Resources"],
detection: { order: ["navigator", "cookie", "localStorage", "sessionStorage", "querystring", "htmlTag"] },
debug: process.env.NODE_ENV === "development",
keySeparator: ".",
interpolation: {
escapeValue: false,
formatSeparator: ",",
},
react: {

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: () => {} });
}

View File

@@ -250,7 +250,7 @@ class TreeNode {
// Try three times to wait for the node to expand.
for (let i = 0; i < RETRY_COUNT; i++) {
try {
await tree.waitFor({ state: "visible" });
await tree.waitFor({ state: "visible", timeout: 30000 });
// The tree has expanded, let's get out of here
return true;
} catch {

View File

@@ -9,9 +9,10 @@ import {
TestAccount,
waitForApiResponse,
} from "../../fx";
import { createMultipleTestContainers } from "../../testData";
import { createMultipleTestContainers, TestContainerContext } from "../../testData";
test.describe("Container Copy - Offline Migration", () => {
let contexts: TestContainerContext[];
let page: Page;
let wrapper: Locator;
let panel: Locator;
@@ -22,7 +23,7 @@ test.describe("Container Copy - Offline Migration", () => {
let expectedCopyJobNameInitial: string;
test.beforeEach("Setup for offline migration test", async ({ browser }) => {
await createMultipleTestContainers({ accountType: TestAccount.SQLContainerCopyOnly, containerCount: 2 });
contexts = await createMultipleTestContainers({ accountType: TestAccount.SQLContainerCopyOnly, containerCount: 2 });
page = await browser.newPage();
({ wrapper, frame } = await ContainerCopy.open(page, TestAccount.SQLContainerCopyOnly));
@@ -33,6 +34,7 @@ test.describe("Container Copy - Offline Migration", () => {
test.afterEach("Cleanup after offline migration test", async () => {
await page.unroute(/.*/, (route) => route.continue());
await page.close();
await Promise.all(contexts.map((context) => context?.dispose()));
});
test("Successfully create and manage offline migration copy job", async () => {

View File

@@ -7,9 +7,10 @@ import {
TestAccount,
waitForApiResponse,
} from "../../fx";
import { createMultipleTestContainers } from "../../testData";
import { createMultipleTestContainers, TestContainerContext } from "../../testData";
test.describe("Container Copy - Online Migration", () => {
let contexts: TestContainerContext[];
let page: Page;
let wrapper: Locator;
let panel: Locator;
@@ -17,7 +18,7 @@ test.describe("Container Copy - Online Migration", () => {
let targetAccountName: string;
test.beforeEach("Setup for online migration test", async ({ browser }) => {
await createMultipleTestContainers({ accountType: TestAccount.SQLContainerCopyOnly, containerCount: 2 });
contexts = await createMultipleTestContainers({ accountType: TestAccount.SQLContainerCopyOnly, containerCount: 2 });
page = await browser.newPage();
({ wrapper, frame } = await ContainerCopy.open(page, TestAccount.SQLContainerCopyOnly));
@@ -27,6 +28,7 @@ test.describe("Container Copy - Online Migration", () => {
test.afterEach("Cleanup after online migration test", async () => {
await page.unroute(/.*/, (route) => route.continue());
await page.close();
await Promise.all(contexts.map((context) => context?.dispose()));
});
test("Successfully create and manage online migration copy job", async () => {

View File

@@ -249,4 +249,27 @@ export const documentTestCases: DocumentTestCase[] = [
},
],
},
{
name: "Single Double-Quoted Partition Key",
databaseId: "e2etests-sql-readonly",
containerId: "doubleQuotedPartitionKey",
documents: [
{
documentId: "doubleQuotedPartitionKey",
partitionKeys: [{ key: "/partition-key", value: "doubleQuotedValue" }],
},
{
documentId: "doubleQuotedPartitionKey_empty_string",
partitionKeys: [{ key: "/partition-key", value: "" }],
},
{
documentId: "doubleQuotedPartitionKey_null",
partitionKeys: [{ key: "/partition-key", value: null }],
},
{
documentId: "doubleQuotedPartitionKey_missing",
partitionKeys: [],
},
],
},
];

View File

@@ -1,5 +1,13 @@
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
import { BulkOperationType, Container, CosmosClient, CosmosClientOptions, Database, JSONObject } from "@azure/cosmos";
import {
BulkOperationType,
Container,
CosmosClient,
CosmosClientOptions,
Database,
ErrorResponse,
JSONObject,
} from "@azure/cosmos";
import { Buffer } from "node:buffer";
import { webcrypto } from "node:crypto";
import {
@@ -78,7 +86,14 @@ export class TestContainerContext {
) {}
async dispose() {
await this.database.delete();
try {
await this.database.delete();
} catch (error) {
if (error instanceof ErrorResponse && error.code === 404) {
return; // Resource already deleted, ignore
}
throw error; // Re-throw other errors
}
}
}
@@ -236,8 +251,15 @@ export const setPartitionKeys = (partitionKeys: PartitionKey[]) => {
partitionKeys.forEach((partitionKey) => {
const { key: keyPath, value: keyValue } = partitionKey;
const cleanPath = keyPath.startsWith("/") ? keyPath.slice(1) : keyPath;
const keys = cleanPath.split("/");
let current: Record<string, unknown> = result;
const keys = cleanPath.split("/").map((segment) => {
// Strip enclosing double quotes from partition key path segments
// e.g., '"partition-key"' -> 'partition-key'
if (segment.length >= 2 && segment.charAt(0) === '"' && segment.charAt(segment.length - 1) === '"') {
return segment.slice(1, -1);
}
return segment;
});
let current = result;
keys.forEach((key, index) => {
if (index === keys.length - 1) {

View File

@@ -161,8 +161,7 @@ const propertyToType = (property: Property, prop: string, required: boolean) =>
async function main() {
const response = await fetch(schemaURL);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const schema: any = await response.json();
const schema = await response.json();
// STEP 1: Convert all definitions to TypeScript types and interfaces
for (const definition in schema.definitions) {

Some files were not shown because too many files have changed in this diff Show More