Compare commits

...

3 Commits

Author SHA1 Message Date
Sung-Hyun Kang
0b1b294c06 Commit types 2026-02-05 18:26:05 -06:00
Sung-Hyun Kang
4d1c42f69e Added localization build 2026-02-05 18:22:14 -06: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
13 changed files with 1329 additions and 762 deletions

1873
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.1",
"@microsoft/applicationinsights-web": "2.6.1",
"@nteract/commutable": "7.5.1",
"@nteract/connected-components": "6.8.2",
@@ -57,7 +57,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 +71,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,6 +80,8 @@
"jquery-typeahead": "2.11.1",
"jquery-ui-dist": "1.13.2",
"knockout": "3.5.1",
"lodash": "4.17.23",
"lodash-es": "4.17.23",
"mkdirp": "1.0.4",
"monaco-editor": "0.44.0",
"ms": "2.1.3",
@@ -102,7 +105,7 @@
"react-youtube": "9.0.1",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"sanitize-html": "2.3.3",
"sanitize-html": "2.17.0",
"shell-quote": "1.7.3",
"styled-components": "5.0.1",
"swr": "0.4.0",
@@ -112,11 +115,15 @@
"utility-types": "3.10.0",
"uuid": "9.0.0",
"web-vitals": "4.2.4",
"zustand": "3.5.0",
"ws": "8.17.1"
"ws": "8.17.1",
"zustand": "3.5.0"
},
"overrides": {
"d3-color": "3.1.0",
"cross-spawn": "7.0.6"
},
"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",
@@ -170,6 +177,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",
@@ -195,8 +203,9 @@
"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",
@@ -204,11 +213,11 @@
},
"scripts": {
"postinstall": "patch-package",
"start": "webpack serve --mode development",
"start": "npm run generate:i18n-keys && webpack serve --mode development",
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
"build:dataExplorer:ci": "npm run build:ci",
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
"build:ci": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
"build": "npm run generate:i18n-keys && npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
"build:ci": "npm run generate:i18n-keys && npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
"pack:prod": "webpack --mode production",
"pack:fast": "webpack --mode development --progress",
"copyToConsumers": "node copyToConsumers",
@@ -230,6 +239,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

@@ -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"
@@ -279,14 +280,15 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
@@ -414,7 +416,8 @@
},
"node_modules/http-proxy-middleware": {
"version": "3.0.5",
"license": "MIT",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz",
"integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==",
"dependencies": {
"@types/http-proxy": "^1.17.15",
"debug": "^4.3.6",

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

@@ -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(() => {

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,22 @@
// -----------------------------------------------------------------
// THIS FILE IS AUTO-GENERATED — DO NOT EDIT BY HAND
// Regenerate with: npm run generate:i18n-keys
// -----------------------------------------------------------------
export const Keys = {
splashScreen: {
title: {
/** Welcome to Azure Cosmos DB */
default: "splashScreen.title.default",
/** Welcome to Azure Cosmos DB for PostgreSQL */
postgres: "splashScreen.title.postgres",
/** Welcome to Azure DocumentDB (with MongoDB compatibility) */
vcoreMongo: "splashScreen.title.vcoreMongo",
},
subtitle: {
/** Globally distributed, multi-model database service for any scale */
default: "splashScreen.subtitle.default",
/** Get started with our sample datasets, documentation, and additional tools. */
getStarted: "splashScreen.subtitle.getStarted",
},
},
} as const;

View File

@@ -0,0 +1,13 @@
{
"splashScreen": {
"title": {
"default": "Willkommen bei Azure Cosmos DB",
"postgres": "Willkommen bei Azure Cosmos DB für PostgreSQL",
"vcoreMongo": "Willkommen bei Azure DocumentDB (mit MongoDB-Kompatibilität)"
},
"subtitle": {
"default": "Global verteilter, multimodaler Datenbankdienst für jede Skalierung",
"getStarted": "Beginnen Sie mit unseren Beispieldatensätzen, Dokumentation und zusätzlichen Tools."
}
}
}

View File

@@ -0,0 +1,13 @@
{
"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."
}
}
}

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

@@ -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

@@ -0,0 +1,71 @@
/**
* Generates src/Localization/Keys.generated.ts from en/Resources.json.
*
* Every leaf value becomes its dot-notation key path, with JSDoc annotations
* showing the English translation so developers see real text on hover.
*
* Libraries:
* - values-to-keys — replaces translation values with dot-path keys
* - i18next-resources-for-ts (json2ts) — serialises objects as typed `as const` TS
*
* Usage: node utils/generateI18nKeys.mjs
*/
import { readFileSync, writeFileSync } from "fs";
import { json2ts } from "i18next-resources-for-ts";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";
import { replace } from "values-to-keys";
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");
const INPUT = resolve(ROOT, "src/Localization/en/Resources.json");
const OUTPUT = resolve(ROOT, "src/Localization/Keys.generated.ts");
// ── helpers ────────────────────────────────────────────────────────
/**
* Walk two parallel objects (keyed + original) and produce TS source
* with JSDoc comments showing the English value at every leaf.
*/
function serialiseWithJSDoc(obj, orig, indent = 2) {
const pad = " ".repeat(indent);
const lines = ["{"];
for (const key of Object.keys(obj)) {
const val = obj[key];
const origVal = orig[key];
if (typeof val === "object" && val !== null) {
lines.push(`${pad}${key}: ${serialiseWithJSDoc(val, origVal, indent + 2)},`);
} else {
lines.push(`${pad}/** ${origVal} */`);
lines.push(`${pad}${key}: ${JSON.stringify(val)},`);
}
}
lines.push(`${" ".repeat(Math.max(0, indent - 2))}}`);
return lines.join("\n");
}
// ── main ───────────────────────────────────────────────────────────
// Keep the original English values for JSDoc annotations
const original = JSON.parse(readFileSync(INPUT, "utf-8"));
// Use values-to-keys to replace every leaf value with its dot-path key
const keyed = replace(JSON.parse(readFileSync(INPUT, "utf-8")));
// Use json2ts to verify the shape is valid for `as const` export
// (We still use our own serialiser because json2ts doesn't add JSDoc comments)
json2ts(keyed); // validates structure; throws on malformed input
const banner = `\
// -----------------------------------------------------------------
// THIS FILE IS AUTO-GENERATED — DO NOT EDIT BY HAND
// Regenerate with: npm run generate:i18n-keys
// -----------------------------------------------------------------
`;
const body = `export const Keys = ${serialiseWithJSDoc(keyed, original)} as const;\n`;
writeFileSync(OUTPUT, banner + body, "utf-8");
// eslint-disable-next-line no-console
console.log(`Generated ${OUTPUT}`);