From 2e5c3554790e665c8ee97d95e0b0733b2ecc4862 Mon Sep 17 00:00:00 2001 From: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:32:33 -0600 Subject: [PATCH] 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 --- .gitignore | 1 + package-lock.json | 311 +++++++++++++++++++-- package.json | 7 +- src/@types/i18next.d.ts | 11 + src/Explorer/SplashScreen/SplashScreen.tsx | 14 +- src/Index.tsx | 1 + src/Localization/LocProject.json | 14 + src/Localization/cs/Resources.json | 13 + src/Localization/de/Resources.json | 13 + src/Localization/en/Resources.json | 13 + src/Localization/es/Resources.json | 13 + src/Localization/fr/Resources.json | 13 + src/Localization/hu/Resources.json | 13 + src/Localization/id/Resources.json | 13 + src/Localization/it/Resources.json | 13 + src/Localization/ja/Resources.json | 13 + src/Localization/ko/Resources.json | 13 + src/Localization/nl/Resources.json | 13 + src/Localization/pl/Resources.json | 13 + src/Localization/pt-BR/Resources.json | 13 + src/Localization/pt-PT/Resources.json | 13 + src/Localization/ru/Resources.json | 13 + src/Localization/sv/Resources.json | 13 + src/Localization/t.ts | 24 ++ src/Localization/tr/Resources.json | 13 + src/Localization/zh-Hans/Resources.json | 13 + src/Localization/zh-Hant/Resources.json | 13 + src/SelfServe/SelfServeComponent.tsx | 5 +- src/i18n.ts | 5 + utils/generateI18nKeys.mjs | 71 +++++ 30 files changed, 685 insertions(+), 26 deletions(-) create mode 100644 src/@types/i18next.d.ts create mode 100644 src/Localization/LocProject.json create mode 100644 src/Localization/cs/Resources.json create mode 100644 src/Localization/de/Resources.json create mode 100644 src/Localization/en/Resources.json create mode 100644 src/Localization/es/Resources.json create mode 100644 src/Localization/fr/Resources.json create mode 100644 src/Localization/hu/Resources.json create mode 100644 src/Localization/id/Resources.json create mode 100644 src/Localization/it/Resources.json create mode 100644 src/Localization/ja/Resources.json create mode 100644 src/Localization/ko/Resources.json create mode 100644 src/Localization/nl/Resources.json create mode 100644 src/Localization/pl/Resources.json create mode 100644 src/Localization/pt-BR/Resources.json create mode 100644 src/Localization/pt-PT/Resources.json create mode 100644 src/Localization/ru/Resources.json create mode 100644 src/Localization/sv/Resources.json create mode 100644 src/Localization/t.ts create mode 100644 src/Localization/tr/Resources.json create mode 100644 src/Localization/zh-Hans/Resources.json create mode 100644 src/Localization/zh-Hant/Resources.json create mode 100644 utils/generateI18nKeys.mjs diff --git a/.gitignore b/.gitignore index 3617cf905..5f05353cf 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ Contracts/* failure.png screenshots/* GettingStarted-ignore*.ipynb +src/Localization/Keys.generated.ts /test-results/ /playwright-report/ /blob-report/ diff --git a/package-lock.json b/package-lock.json index ed6531318..54521d37a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,6 +77,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", @@ -180,6 +181,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", @@ -206,6 +208,7 @@ "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.104.1", "webpack-bundle-analyzer": "5.2.0", @@ -2450,12 +2453,9 @@ "license": "MIT" }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "engines": { "node": ">=6.9.0" } @@ -8684,16 +8684,231 @@ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true }, - "node_modules/@swc/helpers": { - "version": "0.5.3", - "license": "Apache-2.0", + "node_modules/@swc/core": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz", + "integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==", + "dev": true, + "hasInstallScript": true, "dependencies": { - "tslib": "^2.4.0" + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.11", + "@swc/core-darwin-x64": "1.15.11", + "@swc/core-linux-arm-gnueabihf": "1.15.11", + "@swc/core-linux-arm64-gnu": "1.15.11", + "@swc/core-linux-arm64-musl": "1.15.11", + "@swc/core-linux-x64-gnu": "1.15.11", + "@swc/core-linux-x64-musl": "1.15.11", + "@swc/core-win32-arm64-msvc": "1.15.11", + "@swc/core-win32-ia32-msvc": "1.15.11", + "@swc/core-win32-x64-msvc": "1.15.11" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz", + "integrity": "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz", + "integrity": "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz", + "integrity": "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz", + "integrity": "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz", + "integrity": "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz", + "integrity": "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz", + "integrity": "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz", + "integrity": "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz", + "integrity": "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz", + "integrity": "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "dependencies": { + "tslib": "^2.8.0" } }, "node_modules/@swc/helpers/node_modules/tslib": { - "version": "2.6.2", - "license": "0BSD" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } }, "node_modules/@testing-library/dom": { "version": "7.31.2", @@ -16661,6 +16876,57 @@ "cross-fetch": "4.0.0" } }, + "node_modules/i18next-resources-for-ts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/i18next-resources-for-ts/-/i18next-resources-for-ts-2.0.0.tgz", + "integrity": "sha512-RvATolbJlxrwpZh2+R7ZcNtg0ewmXFFx6rdu9i2bUEBvn6ThgA82rxDe3rJQa3hFS0SopX0qPaABqVDN3TUVpw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "@swc/core": "^1.15.3", + "chokidar": "^5.0.0", + "yaml": "^2.8.2" + }, + "bin": { + "i18next-resources-for-ts": "bin/i18next-resources-for-ts.js" + } + }, + "node_modules/i18next-resources-for-ts/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/i18next-resources-for-ts/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/i18next-resources-to-backend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz", + "integrity": "sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -25221,6 +25487,7 @@ }, "node_modules/regenerator-runtime": { "version": "0.14.0", + "dev": true, "license": "MIT" }, "node_modules/regenerator-transform": { @@ -27698,6 +27965,15 @@ "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==" }, + "node_modules/values-to-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/values-to-keys/-/values-to-keys-1.1.0.tgz", + "integrity": "sha512-3ErIYotgwYxBeBstuiaMVDSj6uUzoYnk4A4oM9NqKHrUzSmihvt0DqpQozFq3NWevgnz7hwkOZUXBthIuwxJaQ==", + "dev": true, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/vary": { "version": "1.1.2", "dev": true, @@ -29303,14 +29579,17 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index 7ee6afb0f..8b1faaa20 100644 --- a/package.json +++ b/package.json @@ -72,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", @@ -188,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", @@ -214,6 +216,7 @@ "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.104.1", "webpack-bundle-analyzer": "5.2.0", @@ -222,7 +225,8 @@ "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", @@ -249,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": { diff --git a/src/@types/i18next.d.ts b/src/@types/i18next.d.ts new file mode 100644 index 000000000..65fe858fc --- /dev/null +++ b/src/@types/i18next.d.ts @@ -0,0 +1,11 @@ +import "i18next"; +import Resources from "../Localization/en/Resources.json"; + +declare module "i18next" { + interface CustomTypeOptions { + defaultNS: "Resources"; + resources: { + Resources: typeof Resources; + }; + } +} diff --git a/src/Explorer/SplashScreen/SplashScreen.tsx b/src/Explorer/SplashScreen/SplashScreen.tsx index 7376f5c8e..162a78e5a 100644 --- a/src/Explorer/SplashScreen/SplashScreen.tsx +++ b/src/Explorer/SplashScreen/SplashScreen.tsx @@ -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 = ({ 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(() => { diff --git a/src/Index.tsx b/src/Index.tsx index d660eaed0..cfdf5cf6a 100644 --- a/src/Index.tsx +++ b/src/Index.tsx @@ -1,3 +1,4 @@ +import "./i18n"; import React, { useState } from "react"; import ReactDOM from "react-dom"; import Arrow from "../images/Arrow.svg"; diff --git a/src/Localization/LocProject.json b/src/Localization/LocProject.json new file mode 100644 index 000000000..c8af39c3f --- /dev/null +++ b/src/Localization/LocProject.json @@ -0,0 +1,14 @@ +{ + "Projects": [ + { + "LanguageSet": "Azure_LanguagesExt", + "LocItems": [ + { + "SourceFile": "src\\Localization\\en\\Resources.json", + "CopyOption": "LangIDOnPath", + "OutputPath": "src\\Localization" + } + ] + } + ] +} diff --git a/src/Localization/cs/Resources.json b/src/Localization/cs/Resources.json new file mode 100644 index 000000000..1425108ad --- /dev/null +++ b/src/Localization/cs/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/de/Resources.json b/src/Localization/de/Resources.json new file mode 100644 index 000000000..5b88b5dec --- /dev/null +++ b/src/Localization/de/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/en/Resources.json b/src/Localization/en/Resources.json new file mode 100644 index 000000000..834c642ac --- /dev/null +++ b/src/Localization/en/Resources.json @@ -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." + } + } +} diff --git a/src/Localization/es/Resources.json b/src/Localization/es/Resources.json new file mode 100644 index 000000000..ac83096f1 --- /dev/null +++ b/src/Localization/es/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/fr/Resources.json b/src/Localization/fr/Resources.json new file mode 100644 index 000000000..6a2da72d6 --- /dev/null +++ b/src/Localization/fr/Resources.json @@ -0,0 +1,13 @@ +{ + "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 d’exemple, notre documentation et nos outils supplémentaires." + } + } +} \ No newline at end of file diff --git a/src/Localization/hu/Resources.json b/src/Localization/hu/Resources.json new file mode 100644 index 000000000..4057c87e5 --- /dev/null +++ b/src/Localization/hu/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/id/Resources.json b/src/Localization/id/Resources.json new file mode 100644 index 000000000..1d80da7cc --- /dev/null +++ b/src/Localization/id/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/it/Resources.json b/src/Localization/it/Resources.json new file mode 100644 index 000000000..3bf560052 --- /dev/null +++ b/src/Localization/it/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/ja/Resources.json b/src/Localization/ja/Resources.json new file mode 100644 index 000000000..431a8fe1a --- /dev/null +++ b/src/Localization/ja/Resources.json @@ -0,0 +1,13 @@ +{ + "splashScreen": { + "title": { + "default": "Azure Cosmos DB へようこそ", + "postgres": "Azure Cosmos DB for PostgreSQL へようこそ", + "vcoreMongo": "Azure DocumentDB (MongoDB 互換) へようこそ" + }, + "subtitle": { + "default": "あらゆるスケールに対応するグローバル分散型のマルチモデル データベース サーバー", + "getStarted": "サンプル データセット、ドキュメント、追加ツールを使用して開始してください。" + } + } +} \ No newline at end of file diff --git a/src/Localization/ko/Resources.json b/src/Localization/ko/Resources.json new file mode 100644 index 000000000..966f6c301 --- /dev/null +++ b/src/Localization/ko/Resources.json @@ -0,0 +1,13 @@ +{ + "splashScreen": { + "title": { + "default": "Azure Cosmos DB 시작", + "postgres": "Azure Cosmos DB for PostgreSQL 시작", + "vcoreMongo": "Azure DocumentDB 시작(MongoDB 호환성 포함)" + }, + "subtitle": { + "default": "모든 규모에 대해 전역적으로 분산된 다중 모델 데이터베이스 서비스", + "getStarted": "샘플 데이터 세트, 설명서 및 추가 도구를 시작하세요." + } + } +} \ No newline at end of file diff --git a/src/Localization/nl/Resources.json b/src/Localization/nl/Resources.json new file mode 100644 index 000000000..9e506694f --- /dev/null +++ b/src/Localization/nl/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/pl/Resources.json b/src/Localization/pl/Resources.json new file mode 100644 index 000000000..732fb0073 --- /dev/null +++ b/src/Localization/pl/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/pt-BR/Resources.json b/src/Localization/pt-BR/Resources.json new file mode 100644 index 000000000..c265d5755 --- /dev/null +++ b/src/Localization/pt-BR/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/pt-PT/Resources.json b/src/Localization/pt-PT/Resources.json new file mode 100644 index 000000000..300662269 --- /dev/null +++ b/src/Localization/pt-PT/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/ru/Resources.json b/src/Localization/ru/Resources.json new file mode 100644 index 000000000..f0b829235 --- /dev/null +++ b/src/Localization/ru/Resources.json @@ -0,0 +1,13 @@ +{ + "splashScreen": { + "title": { + "default": "Вас приветствует Azure Cosmos DB", + "postgres": "Добро пожаловать в Azure Cosmos DB for PostgreSQL", + "vcoreMongo": "Добро пожаловать в Azure DocumentDB (с совместимостью с MongoDB)" + }, + "subtitle": { + "default": "Глобально распределенная многомодельная служба базы данных для использования в любом масштабе", + "getStarted": "Начните работу с нашими примерами наборов данных, документацией и дополнительными инструментами." + } + } +} \ No newline at end of file diff --git a/src/Localization/sv/Resources.json b/src/Localization/sv/Resources.json new file mode 100644 index 000000000..686f59c4a --- /dev/null +++ b/src/Localization/sv/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/t.ts b/src/Localization/t.ts new file mode 100644 index 000000000..f4b5da3f6 --- /dev/null +++ b/src/Localization/t.ts @@ -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 = { + [K in keyof T & string]: T[K] extends Record + ? NestedKeyOf + : P extends "" + ? K + : `${P}.${K}`; +}[keyof T & string]; + +/** All valid translation keys derived from en/Resources.json */ +export type ResourceKey = NestedKeyOf; + +/** + * 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 => + (i18n.t as (key: string, options?: unknown) => string)(key, { ns: "Resources", ...options }); diff --git a/src/Localization/tr/Resources.json b/src/Localization/tr/Resources.json new file mode 100644 index 000000000..205da1551 --- /dev/null +++ b/src/Localization/tr/Resources.json @@ -0,0 +1,13 @@ +{ + "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." + } + } +} \ No newline at end of file diff --git a/src/Localization/zh-Hans/Resources.json b/src/Localization/zh-Hans/Resources.json new file mode 100644 index 000000000..4239a0a8e --- /dev/null +++ b/src/Localization/zh-Hans/Resources.json @@ -0,0 +1,13 @@ +{ + "splashScreen": { + "title": { + "default": "欢迎使用 Azure Cosmos DB", + "postgres": "欢迎使用 Azure Cosmos DB for PostgreSQL", + "vcoreMongo": "欢迎使用 Azure DocumentDB (具有 MongoDB 兼容性)" + }, + "subtitle": { + "default": "任何规模的全球分布式多模型数据库服务", + "getStarted": "开始使用我们的示例数据集、文档和其他工具。" + } + } +} \ No newline at end of file diff --git a/src/Localization/zh-Hant/Resources.json b/src/Localization/zh-Hant/Resources.json new file mode 100644 index 000000000..2c5ba89a6 --- /dev/null +++ b/src/Localization/zh-Hant/Resources.json @@ -0,0 +1,13 @@ +{ + "splashScreen": { + "title": { + "default": "歡迎使用 Azure Cosmos DB", + "postgres": "歡迎使用 Azure Cosmos DB for PostgreSQL", + "vcoreMongo": "歡迎使用 Azure DocumentDB (具 MongoDB 相容性)" + }, + "subtitle": { + "default": "適用於任何規模的全域散發、多模型資料庫服務", + "getStarted": "開始使用我們的樣本資料集、文件和其他工具。" + } + } +} \ No newline at end of file diff --git a/src/SelfServe/SelfServeComponent.tsx b/src/SelfServe/SelfServeComponent.tsx index 470adb833..8ef813cdc 100644 --- a/src/SelfServe/SelfServeComponent.tsx +++ b/src/SelfServe/SelfServeComponent.tsx @@ -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 string; componentDidMount(): void { this.performRefresh().then(() => { @@ -119,7 +118,7 @@ export class SelfServeComponent extends React.Component string; } private onError = (hasErrors: boolean): void => { diff --git a/src/i18n.ts b/src/i18n.ts index 9268ada97..ca4966e86 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -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: { diff --git a/utils/generateI18nKeys.mjs b/utils/generateI18nKeys.mjs new file mode 100644 index 000000000..e48c4bbd4 --- /dev/null +++ b/utils/generateI18nKeys.mjs @@ -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}`);