From 231c9e4bdc3d95429b5f2b966b449a011b40890a Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Sat, 2 May 2026 18:02:29 +0000 Subject: [PATCH] feat(react-ui): add multilingual (i18n) support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds end-to-end internationalization to the React UI with five seed languages (English, Italian, Spanish, German, Simplified Chinese) and a sidebar-footer language switcher next to the existing theme toggle. Library: react-i18next + i18next + i18next-http-backend + i18next-browser-languagedetector. The detector caches the user's choice in localStorage (key `localai-language`, mirroring the existing `localai-theme` convention) and updates the `` attribute on change. fallbackLng is `en`, so any missing translation in another locale falls back transparently. Translation files live under `public/locales//.json`. They ride along with the existing `//go:embed react-ui/dist/*` directive, but the previous SPA route in core/http/app.go only exposed `/assets/*` from the embedded React build. This commit generalizes the asset handler into a `serveReactSubdir(subdir)` helper and adds a matching `/locales/*` route so i18next-http-backend can fetch the JSONs at runtime. The http-backend `loadPath` is built via the existing `apiUrl()` helper so instances served under a sub-path (e.g. ``) resolve correctly. Namespaces (13): common, nav, errors, auth, home, models, importModel, chat, agents, skills, collections, media, admin. Translated UI surfaces include the sidebar/header/footer chrome, login + account flows, the Home dashboard (incl. the manage-by-chat assistant CTA), the model gallery + import flow, the chat experience (Chat.jsx + ChatsMenu), agents/skills/collections list pages, the studio media tabs (Image, Video, TTS), and the admin page-headers (Settings incl. its section nav, Manage, Backends, Traces, Nodes, P2P, Users, Usage). Shared components (ConfirmDialog, Toast) take their default labels from the common namespace so callers don't need to pass strings explicitly. Tooling for incremental adoption is included: - `i18next-parser.config.js` + `npm run i18n:extract` to sweep `t()` keys into the JSON skeletons. - `scripts/translate-locales.mjs` (one-off helper) to bootstrap non-English locales from English source via OpenAI or Anthropic APIs, with --copy mode as a placeholder fallback. Idempotent; preserves existing translations unless --overwrite is passed. Larger config-driven pages (ModelEditor, Settings deep field forms, AgentChat/AgentCreate, SkillEdit, CollectionDetails, Talk, Sound, biometrics, FineTune/Quantize, Users modals, Nodes/P2P install pickers, BackendLogs, Traces deep filters, Explorer) intentionally keep their inner content untranslated for now — they fall back to English via fallbackLng so functionality is unaffected, and the extracted-strings pattern + the bootstrap script make follow-up extraction straightforward. The initial Suspense fallback at the root in main.jsx covers the first JSON fetch on cold load. A simple `.app-boot-spinner` styled in App.css provides a non-empty paint while the first namespace loads. Assisted-by: Claude:claude-opus-4-7 [Bash Read Edit Write Agent] Signed-off-by: Ettore Di Giacinto --- core/http/app.go | 32 +- core/http/react-ui/i18next-parser.config.js | 12 + core/http/react-ui/package-lock.json | 1914 ++++++++++++++++- core/http/react-ui/package.json | 36 +- .../react-ui/public/locales/de/admin.json | 62 + .../react-ui/public/locales/de/agents.json | 55 + .../http/react-ui/public/locales/de/auth.json | 112 + .../http/react-ui/public/locales/de/chat.json | 116 + .../public/locales/de/collections.json | 43 + .../react-ui/public/locales/de/common.json | 109 + .../react-ui/public/locales/de/errors.json | 17 + .../http/react-ui/public/locales/de/home.json | 66 + .../public/locales/de/importModel.json | 142 ++ .../react-ui/public/locales/de/media.json | 154 ++ .../react-ui/public/locales/de/models.json | 85 + core/http/react-ui/public/locales/de/nav.json | 51 + .../react-ui/public/locales/de/skills.json | 79 + .../react-ui/public/locales/en/admin.json | 62 + .../react-ui/public/locales/en/agents.json | 55 + .../http/react-ui/public/locales/en/auth.json | 112 + .../http/react-ui/public/locales/en/chat.json | 116 + .../public/locales/en/collections.json | 43 + .../react-ui/public/locales/en/common.json | 109 + .../react-ui/public/locales/en/errors.json | 17 + .../http/react-ui/public/locales/en/home.json | 66 + .../public/locales/en/importModel.json | 142 ++ .../react-ui/public/locales/en/media.json | 154 ++ .../react-ui/public/locales/en/models.json | 85 + core/http/react-ui/public/locales/en/nav.json | 51 + .../react-ui/public/locales/en/skills.json | 79 + .../react-ui/public/locales/es/admin.json | 62 + .../react-ui/public/locales/es/agents.json | 55 + .../http/react-ui/public/locales/es/auth.json | 112 + .../http/react-ui/public/locales/es/chat.json | 116 + .../public/locales/es/collections.json | 43 + .../react-ui/public/locales/es/common.json | 109 + .../react-ui/public/locales/es/errors.json | 17 + .../http/react-ui/public/locales/es/home.json | 66 + .../public/locales/es/importModel.json | 142 ++ .../react-ui/public/locales/es/media.json | 154 ++ .../react-ui/public/locales/es/models.json | 85 + core/http/react-ui/public/locales/es/nav.json | 51 + .../react-ui/public/locales/es/skills.json | 79 + .../react-ui/public/locales/it/admin.json | 62 + .../react-ui/public/locales/it/agents.json | 55 + .../http/react-ui/public/locales/it/auth.json | 112 + .../http/react-ui/public/locales/it/chat.json | 116 + .../public/locales/it/collections.json | 43 + .../react-ui/public/locales/it/common.json | 109 + .../react-ui/public/locales/it/errors.json | 17 + .../http/react-ui/public/locales/it/home.json | 66 + .../public/locales/it/importModel.json | 142 ++ .../react-ui/public/locales/it/media.json | 154 ++ .../react-ui/public/locales/it/models.json | 85 + core/http/react-ui/public/locales/it/nav.json | 51 + .../react-ui/public/locales/it/skills.json | 79 + .../react-ui/public/locales/zh-CN/admin.json | 62 + .../react-ui/public/locales/zh-CN/agents.json | 55 + .../react-ui/public/locales/zh-CN/auth.json | 112 + .../react-ui/public/locales/zh-CN/chat.json | 116 + .../public/locales/zh-CN/collections.json | 43 + .../react-ui/public/locales/zh-CN/common.json | 109 + .../react-ui/public/locales/zh-CN/errors.json | 17 + .../react-ui/public/locales/zh-CN/home.json | 66 + .../public/locales/zh-CN/importModel.json | 142 ++ .../react-ui/public/locales/zh-CN/media.json | 154 ++ .../react-ui/public/locales/zh-CN/models.json | 85 + .../react-ui/public/locales/zh-CN/nav.json | 51 + .../react-ui/public/locales/zh-CN/skills.json | 79 + .../react-ui/scripts/translate-locales.mjs | 180 ++ core/http/react-ui/src/App.css | 94 + core/http/react-ui/src/App.jsx | 19 +- .../react-ui/src/components/ChatsMenu.jsx | 26 +- .../react-ui/src/components/ConfirmDialog.jsx | 17 +- .../src/components/LanguageSwitcher.jsx | 74 + core/http/react-ui/src/components/Sidebar.jsx | 79 +- core/http/react-ui/src/components/Toast.jsx | 8 +- core/http/react-ui/src/i18n/index.js | 63 + core/http/react-ui/src/main.jsx | 27 +- core/http/react-ui/src/pages/Account.jsx | 99 +- core/http/react-ui/src/pages/Agents.jsx | 106 +- core/http/react-ui/src/pages/Backends.jsx | 6 +- core/http/react-ui/src/pages/Chat.jsx | 133 +- core/http/react-ui/src/pages/Collections.jsx | 69 +- core/http/react-ui/src/pages/Home.jsx | 96 +- core/http/react-ui/src/pages/ImageGen.jsx | 42 +- core/http/react-ui/src/pages/ImportModel.jsx | 251 ++- core/http/react-ui/src/pages/Login.jsx | 92 +- core/http/react-ui/src/pages/Manage.jsx | 8 +- core/http/react-ui/src/pages/Models.jsx | 136 +- core/http/react-ui/src/pages/Nodes.jsx | 6 +- core/http/react-ui/src/pages/NotFound.jsx | 8 +- core/http/react-ui/src/pages/P2P.jsx | 6 +- core/http/react-ui/src/pages/Settings.jsx | 36 +- core/http/react-ui/src/pages/Skills.jsx | 132 +- core/http/react-ui/src/pages/Studio.jsx | 12 +- core/http/react-ui/src/pages/TTS.jsx | 20 +- core/http/react-ui/src/pages/Traces.jsx | 6 +- core/http/react-ui/src/pages/Usage.jsx | 6 +- core/http/react-ui/src/pages/Users.jsx | 6 +- core/http/react-ui/src/pages/VideoGen.jsx | 36 +- 101 files changed, 8616 insertions(+), 732 deletions(-) create mode 100644 core/http/react-ui/i18next-parser.config.js create mode 100644 core/http/react-ui/public/locales/de/admin.json create mode 100644 core/http/react-ui/public/locales/de/agents.json create mode 100644 core/http/react-ui/public/locales/de/auth.json create mode 100644 core/http/react-ui/public/locales/de/chat.json create mode 100644 core/http/react-ui/public/locales/de/collections.json create mode 100644 core/http/react-ui/public/locales/de/common.json create mode 100644 core/http/react-ui/public/locales/de/errors.json create mode 100644 core/http/react-ui/public/locales/de/home.json create mode 100644 core/http/react-ui/public/locales/de/importModel.json create mode 100644 core/http/react-ui/public/locales/de/media.json create mode 100644 core/http/react-ui/public/locales/de/models.json create mode 100644 core/http/react-ui/public/locales/de/nav.json create mode 100644 core/http/react-ui/public/locales/de/skills.json create mode 100644 core/http/react-ui/public/locales/en/admin.json create mode 100644 core/http/react-ui/public/locales/en/agents.json create mode 100644 core/http/react-ui/public/locales/en/auth.json create mode 100644 core/http/react-ui/public/locales/en/chat.json create mode 100644 core/http/react-ui/public/locales/en/collections.json create mode 100644 core/http/react-ui/public/locales/en/common.json create mode 100644 core/http/react-ui/public/locales/en/errors.json create mode 100644 core/http/react-ui/public/locales/en/home.json create mode 100644 core/http/react-ui/public/locales/en/importModel.json create mode 100644 core/http/react-ui/public/locales/en/media.json create mode 100644 core/http/react-ui/public/locales/en/models.json create mode 100644 core/http/react-ui/public/locales/en/nav.json create mode 100644 core/http/react-ui/public/locales/en/skills.json create mode 100644 core/http/react-ui/public/locales/es/admin.json create mode 100644 core/http/react-ui/public/locales/es/agents.json create mode 100644 core/http/react-ui/public/locales/es/auth.json create mode 100644 core/http/react-ui/public/locales/es/chat.json create mode 100644 core/http/react-ui/public/locales/es/collections.json create mode 100644 core/http/react-ui/public/locales/es/common.json create mode 100644 core/http/react-ui/public/locales/es/errors.json create mode 100644 core/http/react-ui/public/locales/es/home.json create mode 100644 core/http/react-ui/public/locales/es/importModel.json create mode 100644 core/http/react-ui/public/locales/es/media.json create mode 100644 core/http/react-ui/public/locales/es/models.json create mode 100644 core/http/react-ui/public/locales/es/nav.json create mode 100644 core/http/react-ui/public/locales/es/skills.json create mode 100644 core/http/react-ui/public/locales/it/admin.json create mode 100644 core/http/react-ui/public/locales/it/agents.json create mode 100644 core/http/react-ui/public/locales/it/auth.json create mode 100644 core/http/react-ui/public/locales/it/chat.json create mode 100644 core/http/react-ui/public/locales/it/collections.json create mode 100644 core/http/react-ui/public/locales/it/common.json create mode 100644 core/http/react-ui/public/locales/it/errors.json create mode 100644 core/http/react-ui/public/locales/it/home.json create mode 100644 core/http/react-ui/public/locales/it/importModel.json create mode 100644 core/http/react-ui/public/locales/it/media.json create mode 100644 core/http/react-ui/public/locales/it/models.json create mode 100644 core/http/react-ui/public/locales/it/nav.json create mode 100644 core/http/react-ui/public/locales/it/skills.json create mode 100644 core/http/react-ui/public/locales/zh-CN/admin.json create mode 100644 core/http/react-ui/public/locales/zh-CN/agents.json create mode 100644 core/http/react-ui/public/locales/zh-CN/auth.json create mode 100644 core/http/react-ui/public/locales/zh-CN/chat.json create mode 100644 core/http/react-ui/public/locales/zh-CN/collections.json create mode 100644 core/http/react-ui/public/locales/zh-CN/common.json create mode 100644 core/http/react-ui/public/locales/zh-CN/errors.json create mode 100644 core/http/react-ui/public/locales/zh-CN/home.json create mode 100644 core/http/react-ui/public/locales/zh-CN/importModel.json create mode 100644 core/http/react-ui/public/locales/zh-CN/media.json create mode 100644 core/http/react-ui/public/locales/zh-CN/models.json create mode 100644 core/http/react-ui/public/locales/zh-CN/nav.json create mode 100644 core/http/react-ui/public/locales/zh-CN/skills.json create mode 100644 core/http/react-ui/scripts/translate-locales.mjs create mode 100644 core/http/react-ui/src/components/LanguageSwitcher.jsx create mode 100644 core/http/react-ui/src/i18n/index.js diff --git a/core/http/app.go b/core/http/app.go index c06b69a350d9..e18690def986 100644 --- a/core/http/app.go +++ b/core/http/app.go @@ -447,24 +447,28 @@ func API(application *application.Application) (*echo.Echo, error) { return prefixRedirect(c, "/app/"+p) }) - // Serve React static assets (JS, CSS, etc.) - serveReactAsset := func(c echo.Context) error { - p := "assets/" + c.Param("*") - f, err := reactFS.Open(p) - if err == nil { - defer f.Close() - stat, statErr := f.Stat() - if statErr == nil && !stat.IsDir() { - contentType := mime.TypeByExtension(filepath.Ext(p)) - if contentType == "" { - contentType = echo.MIMEOctetStream + // Serve React static assets (JS, CSS, etc.) and i18n locale JSONs + // from the embedded React build. + serveReactSubdir := func(subdir string) echo.HandlerFunc { + return func(c echo.Context) error { + p := subdir + "/" + c.Param("*") + f, err := reactFS.Open(p) + if err == nil { + defer f.Close() + stat, statErr := f.Stat() + if statErr == nil && !stat.IsDir() { + contentType := mime.TypeByExtension(filepath.Ext(p)) + if contentType == "" { + contentType = echo.MIMEOctetStream + } + return c.Stream(http.StatusOK, contentType, f) } - return c.Stream(http.StatusOK, contentType, f) } + return echo.NewHTTPError(http.StatusNotFound) } - return echo.NewHTTPError(http.StatusNotFound) } - e.GET("/assets/*", serveReactAsset) + e.GET("/assets/*", serveReactSubdir("assets")) + e.GET("/locales/*", serveReactSubdir("locales")) } } routes.RegisterJINARoutes(e, requestExtractor, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig()) diff --git a/core/http/react-ui/i18next-parser.config.js b/core/http/react-ui/i18next-parser.config.js new file mode 100644 index 000000000000..1db8d4a25f93 --- /dev/null +++ b/core/http/react-ui/i18next-parser.config.js @@ -0,0 +1,12 @@ +export default { + locales: ['en', 'it', 'es', 'de', 'zh-CN'], + defaultNamespace: 'common', + output: 'public/locales/$LOCALE/$NAMESPACE.json', + input: ['src/**/*.{js,jsx}'], + keySeparator: '.', + namespaceSeparator: ':', + defaultValue: (locale, _ns, key) => (locale === 'en' ? key : ''), + sort: true, + createOldCatalogs: false, + keepRemoved: false, +} diff --git a/core/http/react-ui/package-lock.json b/core/http/react-ui/package-lock.json index df5d4c9ff65d..b34a39fd5199 100644 --- a/core/http/react-ui/package-lock.json +++ b/core/http/react-ui/package-lock.json @@ -22,9 +22,13 @@ "@modelcontextprotocol/sdk": "^1.25.1", "dompurify": "^3.4.0", "highlight.js": "^11.11.1", + "i18next": "^26.0.8", + "i18next-browser-languagedetector": "^8.2.1", + "i18next-http-backend": "^3.0.6", "marked": "^15.0.7", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-i18next": "^17.0.6", "react-router-dom": "^7.6.1", "yaml": "^2.8.3" }, @@ -36,6 +40,7 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.1.0", + "i18next-parser": "^9.4.0", "vite": "^6.4.2" } }, @@ -257,6 +262,14 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -960,6 +973,18 @@ "node": ">=6" } }, + "node_modules/@gulpjs/to-absolute-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz", + "integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==", + "dev": true, + "dependencies": { + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@hono/node-server": { "version": "1.19.14", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", @@ -1020,6 +1045,23 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1182,6 +1224,16 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@playwright/test": { "version": "1.58.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", @@ -1582,6 +1634,18 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/symlink-or-copy": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/symlink-or-copy/-/symlink-or-copy-1.2.2.tgz", + "integrity": "sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA==", + "dev": true + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -1693,6 +1757,18 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1708,18 +1784,91 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/baseline-browser-mapping": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", @@ -1732,6 +1881,31 @@ "node": ">=6.0.0" } }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -1755,6 +1929,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1765,6 +1945,85 @@ "concat-map": "0.0.1" } }, + "node_modules/broccoli-node-api": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/broccoli-node-api/-/broccoli-node-api-1.7.0.tgz", + "integrity": "sha512-QIqLSVJWJUVOhclmkmypJJH9u9s/aWH4+FH6Q6Ju5l+Io4dtwqdPUNmDfw40o6sxhbZHhqGujDJuHTML1wG8Yw==", + "dev": true + }, + "node_modules/broccoli-node-info": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/broccoli-node-info/-/broccoli-node-info-2.2.0.tgz", + "integrity": "sha512-VabSGRpKIzpmC+r+tJueCE5h8k6vON7EIMMWu6d/FyPdtijwLQ7QvzShEw+m3mHoDzUaj/kiZsDYrS8X2adsBg==", + "dev": true, + "engines": { + "node": "8.* || >= 10.*" + } + }, + "node_modules/broccoli-output-wrapper": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/broccoli-output-wrapper/-/broccoli-output-wrapper-3.2.5.tgz", + "integrity": "sha512-bQAtwjSrF4Nu0CK0JOy5OZqw9t5U0zzv2555EA/cF8/a8SLDTIetk9UgrtMVw7qKLKdSpOZ2liZNeZZDaKgayw==", + "dev": true, + "dependencies": { + "fs-extra": "^8.1.0", + "heimdalljs-logger": "^0.1.10", + "symlink-or-copy": "^1.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + } + }, + "node_modules/broccoli-output-wrapper/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/broccoli-output-wrapper/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/broccoli-output-wrapper/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/broccoli-plugin": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-4.0.7.tgz", + "integrity": "sha512-a4zUsWtA1uns1K7p9rExYVYG99rdKeGRymW0qOCNkvDPHQxVi3yVyJHhQbM3EZwdt2E0mnhr5e0c/bPpJ7p3Wg==", + "dev": true, + "dependencies": { + "broccoli-node-api": "^1.7.0", + "broccoli-output-wrapper": "^3.2.5", + "fs-merger": "^3.2.1", + "promise-map-series": "^0.3.0", + "quick-temp": "^0.1.8", + "rimraf": "^3.0.2", + "symlink-or-copy": "^1.3.1" + }, + "engines": { + "node": "10.* || >= 12.*" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -1798,6 +2057,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1878,6 +2161,57 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1896,6 +2230,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1948,6 +2300,12 @@ "node": ">=6.6.0" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", @@ -1969,6 +2327,14 @@ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1982,6 +2348,34 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -2012,6 +2406,47 @@ "node": ">= 0.8" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, "node_modules/dompurify": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", @@ -2021,6 +2456,20 @@ "@types/trusted-types": "^2.0.7" } }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2034,6 +2483,12 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2045,6 +2500,12 @@ "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", "dev": true }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -2053,6 +2514,55 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ensure-posix-path": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz", + "integrity": "sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/eol": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz", + "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==", + "dev": true + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2322,6 +2832,15 @@ "node": ">= 0.6" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/eventsource": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", @@ -2413,7 +2932,13 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/fast-json-stable-stringify": { + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", @@ -2440,6 +2965,15 @@ } ] }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2525,6 +3059,22 @@ "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2541,6 +3091,100 @@ "node": ">= 0.8" } }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-merger": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/fs-merger/-/fs-merger-3.2.1.tgz", + "integrity": "sha512-AN6sX12liy0JE7C2evclwoo0aCG3PFulLjrTLsJpWh/2mM+DinhpSGqYLbHBBbIW1PLRNcFhJG8Axtz8mQW3ug==", + "dev": true, + "dependencies": { + "broccoli-node-api": "^1.7.0", + "broccoli-node-info": "^2.1.0", + "fs-extra": "^8.0.1", + "fs-tree-diff": "^2.0.1", + "walk-sync": "^2.2.0" + } + }, + "node_modules/fs-merger/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-merger/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/fs-merger/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz", + "integrity": "sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.8", + "streamx": "^2.12.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/fs-tree-diff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-2.0.1.tgz", + "integrity": "sha512-x+CfAZ/lJHQqwlD64pYM5QxWjzWhSjroaVsr8PW831zOApL55qPibed0c+xebaLWVr2BnHFoHdrwOv8pzt8R5A==", + "dev": true, + "dependencies": { + "@types/symlink-or-copy": "^1.2.0", + "heimdalljs-logger": "^0.1.7", + "object-assign": "^4.1.0", + "path-posix": "^1.0.0", + "symlink-or-copy": "^1.1.8" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2607,6 +3251,27 @@ "node": ">= 0.4" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2619,6 +3284,25 @@ "node": ">=10.13.0" } }, + "node_modules/glob-stream": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", + "dev": true, + "dependencies": { + "@gulpjs/to-absolute-glob": "^4.0.0", + "anymatch": "^3.1.3", + "fastq": "^1.13.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "is-negated-glob": "^1.0.0", + "normalize-path": "^3.0.0", + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", @@ -2642,6 +3326,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gulp-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-sort/-/gulp-sort-2.0.0.tgz", + "integrity": "sha512-MyTel3FXOdh1qhw1yKhpimQrAmur9q1X0ZigLmCOxouQD+BD3za9/89O+HfbgBQvvh4igEbp0/PUWO+VqGYG1g==", + "dev": true, + "dependencies": { + "through2": "^2.0.1" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2673,6 +3372,46 @@ "node": ">= 0.4" } }, + "node_modules/heimdalljs": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/heimdalljs/-/heimdalljs-0.2.6.tgz", + "integrity": "sha512-o9bd30+5vLBvBtzCPwwGqpry2+n0Hi6H1+qwt6y+0kwRHGGF8TFIhJPmnuM0xO97zaKrDZMwO/V56fAnn8m/tA==", + "dev": true, + "dependencies": { + "rsvp": "~3.2.1" + } + }, + "node_modules/heimdalljs-logger": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/heimdalljs-logger/-/heimdalljs-logger-0.1.10.tgz", + "integrity": "sha512-pO++cJbhIufVI/fmB/u2Yty3KJD0TqNPecehFae0/eps0hkZ3b4Zc/PezUMOpYuHFQbA7FxHZxa305EhmjLj4g==", + "dev": true, + "dependencies": { + "debug": "^2.2.0", + "heimdalljs": "^0.2.6" + } + }, + "node_modules/heimdalljs-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/heimdalljs-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/heimdalljs/node_modules/rsvp": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.2.1.tgz", + "integrity": "sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg==", + "dev": true + }, "node_modules/highlight.js": { "version": "11.11.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", @@ -2690,6 +3429,45 @@ "node": ">=16.9.0" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -2709,6 +3487,114 @@ "url": "https://opencollective.com/express" } }, + "node_modules/i18next": { + "version": "26.0.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.8.tgz", + "integrity": "sha512-BRzLom0mhDhV9v0QhgUUHWQJuwFmnr1194xEcNLYD6ym8y8s542n4jXUvRLnhNTbh9PmpU6kGZamyuGHQMsGjw==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.1.tgz", + "integrity": "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.6.tgz", + "integrity": "sha512-mBOqy8993jtqAoj6XaI1XeC/8/9v6EPS+681ziegrPvTB0DoaCY7PpTS0SpY56qLMoS4OI1TZEM2Zf59zNh05w==", + "dependencies": { + "cross-fetch": "4.1.0" + } + }, + "node_modules/i18next-parser": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/i18next-parser/-/i18next-parser-9.4.0.tgz", + "integrity": "sha512-SLQJGDj/baBIB9ALmJVXSOXWh3Zn9+wH7J2IuQ4rvx8yuQYpUWitmt8cHFjj6FExjgr8dHfd1SGeQgkowXDO1Q==", + "deprecated": "Project is deprecated, use i18next-cli instead", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "broccoli-plugin": "^4.0.7", + "cheerio": "^1.0.0", + "colors": "^1.4.0", + "commander": "^12.1.0", + "eol": "^0.9.1", + "esbuild": "^0.25.0", + "fs-extra": "^11.2.0", + "gulp-sort": "^2.0.0", + "i18next": "^23.5.1 || ^24.2.0", + "js-yaml": "^4.1.0", + "lilconfig": "^3.1.3", + "rsvp": "^4.8.5", + "sort-keys": "^5.0.0", + "typescript": "^5.0.4", + "vinyl": "^3.0.0", + "vinyl-fs": "^4.0.0" + }, + "bin": { + "i18next": "bin/cli.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || ^22.0.0", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/i18next-parser/node_modules/i18next": { + "version": "24.2.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", + "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -2724,6 +3610,26 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2758,6 +3664,17 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2788,6 +3705,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2800,16 +3726,67 @@ "node": ">=0.10.0" } }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jose": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", @@ -2883,6 +3860,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2892,6 +3881,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/lead": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-4.0.0.tgz", + "integrity": "sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2905,6 +3903,18 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2946,6 +3956,19 @@ "node": ">= 18" } }, + "node_modules/matcher-collection": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz", + "integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==", + "dev": true, + "dependencies": { + "@types/minimatch": "^3.0.3", + "minimatch": "^3.0.2" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3008,9 +4031,27 @@ "node": "*" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mktemp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mktemp/-/mktemp-2.0.3.tgz", + "integrity": "sha512-Bq72L2oi/isYSy0guN9ihNhAMQOyZEwts+Bezm/1U+wh8bQ+fVQ2ZiUoJJjceOMiiKv/BUrA0NF98jFc81CB6w==", + "dev": true, + "engines": { + "node": "20 || 22 || 24" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { @@ -3045,12 +4086,64 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.36", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/now-and-later": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", + "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3136,6 +4229,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3148,6 +4247,55 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3165,6 +4313,15 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -3173,6 +4330,34 @@ "node": ">=8" } }, + "node_modules/path-posix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz", + "integrity": "sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/path-to-regexp": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", @@ -3295,6 +4480,21 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-map-series": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/promise-map-series/-/promise-map-series-0.3.0.tgz", + "integrity": "sha512-3npG2NGhTc8BWBolLLf8l/92OxMGaRLbqvIh9wjCHhDXNvk4zsxaTaCpiCunW09qWPrN2zeNSNwRLVBrQQtutA==", + "dev": true, + "engines": { + "node": "10.* || >= 12.*" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -3330,6 +4530,77 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quick-temp": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/quick-temp/-/quick-temp-0.1.9.tgz", + "integrity": "sha512-yI0h7tIhKVObn03kD+Ln9JFi4OljD28lfaOsTdfpTR0xzrhGOod+q66CjGafUqYX2juUfT9oHIGrTBBo22mkRA==", + "dev": true, + "dependencies": { + "mktemp": "^2.0.1", + "rimraf": "^5.0.10", + "underscore.string": "~3.3.6" + } + }, + "node_modules/quick-temp/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/quick-temp/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/quick-temp/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/quick-temp/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3371,6 +4642,32 @@ "react": "^19.2.4" } }, + "node_modules/react-i18next": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.6.tgz", + "integrity": "sha512-WzJ6SMKF+GTD7JZZqxSR1AKKmXjaSu39sClUrNlwxS4Tl7a99O+ltFy6yhPMO+wgZuxpQjJ2PZkfrQKmAqrLhw==", + "dependencies": { + "@babel/runtime": "^7.29.2", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 26.0.1", + "react": ">= 16.8.0", + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -3416,6 +4713,36 @@ "react-dom": ">=18" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3433,6 +4760,44 @@ "node": ">=4" } }, + "node_modules/resolve-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", + "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", + "dev": true, + "dependencies": { + "value-or-function": "^4.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -3492,6 +4857,21 @@ "node": ">= 18" } }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3651,6 +5031,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sort-keys": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz", + "integrity": "sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==", + "dev": true, + "dependencies": { + "is-plain-obj": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3660,6 +5067,12 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -3668,6 +5081,131 @@ "node": ">= 0.8" } }, + "node_modules/stream-composer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", + "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", + "dev": true, + "dependencies": { + "streamx": "^2.13.2" + } + }, + "node_modules/streamx": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", + "dev": true, + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3697,6 +5235,40 @@ "node": ">=8" } }, + "node_modules/symlink-or-copy": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/symlink-or-copy/-/symlink-or-copy-1.3.1.tgz", + "integrity": "sha512-0K91MEXFpBUaywiwSSkmKjnGcasG/rVBXFLJz5DrgGabpYD6N+3yZrfD6uUIfpuTu65DZLHi7N8CizHc07BPZA==", + "dev": true + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -3713,6 +5285,18 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/to-through": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", + "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", + "dev": true, + "dependencies": { + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3721,6 +5305,11 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3746,6 +5335,50 @@ "node": ">= 0.6" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/underscore.string": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", + "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", + "dev": true, + "dependencies": { + "sprintf-js": "^1.1.1", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3793,6 +5426,29 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/value-or-function": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", + "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", + "dev": true, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3801,6 +5457,88 @@ "node": ">= 0.8" } }, + "node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-contents": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", + "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", + "dev": true, + "dependencies": { + "bl": "^5.0.0", + "vinyl": "^3.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-fs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", + "dev": true, + "dependencies": { + "fs-mkdirp-stream": "^2.0.1", + "glob-stream": "^8.0.3", + "graceful-fs": "^4.2.11", + "iconv-lite": "^0.6.3", + "is-valid-glob": "^1.0.0", + "lead": "^4.0.0", + "normalize-path": "3.0.0", + "resolve-options": "^2.0.0", + "stream-composer": "^1.0.2", + "streamx": "^2.14.0", + "to-through": "^3.0.0", + "value-or-function": "^4.0.0", + "vinyl": "^3.0.1", + "vinyl-sourcemap": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-fs/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", + "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", + "dev": true, + "dependencies": { + "convert-source-map": "^2.0.0", + "graceful-fs": "^4.2.10", + "now-and-later": "^3.0.0", + "streamx": "^2.12.5", + "vinyl": "^3.0.0", + "vinyl-contents": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/vite": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", @@ -3876,11 +5614,82 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" }, + "node_modules/walk-sync": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz", + "integrity": "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==", + "dev": true, + "dependencies": { + "@types/minimatch": "^3.0.3", + "ensure-posix-path": "^1.1.0", + "matcher-collection": "^2.0.0", + "minimatch": "^3.0.4" + }, + "engines": { + "node": "8.* || >= 10.*" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3904,11 +5713,108 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/core/http/react-ui/package.json b/core/http/react-ui/package.json index 6abb21c95c29..994a5139a89c 100644 --- a/core/http/react-ui/package.json +++ b/core/http/react-ui/package.json @@ -8,20 +8,11 @@ "build": "vite build", "preview": "vite preview", "lint": "eslint .", + "i18n:extract": "i18next-parser", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui" }, "dependencies": { - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router-dom": "^7.6.1", - "highlight.js": "^11.11.1", - "marked": "^15.0.7", - "dompurify": "^3.4.0", - "@fortawesome/fontawesome-free": "^6.7.2", - "@modelcontextprotocol/sdk": "^1.25.1", - "@modelcontextprotocol/ext-apps": "^1.2.2", - "yaml": "^2.8.3", "@codemirror/autocomplete": "^6.18.6", "@codemirror/commands": "^6.8.1", "@codemirror/lang-yaml": "^6.1.2", @@ -30,16 +21,31 @@ "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.8", - "@lezer/highlight": "^1.2.1" + "@fortawesome/fontawesome-free": "^6.7.2", + "@lezer/highlight": "^1.2.1", + "@modelcontextprotocol/ext-apps": "^1.2.2", + "@modelcontextprotocol/sdk": "^1.25.1", + "dompurify": "^3.4.0", + "highlight.js": "^11.11.1", + "i18next": "^26.0.8", + "i18next-browser-languagedetector": "^8.2.1", + "i18next-http-backend": "^3.0.6", + "marked": "^15.0.7", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-i18next": "^17.0.6", + "react-router-dom": "^7.6.1", + "yaml": "^2.8.3" }, "devDependencies": { + "@eslint/js": "^9.27.0", + "@playwright/test": "1.58.2", "@vitejs/plugin-react": "^4.5.2", - "vite": "^6.4.2", "eslint": "^9.27.0", - "@eslint/js": "^9.27.0", - "globals": "^16.1.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", - "@playwright/test": "1.58.2" + "globals": "^16.1.0", + "i18next-parser": "^9.4.0", + "vite": "^6.4.2" } } diff --git a/core/http/react-ui/public/locales/de/admin.json b/core/http/react-ui/public/locales/de/admin.json new file mode 100644 index 000000000000..88582b5a20f3 --- /dev/null +++ b/core/http/react-ui/public/locales/de/admin.json @@ -0,0 +1,62 @@ +{ + "manage": { + "title": "System", + "subtitle": "Verwalten Sie installierte Modelle und Backends" + }, + "settings": { + "title": "Einstellungen", + "subtitle": "Konfigurieren Sie die Laufzeiteinstellungen von LocalAI", + "saved": "Einstellungen erfolgreich gespeichert", + "saveFailed": "Speichern fehlgeschlagen: {{message}}", + "loadFailed": "Laden der Einstellungen fehlgeschlagen: {{message}}", + "sections": { + "branding": "Branding", + "watchdog": "Watchdog", + "memory": "Speicher", + "backends": "Backends", + "performance": "Leistung", + "tracing": "Tracing", + "api": "API & CORS", + "p2p": "P2P", + "galleries": "Galerien", + "apikeys": "API-Schlüssel", + "agents": "Agentenaufgaben", + "agentpool": "Agenten-Pool", + "assistant": "LocalAI Assistant", + "responses": "Antworten" + } + }, + "backends": { + "title": "Backend-Verwaltung", + "subtitle": "Entdecken und installieren Sie KI-Backends für Ihre Modelle" + }, + "backendLogs": { + "title": "Backend-Logs", + "subtitle": "Logs laufender Backends anzeigen", + "empty": "Keine Logs verfügbar" + }, + "traces": { + "title": "Traces", + "subtitle": "Protokollierte API-Anfragen, Antworten und Backend-Operationen anzeigen" + }, + "nodes": { + "title": "Verteilte Knoten", + "subtitle": "Backend- und Agenten-Worker-Knoten verwalten" + }, + "p2p": { + "title": "Verteilte KI-Berechnung", + "subtitle": "Skalieren Sie Ihre KI-Workloads über mehrere Geräte mit Peer-to-Peer-Verteilung" + }, + "users": { + "title": "Benutzer", + "subtitle": "Registrierte Benutzer, Rollen und Einladungen verwalten" + }, + "usage": { + "title": "Nutzung", + "subtitle": "API-Token-Nutzungsstatistiken" + }, + "explorer": { + "title": "Explorer", + "subtitle": "Dateien und Konfiguration durchsuchen" + } +} diff --git a/core/http/react-ui/public/locales/de/agents.json b/core/http/react-ui/public/locales/de/agents.json new file mode 100644 index 000000000000..b97077c5eafd --- /dev/null +++ b/core/http/react-ui/public/locales/de/agents.json @@ -0,0 +1,55 @@ +{ + "title": "Agenten", + "subtitle": "Verwalten Sie autonome KI-Agenten", + "actions": { + "agentHub": "Agent Hub", + "import": "Importieren", + "createAgent": "Agent erstellen", + "edit": "Bearbeiten", + "chat": "Chat", + "export": "Exportieren", + "delete": "Löschen", + "pause": "Pausieren", + "resume": "Fortsetzen" + }, + "table": { + "name": "Name", + "status": "Status", + "events": "Ereignisse", + "actions": "Aktionen", + "eventsTooltip": "{{count}} Ereignisse - Zum Anzeigen klicken" + }, + "search": { + "placeholder": "Agenten suchen...", + "summary_one": "{{shown}} von {{total}} Agent", + "summary_other": "{{shown}} von {{total}} Agenten" + }, + "empty": { + "noConfigured": "Keine Agenten konfiguriert", + "noConfiguredText": "Erstellen Sie einen Agenten, um mit autonomen KI-Workflows zu beginnen.", + "browseHub": "Sie wissen nicht, wo Sie anfangen sollen? Durchsuchen Sie den <1>Agent Hub, um vorgefertigte Agentenkonfigurationen zum Importieren zu finden.", + "noMatching": "Keine passenden Agenten", + "noMatchingText": "Kein Agent entspricht \"{{query}}\"" + }, + "sections": { + "yourAgents": "Ihre Agenten", + "otherUsersAgents": "Agenten anderer Benutzer" + }, + "deleteDialog": { + "title": "Agent löschen", + "message": "Agent \"{{name}}\" löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm": "Löschen" + }, + "toasts": { + "loadFailed": "Laden der Agenten fehlgeschlagen: {{message}}", + "deleted": "Agent \"{{name}}\" gelöscht", + "deleteFailed": "Löschen des Agenten fehlgeschlagen: {{message}}", + "paused": "Agent \"{{name}}\" pausiert", + "resumed": "Agent \"{{name}}\" fortgesetzt", + "pauseFailed": "Pausieren des Agenten fehlgeschlagen: {{message}}", + "resumeFailed": "Fortsetzen des Agenten fehlgeschlagen: {{message}}", + "exported": "Agent \"{{name}}\" exportiert", + "exportFailed": "Export des Agenten fehlgeschlagen: {{message}}", + "parseFailed": "Parsen der Agentendatei fehlgeschlagen: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/de/auth.json b/core/http/react-ui/public/locales/de/auth.json new file mode 100644 index 000000000000..2ded3480ad9f --- /dev/null +++ b/core/http/react-ui/public/locales/de/auth.json @@ -0,0 +1,112 @@ +{ + "login": { + "subtitle": "Anmelden, um fortzufahren", + "registerSubtitle": "Konto erstellen", + "createAdminSubtitle": "Erstellen Sie Ihr Administratorkonto", + "tokenSubtitle": "Geben Sie Ihren API-Schlüssel ein, um fortzufahren", + "email": "E-Mail", + "emailPlaceholder": "du@beispiel.com", + "name": "Name", + "namePlaceholder": "Ihr Name (optional)", + "password": "Passwort", + "passwordPlaceholder": "Passwort eingeben...", + "newPasswordPlaceholder": "Mindestens 8 Zeichen", + "confirmPassword": "Passwort bestätigen", + "confirmPasswordPlaceholder": "Passwort wiederholen", + "inviteCodeLabel": "Einladungscode", + "inviteCodeOptional": " (optional — überspringt die Genehmigung)", + "inviteCodePlaceholder": "Einladungscode einfügen...", + "tokenPlaceholder": "API-Schlüssel eingeben...", + "tokenAltPlaceholder": "API-Token eingeben...", + "signIn": "Anmelden", + "signingIn": "Anmeldung läuft...", + "register": "Registrieren", + "creatingAccount": "Konto wird erstellt...", + "createAdminAccount": "Administratorkonto erstellen", + "signInWithGitHub": "Mit GitHub anmelden", + "signInWithSSO": "Mit SSO anmelden", + "loginWithToken": "Mit Token anmelden", + "showTokenLogin": "Mit API-Token anmelden", + "hideTokenLogin": "Token-Anmeldung ausblenden", + "noAccount": "Noch kein Konto?", + "hasAccount": "Bereits ein Konto?", + "or": "oder", + "errors": { + "loginFailed": "Anmeldung fehlgeschlagen", + "registrationFailed": "Registrierung fehlgeschlagen", + "invalidToken": "Ungültiger Token", + "passwordsDoNotMatch": "Passwörter stimmen nicht überein", + "enterToken": "Bitte einen Token eingeben", + "networkError": "Netzwerkfehler", + "inviteRequired": "Ein gültiger Einladungscode ist zur Registrierung erforderlich" + }, + "messages": { + "registrationPending": "Registrierung erfolgreich, Genehmigung ausstehend." + } + }, + "account": { + "title": "Konto", + "subtitle": "Profil, Zugangsdaten und API-Schlüssel", + "unavailable": "Konto nicht verfügbar", + "unavailableText": "Authentifizierung muss aktiviert sein, um Ihr Konto zu verwalten.", + "tabs": { + "profile": "Profil", + "security": "Sicherheit", + "apiKeys": "API-Schlüssel" + }, + "profile": { + "displayName": "Anzeigename", + "displayNameDescription": "Ihr öffentlich angezeigter Name", + "avatarUrl": "Avatar-URL", + "avatarUrlDescription": "URL Ihres Profilbildes", + "avatarUrlPlaceholder": "https://beispiel.com/avatar.png", + "save": "Speichern", + "saving": "Speichern...", + "updated": "Profil aktualisiert", + "updateFailed": "Profilaktualisierung fehlgeschlagen: {{message}}" + }, + "security": { + "currentPassword": "Aktuelles Passwort", + "currentPasswordDescription": "Geben Sie Ihr aktuelles Passwort zur Bestätigung Ihrer Identität ein", + "currentPasswordPlaceholder": "Aktuelles Passwort", + "newPassword": "Neues Passwort", + "newPasswordDescription": "Muss mindestens 8 Zeichen haben", + "newPasswordPlaceholder": "Neues Passwort", + "confirmPassword": "Passwort bestätigen", + "confirmPasswordDescription": "Geben Sie Ihr neues Passwort erneut ein", + "confirmPasswordPlaceholder": "Neues Passwort bestätigen", + "changePassword": "Passwort ändern", + "changing": "Wird geändert...", + "changed": "Passwort geändert", + "passwordsDoNotMatch": "Passwörter stimmen nicht überein", + "tooShort": "Das neue Passwort muss mindestens 8 Zeichen haben", + "oauthOnly": "Passwortverwaltung ist für {{provider}}-Konten nicht verfügbar." + }, + "apiKeys": { + "create": "API-Schlüssel erstellen", + "createDescription": "Erstellen Sie einen Schlüssel für den programmgesteuerten Zugriff", + "namePlaceholder": "Schlüsselname (z. B. meine-app)", + "createButton": "Erstellen", + "creating": "Wird erstellt...", + "createdToast": "API-Schlüssel erstellt", + "createFailed": "API-Schlüssel-Erstellung fehlgeschlagen: {{message}}", + "loadFailed": "Laden der API-Schlüssel fehlgeschlagen: {{message}}", + "revoke": "Widerrufen", + "revokeKey": "Schlüssel widerrufen", + "revokeTitle": "API-Schlüssel widerrufen", + "revokeMessage": "API-Schlüssel \"{{name}}\" widerrufen? Diese Aktion kann nicht rückgängig gemacht werden.", + "revoked": "API-Schlüssel widerrufen", + "revokeFailed": "Widerruf des API-Schlüssels fehlgeschlagen: {{message}}", + "copyNow": "Jetzt kopieren — dieser Schlüssel wird nicht erneut angezeigt", + "copiedToast": "In die Zwischenablage kopiert", + "copyFailed": "Kopieren fehlgeschlagen", + "empty": "Noch keine API-Schlüssel. Erstellen Sie oben einen für programmgesteuerten Zugriff.", + "lastUsed": "zuletzt verwendet {{date}}" + } + }, + "notFound": { + "title": "Seite nicht gefunden", + "text": "Sieht aus, als wäre diese Seite verloren gegangen. Bringen wir Sie zurück auf den Weg.", + "goHome": "Zur Startseite" + } +} diff --git a/core/http/react-ui/public/locales/de/chat.json b/core/http/react-ui/public/locales/de/chat.json new file mode 100644 index 000000000000..7304f8e98810 --- /dev/null +++ b/core/http/react-ui/public/locales/de/chat.json @@ -0,0 +1,116 @@ +{ + "activity": { + "thought": "Gedanke", + "tool": "Werkzeug", + "result": "Ergebnis", + "toolResult": "Ergebnis von {{name}}", + "thinking": "Denke nach..." + }, + "header": { + "manageModeTooltip": "Dieser Chat kann Modelle installieren, Konfigurationen bearbeiten und Backends verwalten, indem mit LocalAI gesprochen wird.", + "modelInfo": "Modell-Info", + "chatSettings": "Chat-Einstellungen", + "modelInfoTitle": "Modell-Info: {{model}}", + "editConfig": "Konfiguration bearbeiten", + "close": "Schließen" + }, + "modelInfo": { + "backend": "Backend", + "modelFile": "Modelldatei", + "contextSize": "Kontextgröße", + "threads": "Threads", + "mcp": "MCP", + "configured": "Konfiguriert", + "chatTemplate": "Chat-Vorlage", + "yes": "Ja", + "gpuLayers": "GPU-Schichten" + }, + "context": { + "label": "Kontext: {{percent}}%", + "labelWithTokens": "Kontext: {{percent}}% ({{tokens}} Tokens)" + }, + "settings": { + "title": "Chat-Einstellungen", + "manageMode": "Verwaltungsmodus", + "manageModeDesc": "Erlauben Sie diesem Chat, Modelle zu installieren, Backends zu wechseln und Konfigurationen zu bearbeiten — durch Gespräche mit LocalAI.", + "systemPrompt": "System-Prompt", + "systemPromptPlaceholder": "Sie sind ein hilfreicher Assistent...", + "temperature": "Temperatur", + "topP": "Top P", + "topK": "Top K", + "contextSize": "Kontextgröße", + "contextSizePlaceholder": "2048", + "clearHistory": "Chat-Verlauf löschen" + }, + "empty": { + "manageTitle": "LocalAI per Chat verwalten", + "manageText": "Bitten Sie um die Installation von Modellen, das Wechseln von Backends, das Bearbeiten von Konfigurationen oder das Prüfen des Status. Der Assistent fasst die Aktionen zusammen und wartet auf Ihre Bestätigung, bevor etwas geändert wird.", + "startTitle": "Beginnen Sie ein Gespräch", + "readyText": "Bereit zum Chatten mit {{model}}", + "selectModelText": "Wählen Sie oben ein Modell aus, um zu beginnen", + "suggestionsManage": [ + "Was ist installiert?", + "Installiere ein Chat-Modell", + "Zeige den Systemstatus", + "Aktualisiere ein Backend" + ], + "suggestionsChat": [ + "Erkläre, wie das funktioniert", + "Hilf mir, Code zu schreiben", + "Fasse ein Dokument zusammen", + "Brainstorme Ideen" + ], + "recent": "Zuletzt", + "noMessages": "Noch keine Nachrichten", + "hintEnter": "Enter zum Senden", + "hintShiftEnter": "Shift+Enter für neue Zeile", + "hintAttach": "Dateien anhängen" + }, + "errors": { + "viewTraces": "Traces für Details anzeigen" + }, + "actions": { + "copy": "Kopieren", + "regenerate": "Neu generieren" + }, + "streaming": { + "transferring": "Modell wird übertragen...", + "transferringTo": "Modell wird zu {{node}} übertragen..." + }, + "tokens": { + "perSec": "{{count}} tok/s", + "peak": "Spitze: {{count}} tok/s", + "usage": "{{prompt}}p + {{completion}}c = {{total}}" + }, + "input": { + "placeholder": "Nachricht...", + "attachFile": "Datei anhängen", + "stopGenerating": "Generierung stoppen", + "canvasTitle": "Canvas — Codeblöcke und Medien in ein Seitenpanel zur Vorschau, zum Kopieren und Herunterladen extrahieren", + "canvasLabel": "Canvas", + "openCanvas": "Canvas-Panel öffnen" + }, + "deleteAllDialog": { + "title": "Alle Chats löschen", + "message": "Alle Chats löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm": "Alle löschen" + }, + "toasts": { + "selectModel": "Bitte wählen Sie ein Modell", + "copied": "In die Zwischenablage kopiert" + }, + "menu": { + "trigger": "Chats", + "triggerTitle": "Unterhaltungen (Strg/Cmd+K)", + "search": "Unterhaltungen suchen...", + "clearSearch": "Suche löschen", + "noMatch": "Keine Unterhaltung entspricht Ihrer Suche", + "noConversations": "Noch keine Unterhaltungen", + "rename": "Umbenennen", + "exportMarkdown": "Als Markdown exportieren", + "deleteChat": "Chat löschen", + "newChat": "Neuer Chat", + "clearAll": "Alle löschen", + "deleteAllTitle": "Alle Unterhaltungen löschen" + } +} diff --git a/core/http/react-ui/public/locales/de/collections.json b/core/http/react-ui/public/locales/de/collections.json new file mode 100644 index 000000000000..6a3bb9239f85 --- /dev/null +++ b/core/http/react-ui/public/locales/de/collections.json @@ -0,0 +1,43 @@ +{ + "title": "Wissensdatenbank", + "subtitle": "Verwalten Sie Dokumentensammlungen für RAG der Agenten", + "newPlaceholder": "Name der neuen Sammlung...", + "actions": { + "create": "Erstellen", + "creating": "Wird erstellt...", + "details": "Details", + "reset": "Zurücksetzen", + "delete": "Löschen", + "viewDetails": "Details anzeigen", + "resetCollection": "Sammlung zurücksetzen", + "deleteCollection": "Sammlung löschen" + }, + "sections": { + "yourCollections": "Ihre Sammlungen", + "otherUsersCollections": "Sammlungen anderer Benutzer" + }, + "empty": { + "title": "Noch keine Sammlungen", + "text": "Mit Sammlungen organisieren Sie Dokumente in Wissensdatenbanken, die Agenten mit RAG (Retrieval-Augmented Generation) durchsuchen können. Erstellen Sie oben eine Sammlung, um zu beginnen.", + "noPersonal": "Sie haben noch keine Sammlungen." + }, + "deleteDialog": { + "title": "Sammlung löschen", + "message": "Sammlung \"{{name}}\" löschen? Dies entfernt alle Einträge und kann nicht rückgängig gemacht werden.", + "confirm": "Löschen" + }, + "resetDialog": { + "title": "Sammlung zurücksetzen", + "message": "Sammlung \"{{name}}\" zurücksetzen? Dies entfernt alle Einträge, behält aber die Sammlung.", + "confirm": "Zurücksetzen" + }, + "toasts": { + "loadFailed": "Laden der Sammlungen fehlgeschlagen: {{message}}", + "created": "Sammlung \"{{name}}\" erstellt", + "createFailed": "Erstellen der Sammlung fehlgeschlagen: {{message}}", + "deleted": "Sammlung \"{{name}}\" gelöscht", + "deleteFailed": "Löschen der Sammlung fehlgeschlagen: {{message}}", + "reset": "Sammlung \"{{name}}\" zurückgesetzt", + "resetFailed": "Zurücksetzen der Sammlung fehlgeschlagen: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/de/common.json b/core/http/react-ui/public/locales/de/common.json new file mode 100644 index 000000000000..8a7bea3dab25 --- /dev/null +++ b/core/http/react-ui/public/locales/de/common.json @@ -0,0 +1,109 @@ +{ + "actions": { + "save": "Speichern", + "saving": "Speichern...", + "cancel": "Abbrechen", + "close": "Schließen", + "confirm": "Bestätigen", + "delete": "Löschen", + "edit": "Bearbeiten", + "add": "Hinzufügen", + "remove": "Entfernen", + "create": "Erstellen", + "update": "Aktualisieren", + "refresh": "Aktualisieren", + "reload": "Neu laden", + "retry": "Erneut versuchen", + "search": "Suchen", + "filter": "Filtern", + "clear": "Leeren", + "reset": "Zurücksetzen", + "apply": "Übernehmen", + "back": "Zurück", + "next": "Weiter", + "previous": "Vorherige", + "open": "Öffnen", + "submit": "Absenden", + "select": "Auswählen", + "selectAll": "Alle auswählen", + "copy": "Kopieren", + "copied": "Kopiert", + "download": "Herunterladen", + "upload": "Hochladen", + "import": "Importieren", + "export": "Exportieren", + "view": "Ansehen", + "details": "Details", + "settings": "Einstellungen", + "help": "Hilfe", + "yes": "Ja", + "no": "Nein", + "loading": "Lädt..." + }, + "status": { + "loading": "Lädt...", + "saving": "Speichern...", + "saved": "Gespeichert", + "ready": "Bereit", + "running": "Läuft", + "stopped": "Gestoppt", + "starting": "Wird gestartet...", + "stopping": "Wird gestoppt...", + "pending": "Ausstehend", + "active": "Aktiv", + "inactive": "Inaktiv", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "online": "Online", + "offline": "Offline", + "error": "Fehler", + "success": "Erfolg", + "warning": "Warnung", + "info": "Info", + "empty": "Keine Einträge", + "none": "Keine", + "unknown": "Unbekannt" + }, + "dialogs": { + "confirmDelete": { + "title": "Löschen bestätigen", + "message": "Möchten Sie dies wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm": "Löschen", + "cancel": "Abbrechen" + }, + "unsavedChanges": { + "title": "Nicht gespeicherte Änderungen", + "message": "Sie haben nicht gespeicherte Änderungen. Möchten Sie diese verwerfen?", + "discard": "Verwerfen", + "keepEditing": "Weiter bearbeiten" + } + }, + "forms": { + "required": "Erforderlich", + "optional": "Optional", + "name": "Name", + "description": "Beschreibung", + "type": "Typ", + "value": "Wert", + "search": "Suchen...", + "selectPlaceholder": "Option auswählen..." + }, + "time": { + "now": "jetzt", + "secondsAgo_one": "vor {{count}} Sekunde", + "secondsAgo_other": "vor {{count}} Sekunden", + "minutesAgo_one": "vor {{count}} Minute", + "minutesAgo_other": "vor {{count}} Minuten", + "hoursAgo_one": "vor {{count}} Stunde", + "hoursAgo_other": "vor {{count}} Stunden", + "daysAgo_one": "vor {{count}} Tag", + "daysAgo_other": "vor {{count}} Tagen" + }, + "units": { + "bytes": "B", + "kilobytes": "KB", + "megabytes": "MB", + "gigabytes": "GB", + "terabytes": "TB" + } +} diff --git a/core/http/react-ui/public/locales/de/errors.json b/core/http/react-ui/public/locales/de/errors.json new file mode 100644 index 000000000000..54e83f585188 --- /dev/null +++ b/core/http/react-ui/public/locales/de/errors.json @@ -0,0 +1,17 @@ +{ + "generic": "Etwas ist schiefgelaufen", + "network": "Netzwerkfehler. Überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.", + "unauthorized": "Sie sind nicht berechtigt, diese Aktion auszuführen.", + "forbidden": "Zugriff verweigert.", + "notFound": "Die angeforderte Ressource wurde nicht gefunden.", + "serverError": "Serverfehler. Bitte versuchen Sie es später erneut.", + "loadFailed": "Laden fehlgeschlagen: {{message}}", + "saveFailed": "Speichern fehlgeschlagen: {{message}}", + "deleteFailed": "Löschen fehlgeschlagen: {{message}}", + "updateFailed": "Aktualisieren fehlgeschlagen: {{message}}", + "createFailed": "Erstellen fehlgeschlagen: {{message}}", + "operationFailed": "Vorgang fehlgeschlagen: {{message}}", + "invalidInput": "Ungültige Eingabe. Bitte überprüfen Sie das Formular und versuchen Sie es erneut.", + "tryAgain": "Bitte versuchen Sie es erneut.", + "contactAdmin": "Wenn das Problem weiterhin besteht, wenden Sie sich an Ihren Administrator." +} diff --git a/core/http/react-ui/public/locales/de/home.json b/core/http/react-ui/public/locales/de/home.json new file mode 100644 index 000000000000..534c4f49b788 --- /dev/null +++ b/core/http/react-ui/public/locales/de/home.json @@ -0,0 +1,66 @@ +{ + "cluster": { + "vram": "Cluster-VRAM", + "ram": "Cluster-RAM", + "nodesOnline": "{{healthy}}/{{total}} Knoten online" + }, + "resourceGpu": "GPU", + "resourceRam": "RAM", + "assistant": { + "title": "LocalAI per Chat verwalten", + "description": "Modelle installieren, Backends wechseln, Konfigurationen bearbeiten und Status prüfen — durch Gespräche mit LocalAI.", + "open": "Assistent öffnen", + "tooltip": "LocalAI per Chat verwalten" + }, + "input": { + "placeholder": "Nachricht...", + "attachImage": "Bild anhängen", + "attachAudio": "Audio anhängen", + "attachFile": "Datei anhängen", + "enterToSend": "Enter zum Senden", + "selectModelFirst": "Bitte zuerst ein Modell auswählen", + "sendMessage": "Nachricht senden", + "selectModelToast": "Bitte zuerst ein Modell auswählen" + }, + "quickLinks": { + "manageByChat": "Per Chat verwalten", + "installedModels": "Installierte Modelle", + "browseGallery": "Galerie durchsuchen", + "importModel": "Modell importieren", + "documentation": "Dokumentation" + }, + "loadedModels": { + "count_one": "{{count}} Modell geladen", + "count_other": "{{count}} Modelle geladen", + "stop": "Modell stoppen", + "stopAll": "Alle stoppen" + }, + "stopDialog": { + "title": "Modell stoppen", + "message": "Modell {{model}} stoppen?", + "confirm": "{{model}} stoppen", + "stopAllTitle": "Alle Modelle stoppen", + "stopAllMessage": "Alle {{count}} geladenen Modelle stoppen?", + "stopAllConfirm": "Alle stoppen", + "stoppedToast": "{{model}} gestoppt", + "allStoppedToast": "Alle Modelle gestoppt", + "stopFailed": "Stopp fehlgeschlagen: {{message}}" + }, + "wizard": { + "getStarted": "Loslegen mit {{name}}", + "intro": "Installieren Sie Ihr erstes Modell, um zu beginnen. Durchsuchen Sie die Galerie oder importieren Sie ein eigenes.", + "steps": { + "step1Title": "Modellgalerie durchsuchen", + "step1Body": "Finden Sie das richtige Modell für Ihre Anforderungen aus unserer kuratierten Sammlung.", + "step2Title": "Modell installieren", + "step2Body": "Klicken Sie auf Installieren, um es automatisch herunterzuladen und zu konfigurieren.", + "step3Title": "Chat starten", + "step3Body": "Chatten Sie mit Ihrem Modell direkt im Browser oder nutzen Sie die API." + }, + "browseGallery": "Modellgalerie durchsuchen", + "importModel": "Modell importieren", + "docs": "Dokumentation", + "noModelsTitle": "Keine Modelle verfügbar", + "noModelsBody": "Es sind noch keine Modelle installiert. Bitten Sie Ihren Administrator, Modelle einzurichten, damit Sie mit dem Chatten beginnen können." + } +} diff --git a/core/http/react-ui/public/locales/de/importModel.json b/core/http/react-ui/public/locales/de/importModel.json new file mode 100644 index 000000000000..67623b955e26 --- /dev/null +++ b/core/http/react-ui/public/locales/de/importModel.json @@ -0,0 +1,142 @@ +{ + "title": "Neues Modell importieren", + "subtitle": { + "simple": "Modell von einer URI importieren — Auto-Erkennung wählt das Backend.", + "powerYaml": "Vollständige Modell-YAML-Konfiguration schreiben.", + "powerPrefs": "Detaillierte Importeinstellungen." + }, + "actions": { + "import": "Modell importieren", + "importing": "Importieren...", + "create": "Erstellen", + "saving": "Speichern...", + "browseHF": "Modelle auf HF durchsuchen", + "addCustom": "Benutzerdefiniert hinzufügen", + "copy": "Kopieren" + }, + "form": { + "modelUri": "Modell-URI", + "uriPlaceholder": "huggingface://TheBloke/Llama-2-7B-Chat-GGUF oder https://example.com/model.gguf", + "uriHint": "Geben Sie die URI oder den Pfad der zu importierenden Modelldatei ein", + "supportedFormats": "Unterstützte URI-Formate", + "options": "Optionen", + "preferences": "Einstellungen (optional)", + "commonPreferences": "Allgemeine Einstellungen", + "customPreferences": "Benutzerdefinierte Einstellungen", + "customKeyValueHint": "Fügen Sie benutzerdefinierte Schlüssel-Wert-Paare für erweiterte Konfiguration hinzu.", + "preferenceKey": "Einstellungsschlüssel für Zeile {{index}}", + "preferenceValue": "Einstellungswert für Zeile {{index}}", + "removePref": "Diese Einstellung entfernen", + "key": "Schlüssel", + "value": "Wert", + "backend": "Backend", + "backendAuto": "Auto-Erkennung (basierend auf URI)", + "backendLoading": "Backends werden geladen…", + "backendSearch": "Backends suchen...", + "backendHint": "Erzwingen Sie ein bestimmtes Backend. Leer lassen für Auto-Erkennung von der URI. Mit \"manuelle Auswahl\" markierte Einträge sind nicht auto-erkennbar — wählen Sie sie selbst, wenn Sie wissen, was das Modell benötigt.", + "backendErrorHint": "Backend-Liste konnte nicht geladen werden — nur Auto-Erkennung.", + "backendNotInstalled": "Dieses Backend ist noch nicht installiert. Beim Senden des Imports wird es zuerst heruntergeladen.", + "modelName": "Modellname", + "modelNamePlaceholder": "Leer lassen, um den Dateinamen zu verwenden", + "modelNameHint": "Benutzerdefinierter Name für das Modell. Wenn leer, wird der Dateiname verwendet.", + "description": "Beschreibung", + "descriptionPlaceholder": "Leer lassen für Standardbeschreibung", + "descriptionHint": "Benutzerdefinierte Beschreibung für das Modell.", + "quantizations": "Quantisierungen", + "quantizationsPlaceholder": "q4_k_m,q4_k_s,q3_k_m (kommasepariert)", + "quantizationsHint": "Bevorzugte Quantisierungen (kommasepariert). Leer lassen für Standard (q4_k_m).", + "mmprojQuantizations": "MMProj-Quantisierungen", + "mmprojQuantizationsPlaceholder": "fp16,fp32 (kommasepariert)", + "mmprojQuantizationsHint": "Bevorzugte MMProj-Quantisierungen. Leer lassen für Standard (fp16).", + "embeddings": "Embeddings", + "embeddingsHint": "Embeddings-Unterstützung für dieses Modell aktivieren.", + "modelType": "Modelltyp", + "modelTypePlaceholder": "AutoModelForCausalLM (für transformers-Backend)", + "modelTypeHint": "Modelltyp für transformers-Backend. Beispiele: AutoModelForCausalLM, SentenceTransformer, Mamba.", + "pipelineType": "Pipeline-Typ", + "pipelineTypeHint": "Pipeline-Typ für diffusers-Backend.", + "schedulerType": "Scheduler-Typ", + "schedulerTypePlaceholder": "k_dpmpp_2m (optional)", + "schedulerTypeHint": "Scheduler-Typ für diffusers-Backend. Beispiele: k_dpmpp_2m, euler_a, ddim.", + "enableParameters": "Aktivierte Parameter", + "enableParametersPlaceholder": "negative_prompt,num_inference_steps (kommasepariert)", + "enableParametersHint": "Aktivierte Parameter für diffusers-Backend (kommasepariert).", + "cuda": "CUDA", + "cudaHint": "CUDA-Unterstützung für GPU-Beschleunigung aktivieren.", + "yamlEditor": "YAML-Konfigurationseditor", + "manualPick": "manuelle Auswahl", + "manualPickTooltip": "Auto-Erkennung leitet nicht zu diesem Backend. Wählen Sie es hier, wenn Sie wissen, dass Sie das wollen." + }, + "modality": { + "text": "Text-LLM", + "asr": "Spracherkennung", + "tts": "Sprachsynthese", + "image": "Bild / Video", + "embeddings": "Embeddings", + "reranker": "Reranker", + "detection": "Objekterkennung", + "vad": "Sprachaktivitätserkennung", + "other": "Sonstige" + }, + "powerTabs": { + "ariaLabel": "Erweiterter Modus-Tab", + "preferences": "Einstellungen", + "yaml": "YAML" + }, + "switchDialog": { + "title": "Benutzerdefinierte Einstellungen behalten?", + "body": "Beim Wechsel in den Einfach-Modus werden Einstellungen außerhalb von Backend, Name und Beschreibung ausgeblendet. Sie werden beim Import dennoch gesendet.", + "cancel": "Abbrechen", + "discard": "Verwerfen & wechseln", + "keep": "Behalten & wechseln" + }, + "estimate": { + "title": "Geschätzte Anforderungen", + "download": "Download: {{size}}", + "vram": "VRAM: {{vram}}" + }, + "toasts": { + "noUri": "Bitte eine Modell-URI eingeben", + "noYaml": "Bitte YAML-Konfiguration eingeben", + "started": "Import gestartet! Fortschritt wird verfolgt...", + "startedWithMeta": "Import gestartet! Fortschritt wird verfolgt... ({{meta}})", + "imported": "Modell erfolgreich importiert!", + "importedYaml": "Modellkonfiguration erfolgreich importiert!", + "importFailed": "Import fehlgeschlagen: {{message}}", + "startImportFailed": "Start des Imports fehlgeschlagen: {{message}}", + "backendsLoadFailed": "Backend-Liste konnte nicht geladen werden — nur Auto-Erkennung wird verwendet", + "modalityClearedBackend": "Backend-Auswahl gelöscht — sie war nicht in der {{label}}-Gruppe.", + "copied": "In die Zwischenablage kopiert" + }, + "uriFormats": { + "huggingface": { + "title": "HuggingFace", + "standard": "Standard HuggingFace-Format", + "short": "Kurzes HuggingFace-Format", + "fullUrl": "Vollständige HuggingFace-URL" + }, + "http": { + "title": "HTTP/HTTPS-URLs", + "direct": "Direkter Download von einer beliebigen HTTPS-URL" + }, + "local": { + "title": "Lokale Dateien", + "filePath": "Lokaler Dateipfad (absolut)", + "directYaml": "Direkte lokale YAML-Konfigurationsdatei" + }, + "oci": { + "title": "OCI-Registry", + "registry": "OCI-Container-Registry", + "tarball": "Lokale OCI-Tarball-Datei" + }, + "ollama": { + "title": "Ollama", + "model": "Ollama-Modellformat" + }, + "yaml": { + "title": "YAML-Konfigurationsdateien", + "remote": "Remote-YAML-Konfigurationsdatei", + "local": "Lokale YAML-Konfigurationsdatei" + } + } +} diff --git a/core/http/react-ui/public/locales/de/media.json b/core/http/react-ui/public/locales/de/media.json new file mode 100644 index 000000000000..57afebff6f94 --- /dev/null +++ b/core/http/react-ui/public/locales/de/media.json @@ -0,0 +1,154 @@ +{ + "studio": { + "tabs": { + "images": "Bilder", + "video": "Video", + "tts": "TTS", + "sound": "Audio" + } + }, + "image": { + "title": "Bildgenerierung", + "labels": { + "model": "Modell", + "prompt": "Prompt", + "promptPlaceholder": "Beschreiben Sie das Bild, das Sie generieren möchten...", + "negativePrompt": "Negativer Prompt", + "negativePromptPlaceholder": "Was vermieden werden soll...", + "size": "Größe", + "count": "Anzahl (1-4)", + "advanced": "Erweiterte Einstellungen", + "imageInputs": "Bildeingaben", + "steps": "Schritte", + "stepsPlaceholder": "20", + "seed": "Seed", + "seedPlaceholder": "Zufällig", + "sourceImage": "Ausgangsbild (img2img)", + "refImages": "Referenzbilder", + "refImagesAdded_one": "{{count}} Bild hinzugefügt", + "refImagesAdded_other": "{{count}} Bilder hinzugefügt" + }, + "actions": { + "generate": "Generieren", + "generating": "Generieren..." + }, + "empty": "Generierte Bilder erscheinen hier", + "toasts": { + "noPrompt": "Bitte einen Prompt eingeben", + "noModel": "Bitte ein Modell auswählen", + "noResults": "Keine Bilder generiert" + } + }, + "video": { + "title": "Videogenerierung", + "labels": { + "model": "Modell", + "prompt": "Prompt", + "promptPlaceholder": "Beschreiben Sie das Video, das Sie generieren möchten...", + "duration": "Dauer (s)", + "fps": "FPS", + "size": "Größe", + "advanced": "Erweiterte Einstellungen", + "seed": "Seed", + "seedPlaceholder": "Zufällig" + }, + "actions": { + "generate": "Video generieren", + "generating": "Generieren..." + }, + "empty": "Generierte Videos erscheinen hier", + "toasts": { + "noPrompt": "Bitte einen Prompt eingeben", + "noModel": "Bitte ein Modell auswählen", + "noResults": "Keine Videos generiert" + } + }, + "tts": { + "title": "Text zu Sprache", + "labels": { + "model": "Modell", + "voice": "Stimme", + "voicePlaceholder": "Optionale Stimm-ID", + "input": "Text", + "inputPlaceholder": "Text zum Synthetisieren eingeben..." + }, + "actions": { + "generate": "Audio generieren", + "generating": "Generieren..." + }, + "empty": "Generiertes Audio erscheint hier", + "toasts": { + "noText": "Bitte Text eingeben", + "noModel": "Bitte ein Modell auswählen", + "generateFailed": "Generierung fehlgeschlagen" + } + }, + "sound": { + "title": "Audiogenerierung", + "labels": { + "model": "Modell", + "prompt": "Prompt", + "promptPlaceholder": "Beschreiben Sie den Klang, den Sie generieren möchten...", + "duration": "Dauer (s)", + "language": "Sprache", + "vocalLanguage": "Vokalsprache", + "lyrics": "Liedtext (optional)", + "lyricsPlaceholder": "Liedtext für Vokalgenerierung", + "advanced": "Erweiterte Einstellungen", + "seed": "Seed", + "seedPlaceholder": "Zufällig" + }, + "actions": { + "generate": "Generieren", + "generating": "Generieren..." + }, + "empty": "Generiertes Audio erscheint hier", + "toasts": { + "noPrompt": "Bitte einen Prompt eingeben", + "noModel": "Bitte ein Modell auswählen", + "generateFailed": "Generierung fehlgeschlagen" + } + }, + "talk": { + "title": "Sprechen", + "subtitle": "Sprachunterhaltung in Echtzeit", + "actions": { + "start": "Sitzung starten", + "stop": "Sitzung beenden", + "connecting": "Verbinden...", + "muted": "Stumm", + "mute": "Stummschalten", + "unmute": "Stumm aufheben" + }, + "labels": { + "model": "Modell", + "voice": "Stimme", + "voicePlaceholder": "alloy", + "language": "Sprache", + "languagePlaceholder": "de", + "instructions": "Anweisungen", + "instructionsPlaceholder": "Persönlichkeit des Assistenten festlegen..." + }, + "status": { + "idle": "Inaktiv", + "connecting": "Verbinden...", + "listening": "Höre zu...", + "speaking": "Spricht...", + "ended": "Sitzung beendet" + }, + "toasts": { + "noModel": "Wählen Sie zuerst ein Modell", + "connectFailed": "Verbindung fehlgeschlagen: {{message}}" + } + }, + "history": { + "title": "Verlauf", + "empty": "Noch kein Verlauf", + "deleteEntry": "Eintrag löschen", + "clear": "Verlauf löschen", + "clearTitle": "Gesamten Verlauf löschen", + "clearMessage": "Alle Verlaufseinträge entfernen? Diese Aktion kann nicht rückgängig gemacht werden.", + "clearConfirm": "Löschen", + "cleared": "Verlauf gelöscht" + } +} diff --git a/core/http/react-ui/public/locales/de/models.json b/core/http/react-ui/public/locales/de/models.json new file mode 100644 index 000000000000..f322b1ccc199 --- /dev/null +++ b/core/http/react-ui/public/locales/de/models.json @@ -0,0 +1,85 @@ +{ + "title": "Modelle installieren", + "subtitle": "Durchsuchen und installieren Sie KI-Modelle aus der Galerie", + "stats": { + "available": "Verfügbar", + "installed": "Installiert" + }, + "actions": { + "addModel": "Modell hinzufügen", + "importModel": "Modell importieren", + "install": "Installieren", + "reinstall": "Neu installieren", + "delete": "Löschen" + }, + "filters": { + "all": "Alle", + "llm": "LLM", + "image": "Bild", + "multimodal": "Multimodal", + "vision": "Vision", + "tts": "TTS", + "stt": "STT", + "embedding": "Embedding", + "rerank": "Rerank", + "allBackends": "Alle Backends", + "searchBackends": "Backends suchen..." + }, + "search": { + "placeholder": "Modelle suchen...", + "clearFilters": "Filter zurücksetzen" + }, + "table": { + "modelName": "Modellname", + "description": "Beschreibung", + "backend": "Backend", + "sizeVram": "Größe / VRAM", + "status": "Status", + "actions": "Aktionen", + "size": "Größe: {{size}}", + "vram": "VRAM: {{vram}}", + "fits": "Passt", + "mayNotFit": "Passt möglicherweise nicht", + "trustRemoteCode": "Trust Remote Code", + "installing": "Installation", + "installingPct": "Installation · {{percent}}%", + "installed": "Installiert", + "notInstalled": "Nicht installiert" + }, + "detail": { + "description": "Beschreibung", + "gallery": "Galerie", + "backend": "Backend", + "size": "Größe", + "vram": "VRAM", + "license": "Lizenz", + "tags": "Tags", + "links": "Links", + "warning": "Warnung", + "files": "Dateien", + "fitsGpu": "Passt in die GPU", + "mayNotFitGpu": "Passt möglicherweise nicht in die GPU", + "requiresTrustRemoteCode": "Erfordert Trust Remote Code", + "fileCount_one": "{{count}} Datei", + "fileCount_other": "{{count}} Dateien", + "filename": "Dateiname", + "uri": "URI", + "sha256": "SHA256" + }, + "empty": { + "title": "Keine Modelle gefunden", + "withFilters": "Keine Modelle entsprechen den aktuellen Such- oder Filterkriterien.", + "noFilters": "Die Modellgalerie ist leer." + }, + "deleteDialog": { + "title": "Modell löschen", + "message": "Modell {{model}} löschen?", + "confirm": "{{model}} löschen", + "deletingToast": "{{model}} wird gelöscht..." + }, + "errors": { + "loadFailed": "Laden der Modelle fehlgeschlagen: {{message}}", + "installFailed": "Installation fehlgeschlagen: {{message}}", + "deleteFailed": "Löschen fehlgeschlagen: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/de/nav.json b/core/http/react-ui/public/locales/de/nav.json new file mode 100644 index 000000000000..81aa157054f9 --- /dev/null +++ b/core/http/react-ui/public/locales/de/nav.json @@ -0,0 +1,51 @@ +{ + "appName": "LocalAI", + "openMenu": "Menü öffnen", + "closeMenu": "Menü schließen", + "primaryNavigation": "Hauptnavigation", + "switchToLightMode": "Zum hellen Modus wechseln", + "switchToDarkMode": "Zum dunklen Modus wechseln", + "expandSidebar": "Seitenleiste erweitern", + "collapseSidebar": "Seitenleiste einklappen", + "changeLanguage": "Sprache ändern", + "logout": "Abmelden", + "accountSettings": "Kontoeinstellungen", + "account": "Konto", + "accountFor": "Konto: {{name}}", + "sections": { + "tools": "Werkzeuge", + "biometrics": "Biometrie", + "agents": "Agenten", + "system": "System" + }, + "items": { + "home": "Start", + "installModels": "Modelle installieren", + "chat": "Chat", + "studio": "Studio", + "talk": "Sprechen", + "fineTune": "Fine-Tuning (Experimentell)", + "quantize": "Quantisierung (Experimentell)", + "faceRecognition": "Gesichtserkennung", + "voiceRecognition": "Spracherkennung", + "agents": "Agenten", + "skills": "Fähigkeiten", + "memory": "Speicher", + "mcpJobs": "MCP CI-Aufgaben", + "usage": "Nutzung", + "users": "Benutzer", + "backends": "Backends", + "traces": "Traces", + "nodes": "Knoten", + "swarm": "Swarm", + "system": "System", + "settings": "Einstellungen", + "api": "API" + }, + "footer": { + "github": "GitHub", + "documentation": "Dokumentation", + "author": "Autor", + "copyright": "© 2023-{{year}} {{author}}" + } +} diff --git a/core/http/react-ui/public/locales/de/skills.json b/core/http/react-ui/public/locales/de/skills.json new file mode 100644 index 000000000000..e648d71957b9 --- /dev/null +++ b/core/http/react-ui/public/locales/de/skills.json @@ -0,0 +1,79 @@ +{ + "title": "Fähigkeiten", + "subtitle": "Verwalten Sie Agentenfähigkeiten (wiederverwendbare Anweisungen und Ressourcen)", + "unavailable": { + "subtitle": "Der Fähigkeiten-Dienst ist nicht verfügbar oder der Index wird neu erstellt. Versuchen Sie es in einem Moment erneut.", + "retry": "Erneut versuchen" + }, + "actions": { + "newSkill": "Neue Fähigkeit", + "createSkill": "Fähigkeit erstellen", + "import": "Importieren", + "importing": "Importieren...", + "gitRepos": "Git-Repos", + "edit": "Bearbeiten", + "delete": "Löschen", + "export": "Exportieren", + "sync": "Synchronisieren", + "addRepo": "Repo hinzufügen", + "adding": "Hinzufügen...", + "remove": "Entfernen", + "enable": "Aktivieren", + "disable": "Deaktivieren" + }, + "search": { + "placeholder": "Fähigkeiten suchen..." + }, + "git": { + "title": "Git-Repositories", + "description": "Fügen Sie Git-Repositories hinzu, aus denen Fähigkeiten geladen werden. Fähigkeiten erscheinen nach der Synchronisierung in der Liste.", + "urlPlaceholder": "https://github.com/user/repo oder git@github.com:user/repo.git", + "noRepos": "Keine Git-Repos konfiguriert. Fügen Sie oben eines hinzu.", + "disabled": "Deaktiviert", + "removeRepo": "Repo entfernen" + }, + "card": { + "noDescription": "Keine Beschreibung", + "readOnly": "Nur lesen", + "editTitle": "Fähigkeit bearbeiten", + "deleteTitle": "Fähigkeit löschen", + "exportTitle": "Als .tar.gz exportieren" + }, + "empty": { + "title": "Keine Fähigkeiten gefunden", + "text": "Erstellen oder importieren Sie eine Fähigkeit, um loszulegen.", + "noPersonal": "Sie haben noch keine Fähigkeiten." + }, + "sections": { + "yourSkills": "Ihre Fähigkeiten", + "otherUsersSkills": "Fähigkeiten anderer Benutzer" + }, + "deleteDialog": { + "title": "Fähigkeit löschen", + "message": "Fähigkeit \"{{name}}\" löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm": "Löschen" + }, + "removeRepoDialog": { + "title": "Git-Repository entfernen", + "message": "Dieses Git-Repository entfernen? Fähigkeiten daraus sind nicht mehr verfügbar.", + "confirm": "Entfernen" + }, + "toasts": { + "loadFailed": "Laden der Fähigkeiten fehlgeschlagen", + "deleted": "Fähigkeit \"{{name}}\" gelöscht", + "deleteFailed": "Löschen der Fähigkeit fehlgeschlagen", + "exported": "Fähigkeit \"{{name}}\" exportiert", + "exportFailed": "Export fehlgeschlagen", + "imported": "Fähigkeit aus \"{{file}}\" importiert", + "importFailed": "Import fehlgeschlagen", + "loadReposFailed": "Laden der Git-Repos fehlgeschlagen", + "repoAdded": "Git-Repo hinzugefügt und synchronisiert", + "addRepoFailed": "Hinzufügen des Repos fehlgeschlagen", + "synced": "Repo synchronisiert", + "syncFailed": "Synchronisierung fehlgeschlagen", + "toggled": "Repo umgeschaltet", + "toggleFailed": "Umschalten fehlgeschlagen", + "removed": "Repo entfernt", + "removeFailed": "Entfernen fehlgeschlagen" + } +} diff --git a/core/http/react-ui/public/locales/en/admin.json b/core/http/react-ui/public/locales/en/admin.json new file mode 100644 index 000000000000..a76ef8df1f00 --- /dev/null +++ b/core/http/react-ui/public/locales/en/admin.json @@ -0,0 +1,62 @@ +{ + "manage": { + "title": "System", + "subtitle": "Manage installed models and backends" + }, + "settings": { + "title": "Settings", + "subtitle": "Configure LocalAI runtime settings", + "saved": "Settings saved successfully", + "saveFailed": "Save failed: {{message}}", + "loadFailed": "Failed to load settings: {{message}}", + "sections": { + "branding": "Branding", + "watchdog": "Watchdog", + "memory": "Memory", + "backends": "Backends", + "performance": "Performance", + "tracing": "Tracing", + "api": "API & CORS", + "p2p": "P2P", + "galleries": "Galleries", + "apikeys": "API Keys", + "agents": "Agent Jobs", + "agentpool": "Agent Pool", + "assistant": "LocalAI Assistant", + "responses": "Responses" + } + }, + "backends": { + "title": "Backend Management", + "subtitle": "Discover and install AI backends to power your models" + }, + "backendLogs": { + "title": "Backend Logs", + "subtitle": "View logs from running backends", + "empty": "No logs available" + }, + "traces": { + "title": "Traces", + "subtitle": "View logged API requests, responses, and backend operations" + }, + "nodes": { + "title": "Distributed Nodes", + "subtitle": "Manage backend and agent worker nodes" + }, + "p2p": { + "title": "Distributed AI Computing", + "subtitle": "Scale your AI workloads across multiple devices with peer-to-peer distribution" + }, + "users": { + "title": "Users", + "subtitle": "Manage registered users, roles, and invites" + }, + "usage": { + "title": "Usage", + "subtitle": "API token usage statistics" + }, + "explorer": { + "title": "Explorer", + "subtitle": "Browse files and configuration" + } +} diff --git a/core/http/react-ui/public/locales/en/agents.json b/core/http/react-ui/public/locales/en/agents.json new file mode 100644 index 000000000000..27a386ce7cc1 --- /dev/null +++ b/core/http/react-ui/public/locales/en/agents.json @@ -0,0 +1,55 @@ +{ + "title": "Agents", + "subtitle": "Manage autonomous AI agents", + "actions": { + "agentHub": "Agent Hub", + "import": "Import", + "createAgent": "Create Agent", + "edit": "Edit", + "chat": "Chat", + "export": "Export", + "delete": "Delete", + "pause": "Pause", + "resume": "Resume" + }, + "table": { + "name": "Name", + "status": "Status", + "events": "Events", + "actions": "Actions", + "eventsTooltip": "{{count}} events - Click to view" + }, + "search": { + "placeholder": "Search agents...", + "summary_one": "{{shown}} of {{total}} agent", + "summary_other": "{{shown}} of {{total}} agents" + }, + "empty": { + "noConfigured": "No agents configured", + "noConfiguredText": "Create an agent to get started with autonomous AI workflows.", + "browseHub": "Don't know where to start? Browse the <1>Agent Hub to find ready-made agent configurations you can import.", + "noMatching": "No matching agents", + "noMatchingText": "No agents match \"{{query}}\"" + }, + "sections": { + "yourAgents": "Your Agents", + "otherUsersAgents": "Other Users' Agents" + }, + "deleteDialog": { + "title": "Delete Agent", + "message": "Delete agent \"{{name}}\"? This action cannot be undone.", + "confirm": "Delete" + }, + "toasts": { + "loadFailed": "Failed to load agents: {{message}}", + "deleted": "Agent \"{{name}}\" deleted", + "deleteFailed": "Failed to delete agent: {{message}}", + "paused": "Agent \"{{name}}\" paused", + "resumed": "Agent \"{{name}}\" resumed", + "pauseFailed": "Failed to pause agent: {{message}}", + "resumeFailed": "Failed to resume agent: {{message}}", + "exported": "Agent \"{{name}}\" exported", + "exportFailed": "Failed to export agent: {{message}}", + "parseFailed": "Failed to parse agent file: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/en/auth.json b/core/http/react-ui/public/locales/en/auth.json new file mode 100644 index 000000000000..69efa39ed27b --- /dev/null +++ b/core/http/react-ui/public/locales/en/auth.json @@ -0,0 +1,112 @@ +{ + "login": { + "subtitle": "Sign in to continue", + "registerSubtitle": "Create an account", + "createAdminSubtitle": "Create your admin account", + "tokenSubtitle": "Enter your API key to continue", + "email": "Email", + "emailPlaceholder": "you@example.com", + "name": "Name", + "namePlaceholder": "Your name (optional)", + "password": "Password", + "passwordPlaceholder": "Enter password...", + "newPasswordPlaceholder": "At least 8 characters", + "confirmPassword": "Confirm Password", + "confirmPasswordPlaceholder": "Repeat password", + "inviteCodeLabel": "Invite Code", + "inviteCodeOptional": " (optional — skip the approval wait)", + "inviteCodePlaceholder": "Paste your invite code...", + "tokenPlaceholder": "Enter API key...", + "tokenAltPlaceholder": "Enter API token...", + "signIn": "Sign In", + "signingIn": "Signing in...", + "register": "Register", + "creatingAccount": "Creating account...", + "createAdminAccount": "Create Admin Account", + "signInWithGitHub": "Sign in with GitHub", + "signInWithSSO": "Sign in with SSO", + "loginWithToken": "Login with Token", + "showTokenLogin": "Login with API Token", + "hideTokenLogin": "Hide token login", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "or": "or", + "errors": { + "loginFailed": "Login failed", + "registrationFailed": "Registration failed", + "invalidToken": "Invalid token", + "passwordsDoNotMatch": "Passwords do not match", + "enterToken": "Please enter a token", + "networkError": "Network error", + "inviteRequired": "A valid invite code is required to register" + }, + "messages": { + "registrationPending": "Registration successful, awaiting approval." + } + }, + "account": { + "title": "Account", + "subtitle": "Profile, credentials, and API keys", + "unavailable": "Account unavailable", + "unavailableText": "Authentication must be enabled to manage your account.", + "tabs": { + "profile": "Profile", + "security": "Security", + "apiKeys": "API Keys" + }, + "profile": { + "displayName": "Display name", + "displayNameDescription": "Your public display name", + "avatarUrl": "Avatar URL", + "avatarUrlDescription": "URL to your profile picture", + "avatarUrlPlaceholder": "https://example.com/avatar.png", + "save": "Save", + "saving": "Saving...", + "updated": "Profile updated", + "updateFailed": "Failed to update profile: {{message}}" + }, + "security": { + "currentPassword": "Current password", + "currentPasswordDescription": "Enter your existing password to verify your identity", + "currentPasswordPlaceholder": "Current password", + "newPassword": "New password", + "newPasswordDescription": "Must be at least 8 characters", + "newPasswordPlaceholder": "New password", + "confirmPassword": "Confirm password", + "confirmPasswordDescription": "Re-enter your new password", + "confirmPasswordPlaceholder": "Confirm new password", + "changePassword": "Change password", + "changing": "Changing...", + "changed": "Password changed", + "passwordsDoNotMatch": "Passwords do not match", + "tooShort": "New password must be at least 8 characters", + "oauthOnly": "Password management is not available for {{provider}} accounts." + }, + "apiKeys": { + "create": "Create API key", + "createDescription": "Generate a key for programmatic access", + "namePlaceholder": "Key name (e.g. my-app)", + "createButton": "Create", + "creating": "Creating...", + "createdToast": "API key created", + "createFailed": "Failed to create API key: {{message}}", + "loadFailed": "Failed to load API keys: {{message}}", + "revoke": "Revoke", + "revokeKey": "Revoke key", + "revokeTitle": "Revoke API Key", + "revokeMessage": "Revoke API key \"{{name}}\"? This cannot be undone.", + "revoked": "API key revoked", + "revokeFailed": "Failed to revoke API key: {{message}}", + "copyNow": "Copy now — this key won't be shown again", + "copiedToast": "Copied to clipboard", + "copyFailed": "Failed to copy", + "empty": "No API keys yet. Create one above to get programmatic access.", + "lastUsed": "last used {{date}}" + } + }, + "notFound": { + "title": "Page Not Found", + "text": "Looks like this page wandered off. Let's get you back on track.", + "goHome": "Go Home" + } +} diff --git a/core/http/react-ui/public/locales/en/chat.json b/core/http/react-ui/public/locales/en/chat.json new file mode 100644 index 000000000000..a4dd502f6dcd --- /dev/null +++ b/core/http/react-ui/public/locales/en/chat.json @@ -0,0 +1,116 @@ +{ + "activity": { + "thought": "Thought", + "tool": "Tool", + "result": "Result", + "toolResult": "{{name}} result", + "thinking": "Thinking..." + }, + "header": { + "manageModeTooltip": "This chat can install models, edit configs and manage backends by talking to LocalAI.", + "modelInfo": "Model info", + "chatSettings": "Chat settings", + "modelInfoTitle": "Model Info: {{model}}", + "editConfig": "Edit config", + "close": "Close" + }, + "modelInfo": { + "backend": "Backend", + "modelFile": "Model file", + "contextSize": "Context size", + "threads": "Threads", + "mcp": "MCP", + "configured": "Configured", + "chatTemplate": "Chat template", + "yes": "Yes", + "gpuLayers": "GPU layers" + }, + "context": { + "label": "Context: {{percent}}%", + "labelWithTokens": "Context: {{percent}}% ({{tokens}} tokens)" + }, + "settings": { + "title": "Chat Settings", + "manageMode": "Manage mode", + "manageModeDesc": "Let this chat install models, switch backends, and edit configs by talking to LocalAI.", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "You are a helpful assistant...", + "temperature": "Temperature", + "topP": "Top P", + "topK": "Top K", + "contextSize": "Context Size", + "contextSizePlaceholder": "2048", + "clearHistory": "Clear chat history" + }, + "empty": { + "manageTitle": "Manage LocalAI by chatting", + "manageText": "Ask to install models, switch backends, edit configs, or check status. The assistant will summarise actions and wait for your confirmation before changing anything.", + "startTitle": "Start a conversation", + "readyText": "Ready to chat with {{model}}", + "selectModelText": "Select a model above to get started", + "suggestionsManage": [ + "What is installed?", + "Install a chat model", + "Show system status", + "Update a backend" + ], + "suggestionsChat": [ + "Explain how this works", + "Help me write code", + "Summarize a document", + "Brainstorm ideas" + ], + "recent": "Recent", + "noMessages": "No messages yet", + "hintEnter": "Enter to send", + "hintShiftEnter": "Shift+Enter for newline", + "hintAttach": "Attach files" + }, + "errors": { + "viewTraces": "View traces for details" + }, + "actions": { + "copy": "Copy", + "regenerate": "Regenerate" + }, + "streaming": { + "transferring": "Transferring model...", + "transferringTo": "Transferring model to {{node}}..." + }, + "tokens": { + "perSec": "{{count}} tok/s", + "peak": "Peak: {{count}} tok/s", + "usage": "{{prompt}}p + {{completion}}c = {{total}}" + }, + "input": { + "placeholder": "Message...", + "attachFile": "Attach file", + "stopGenerating": "Stop generating", + "canvasTitle": "Canvas — extract code blocks and media into a side panel for preview, copy, and download", + "canvasLabel": "Canvas", + "openCanvas": "Open canvas panel" + }, + "deleteAllDialog": { + "title": "Delete All Chats", + "message": "Delete all chats? This cannot be undone.", + "confirm": "Delete all" + }, + "toasts": { + "selectModel": "Please select a model", + "copied": "Copied to clipboard" + }, + "menu": { + "trigger": "Chats", + "triggerTitle": "Conversations (Ctrl/Cmd+K)", + "search": "Search conversations...", + "clearSearch": "Clear search", + "noMatch": "No conversations match your search", + "noConversations": "No conversations yet", + "rename": "Rename", + "exportMarkdown": "Export as Markdown", + "deleteChat": "Delete chat", + "newChat": "New chat", + "clearAll": "Clear all", + "deleteAllTitle": "Delete all conversations" + } +} diff --git a/core/http/react-ui/public/locales/en/collections.json b/core/http/react-ui/public/locales/en/collections.json new file mode 100644 index 000000000000..297f17ad3395 --- /dev/null +++ b/core/http/react-ui/public/locales/en/collections.json @@ -0,0 +1,43 @@ +{ + "title": "Knowledge Base", + "subtitle": "Manage document collections for agent RAG", + "newPlaceholder": "New collection name...", + "actions": { + "create": "Create", + "creating": "Creating...", + "details": "Details", + "reset": "Reset", + "delete": "Delete", + "viewDetails": "View details", + "resetCollection": "Reset collection", + "deleteCollection": "Delete collection" + }, + "sections": { + "yourCollections": "Your Collections", + "otherUsersCollections": "Other Users' Collections" + }, + "empty": { + "title": "No collections yet", + "text": "Collections let you organize documents into knowledge bases that agents can search using RAG (Retrieval-Augmented Generation). Create a collection above to get started.", + "noPersonal": "You have no collections yet." + }, + "deleteDialog": { + "title": "Delete Collection", + "message": "Delete collection \"{{name}}\"? This will remove all entries and cannot be undone.", + "confirm": "Delete" + }, + "resetDialog": { + "title": "Reset Collection", + "message": "Reset collection \"{{name}}\"? This will remove all entries but keep the collection.", + "confirm": "Reset" + }, + "toasts": { + "loadFailed": "Failed to load collections: {{message}}", + "created": "Collection \"{{name}}\" created", + "createFailed": "Failed to create collection: {{message}}", + "deleted": "Collection \"{{name}}\" deleted", + "deleteFailed": "Failed to delete collection: {{message}}", + "reset": "Collection \"{{name}}\" reset", + "resetFailed": "Failed to reset collection: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/en/common.json b/core/http/react-ui/public/locales/en/common.json new file mode 100644 index 000000000000..a18ecdb3acb8 --- /dev/null +++ b/core/http/react-ui/public/locales/en/common.json @@ -0,0 +1,109 @@ +{ + "actions": { + "save": "Save", + "saving": "Saving...", + "cancel": "Cancel", + "close": "Close", + "confirm": "Confirm", + "delete": "Delete", + "edit": "Edit", + "add": "Add", + "remove": "Remove", + "create": "Create", + "update": "Update", + "refresh": "Refresh", + "reload": "Reload", + "retry": "Retry", + "search": "Search", + "filter": "Filter", + "clear": "Clear", + "reset": "Reset", + "apply": "Apply", + "back": "Back", + "next": "Next", + "previous": "Previous", + "open": "Open", + "submit": "Submit", + "select": "Select", + "selectAll": "Select all", + "copy": "Copy", + "copied": "Copied", + "download": "Download", + "upload": "Upload", + "import": "Import", + "export": "Export", + "view": "View", + "details": "Details", + "settings": "Settings", + "help": "Help", + "yes": "Yes", + "no": "No", + "loading": "Loading..." + }, + "status": { + "loading": "Loading...", + "saving": "Saving...", + "saved": "Saved", + "ready": "Ready", + "running": "Running", + "stopped": "Stopped", + "starting": "Starting...", + "stopping": "Stopping...", + "pending": "Pending", + "active": "Active", + "inactive": "Inactive", + "enabled": "Enabled", + "disabled": "Disabled", + "online": "Online", + "offline": "Offline", + "error": "Error", + "success": "Success", + "warning": "Warning", + "info": "Info", + "empty": "No items", + "none": "None", + "unknown": "Unknown" + }, + "dialogs": { + "confirmDelete": { + "title": "Confirm deletion", + "message": "Are you sure you want to delete this? This action cannot be undone.", + "confirm": "Delete", + "cancel": "Cancel" + }, + "unsavedChanges": { + "title": "Unsaved changes", + "message": "You have unsaved changes. Do you want to discard them?", + "discard": "Discard", + "keepEditing": "Keep editing" + } + }, + "forms": { + "required": "Required", + "optional": "Optional", + "name": "Name", + "description": "Description", + "type": "Type", + "value": "Value", + "search": "Search...", + "selectPlaceholder": "Select an option..." + }, + "time": { + "now": "now", + "secondsAgo_one": "{{count}} second ago", + "secondsAgo_other": "{{count}} seconds ago", + "minutesAgo_one": "{{count}} minute ago", + "minutesAgo_other": "{{count}} minutes ago", + "hoursAgo_one": "{{count}} hour ago", + "hoursAgo_other": "{{count}} hours ago", + "daysAgo_one": "{{count}} day ago", + "daysAgo_other": "{{count}} days ago" + }, + "units": { + "bytes": "B", + "kilobytes": "KB", + "megabytes": "MB", + "gigabytes": "GB", + "terabytes": "TB" + } +} diff --git a/core/http/react-ui/public/locales/en/errors.json b/core/http/react-ui/public/locales/en/errors.json new file mode 100644 index 000000000000..258da95ba46c --- /dev/null +++ b/core/http/react-ui/public/locales/en/errors.json @@ -0,0 +1,17 @@ +{ + "generic": "Something went wrong", + "network": "Network error. Check your connection and try again.", + "unauthorized": "You are not authorized to perform this action.", + "forbidden": "Access denied.", + "notFound": "The requested resource was not found.", + "serverError": "Server error. Please try again later.", + "loadFailed": "Failed to load: {{message}}", + "saveFailed": "Save failed: {{message}}", + "deleteFailed": "Delete failed: {{message}}", + "updateFailed": "Update failed: {{message}}", + "createFailed": "Create failed: {{message}}", + "operationFailed": "Operation failed: {{message}}", + "invalidInput": "Invalid input. Please check the form and try again.", + "tryAgain": "Please try again.", + "contactAdmin": "If the problem persists, contact your administrator." +} diff --git a/core/http/react-ui/public/locales/en/home.json b/core/http/react-ui/public/locales/en/home.json new file mode 100644 index 000000000000..22d5a1048704 --- /dev/null +++ b/core/http/react-ui/public/locales/en/home.json @@ -0,0 +1,66 @@ +{ + "cluster": { + "vram": "Cluster VRAM", + "ram": "Cluster RAM", + "nodesOnline": "{{healthy}}/{{total}} nodes online" + }, + "resourceGpu": "GPU", + "resourceRam": "RAM", + "assistant": { + "title": "Manage LocalAI by chatting", + "description": "Install models, switch backends, edit configs and check status by talking to LocalAI.", + "open": "Open assistant", + "tooltip": "Manage LocalAI by chatting" + }, + "input": { + "placeholder": "Message...", + "attachImage": "Attach image", + "attachAudio": "Attach audio", + "attachFile": "Attach file", + "enterToSend": "Enter to send", + "selectModelFirst": "Select a model first", + "sendMessage": "Send message", + "selectModelToast": "Please select a model first" + }, + "quickLinks": { + "manageByChat": "Manage by chat", + "installedModels": "Installed Models", + "browseGallery": "Browse Gallery", + "importModel": "Import Model", + "documentation": "Documentation" + }, + "loadedModels": { + "count_one": "{{count}} model loaded", + "count_other": "{{count}} models loaded", + "stop": "Stop model", + "stopAll": "Stop all" + }, + "stopDialog": { + "title": "Stop Model", + "message": "Stop model {{model}}?", + "confirm": "Stop {{model}}", + "stopAllTitle": "Stop All Models", + "stopAllMessage": "Stop all {{count}} loaded models?", + "stopAllConfirm": "Stop all", + "stoppedToast": "Stopped {{model}}", + "allStoppedToast": "All models stopped", + "stopFailed": "Failed to stop: {{message}}" + }, + "wizard": { + "getStarted": "Get started with {{name}}", + "intro": "Install your first model to begin. Browse the gallery or import your own.", + "steps": { + "step1Title": "Browse the Model Gallery", + "step1Body": "Find the right model for your needs from our curated collection.", + "step2Title": "Install a Model", + "step2Body": "Click install to download and configure it automatically.", + "step3Title": "Start Chatting", + "step3Body": "Chat with your model right from the browser or use the API." + }, + "browseGallery": "Browse Model Gallery", + "importModel": "Import Model", + "docs": "Docs", + "noModelsTitle": "No Models Available", + "noModelsBody": "There are no models installed yet. Ask your administrator to set up models so you can start chatting." + } +} diff --git a/core/http/react-ui/public/locales/en/importModel.json b/core/http/react-ui/public/locales/en/importModel.json new file mode 100644 index 000000000000..0a67d1613fba --- /dev/null +++ b/core/http/react-ui/public/locales/en/importModel.json @@ -0,0 +1,142 @@ +{ + "title": "Import New Model", + "subtitle": { + "simple": "Import a model from a URI — auto-detect picks the backend.", + "powerYaml": "Write the full model YAML configuration.", + "powerPrefs": "Fine-grained import preferences." + }, + "actions": { + "import": "Import Model", + "importing": "Importing...", + "create": "Create", + "saving": "Saving...", + "browseHF": "Browse models on HF", + "addCustom": "Add Custom", + "copy": "Copy" + }, + "form": { + "modelUri": "Model URI", + "uriPlaceholder": "huggingface://TheBloke/Llama-2-7B-Chat-GGUF or https://example.com/model.gguf", + "uriHint": "Enter the URI or path to the model file you want to import", + "supportedFormats": "Supported URI Formats", + "options": "Options", + "preferences": "Preferences (Optional)", + "commonPreferences": "Common Preferences", + "customPreferences": "Custom Preferences", + "customKeyValueHint": "Add custom key-value pairs for advanced configuration.", + "preferenceKey": "Preference key for row {{index}}", + "preferenceValue": "Preference value for row {{index}}", + "removePref": "Remove this preference", + "key": "Key", + "value": "Value", + "backend": "Backend", + "backendAuto": "Auto-detect (based on URI)", + "backendLoading": "Loading backends…", + "backendSearch": "Search backends...", + "backendHint": "Force a specific backend. Leave empty to auto-detect from the URI. Items marked \"manual pick\" aren't auto-detectable — pick them yourself if you know what the model needs.", + "backendErrorHint": "Could not load backend list — auto-detect only.", + "backendNotInstalled": "This backend isn't installed yet. Submitting import will download it first.", + "modelName": "Model Name", + "modelNamePlaceholder": "Leave empty to use filename", + "modelNameHint": "Custom name for the model. If empty, the filename will be used.", + "description": "Description", + "descriptionPlaceholder": "Leave empty to use default description", + "descriptionHint": "Custom description for the model.", + "quantizations": "Quantizations", + "quantizationsPlaceholder": "q4_k_m,q4_k_s,q3_k_m (comma-separated)", + "quantizationsHint": "Preferred quantizations (comma-separated). Leave empty for default (q4_k_m).", + "mmprojQuantizations": "MMProj Quantizations", + "mmprojQuantizationsPlaceholder": "fp16,fp32 (comma-separated)", + "mmprojQuantizationsHint": "Preferred MMProj quantizations. Leave empty for default (fp16).", + "embeddings": "Embeddings", + "embeddingsHint": "Enable embeddings support for this model.", + "modelType": "Model Type", + "modelTypePlaceholder": "AutoModelForCausalLM (for transformers backend)", + "modelTypeHint": "Model type for transformers backend. Examples: AutoModelForCausalLM, SentenceTransformer, Mamba.", + "pipelineType": "Pipeline Type", + "pipelineTypeHint": "Pipeline type for diffusers backend.", + "schedulerType": "Scheduler Type", + "schedulerTypePlaceholder": "k_dpmpp_2m (optional)", + "schedulerTypeHint": "Scheduler type for diffusers backend. Examples: k_dpmpp_2m, euler_a, ddim.", + "enableParameters": "Enable Parameters", + "enableParametersPlaceholder": "negative_prompt,num_inference_steps (comma-separated)", + "enableParametersHint": "Enabled parameters for diffusers backend (comma-separated).", + "cuda": "CUDA", + "cudaHint": "Enable CUDA support for GPU acceleration.", + "yamlEditor": "YAML Configuration Editor", + "manualPick": "manual pick", + "manualPickTooltip": "Auto-detect won't route to this backend. Pick it here if you know that's what you want." + }, + "modality": { + "text": "Text LLM", + "asr": "Speech recognition", + "tts": "Text-to-speech", + "image": "Image / Video", + "embeddings": "Embeddings", + "reranker": "Rerankers", + "detection": "Object detection", + "vad": "Voice activity detection", + "other": "Other" + }, + "powerTabs": { + "ariaLabel": "Advanced mode tab", + "preferences": "Preferences", + "yaml": "YAML" + }, + "switchDialog": { + "title": "Keep your custom preferences?", + "body": "Switching to Simple mode hides preferences beyond backend, name, and description. They'll still be sent when you import.", + "cancel": "Cancel", + "discard": "Discard & switch", + "keep": "Keep & switch" + }, + "estimate": { + "title": "Estimated requirements", + "download": "Download: {{size}}", + "vram": "VRAM: {{vram}}" + }, + "toasts": { + "noUri": "Please enter a model URI", + "noYaml": "Please enter YAML configuration", + "started": "Import started! Tracking progress...", + "startedWithMeta": "Import started! Tracking progress... ({{meta}})", + "imported": "Model imported successfully!", + "importedYaml": "Model configuration imported successfully!", + "importFailed": "Import failed: {{message}}", + "startImportFailed": "Failed to start import: {{message}}", + "backendsLoadFailed": "Could not load backend list — using auto-detect only", + "modalityClearedBackend": "Cleared backend selection — it wasn't in the {{label}} group.", + "copied": "Copied to clipboard" + }, + "uriFormats": { + "huggingface": { + "title": "HuggingFace", + "standard": "Standard HuggingFace format", + "short": "Short HuggingFace format", + "fullUrl": "Full HuggingFace URL" + }, + "http": { + "title": "HTTP/HTTPS URLs", + "direct": "Direct download from any HTTPS URL" + }, + "local": { + "title": "Local Files", + "filePath": "Local file path (absolute)", + "directYaml": "Direct local YAML config file" + }, + "oci": { + "title": "OCI Registry", + "registry": "OCI container registry", + "tarball": "Local OCI tarball file" + }, + "ollama": { + "title": "Ollama", + "model": "Ollama model format" + }, + "yaml": { + "title": "YAML Configuration Files", + "remote": "Remote YAML config file", + "local": "Local YAML config file" + } + } +} diff --git a/core/http/react-ui/public/locales/en/media.json b/core/http/react-ui/public/locales/en/media.json new file mode 100644 index 000000000000..4f5c755de422 --- /dev/null +++ b/core/http/react-ui/public/locales/en/media.json @@ -0,0 +1,154 @@ +{ + "studio": { + "tabs": { + "images": "Images", + "video": "Video", + "tts": "TTS", + "sound": "Sound" + } + }, + "image": { + "title": "Image Generation", + "labels": { + "model": "Model", + "prompt": "Prompt", + "promptPlaceholder": "Describe the image you want to generate...", + "negativePrompt": "Negative Prompt", + "negativePromptPlaceholder": "What to avoid...", + "size": "Size", + "count": "Count (1-4)", + "advanced": "Advanced Settings", + "imageInputs": "Image Inputs", + "steps": "Steps", + "stepsPlaceholder": "20", + "seed": "Seed", + "seedPlaceholder": "Random", + "sourceImage": "Source Image (img2img)", + "refImages": "Reference Images", + "refImagesAdded_one": "{{count}} image added", + "refImagesAdded_other": "{{count}} images added" + }, + "actions": { + "generate": "Generate", + "generating": "Generating..." + }, + "empty": "Generated images will appear here", + "toasts": { + "noPrompt": "Please enter a prompt", + "noModel": "Please select a model", + "noResults": "No images generated" + } + }, + "video": { + "title": "Video Generation", + "labels": { + "model": "Model", + "prompt": "Prompt", + "promptPlaceholder": "Describe the video you want to generate...", + "duration": "Duration (seconds)", + "fps": "FPS", + "size": "Size", + "advanced": "Advanced Settings", + "seed": "Seed", + "seedPlaceholder": "Random" + }, + "actions": { + "generate": "Generate", + "generating": "Generating..." + }, + "empty": "Generated video will appear here", + "toasts": { + "noPrompt": "Please enter a prompt", + "noModel": "Please select a model", + "noResults": "No video generated" + } + }, + "tts": { + "title": "Text to Speech", + "labels": { + "model": "Model", + "voice": "Voice", + "voicePlaceholder": "Optional voice ID", + "input": "Text", + "inputPlaceholder": "Enter text to synthesize..." + }, + "actions": { + "generate": "Generate", + "generating": "Generating..." + }, + "empty": "Generated audio will appear here", + "toasts": { + "noText": "Please enter text", + "noModel": "Please select a model", + "generateFailed": "Generation failed" + } + }, + "sound": { + "title": "Sound Generation", + "labels": { + "model": "Model", + "prompt": "Prompt", + "promptPlaceholder": "Describe the sound you want to generate...", + "duration": "Duration (seconds)", + "language": "Language", + "vocalLanguage": "Vocal language", + "lyrics": "Lyrics (optional)", + "lyricsPlaceholder": "Lyrics for vocal generation", + "advanced": "Advanced Settings", + "seed": "Seed", + "seedPlaceholder": "Random" + }, + "actions": { + "generate": "Generate", + "generating": "Generating..." + }, + "empty": "Generated audio will appear here", + "toasts": { + "noPrompt": "Please enter a prompt", + "noModel": "Please select a model", + "generateFailed": "Generation failed" + } + }, + "talk": { + "title": "Talk", + "subtitle": "Realtime voice conversation", + "actions": { + "start": "Start session", + "stop": "End session", + "connecting": "Connecting...", + "muted": "Muted", + "mute": "Mute", + "unmute": "Unmute" + }, + "labels": { + "model": "Model", + "voice": "Voice", + "voicePlaceholder": "alloy", + "language": "Language", + "languagePlaceholder": "en", + "instructions": "Instructions", + "instructionsPlaceholder": "Set the assistant's persona..." + }, + "status": { + "idle": "Idle", + "connecting": "Connecting...", + "listening": "Listening...", + "speaking": "Speaking...", + "ended": "Session ended" + }, + "toasts": { + "noModel": "Select a model first", + "connectFailed": "Failed to connect: {{message}}" + } + }, + "history": { + "title": "History", + "empty": "No history yet", + "deleteEntry": "Delete entry", + "clear": "Clear history", + "clearTitle": "Clear all history", + "clearMessage": "Remove all history entries? This cannot be undone.", + "clearConfirm": "Clear", + "cleared": "History cleared" + } +} diff --git a/core/http/react-ui/public/locales/en/models.json b/core/http/react-ui/public/locales/en/models.json new file mode 100644 index 000000000000..a20ad309a851 --- /dev/null +++ b/core/http/react-ui/public/locales/en/models.json @@ -0,0 +1,85 @@ +{ + "title": "Install Models", + "subtitle": "Browse and install AI models from the gallery", + "stats": { + "available": "Available", + "installed": "Installed" + }, + "actions": { + "addModel": "Add Model", + "importModel": "Import Model", + "install": "Install", + "reinstall": "Reinstall", + "delete": "Delete" + }, + "filters": { + "all": "All", + "llm": "LLM", + "image": "Image", + "multimodal": "Multimodal", + "vision": "Vision", + "tts": "TTS", + "stt": "STT", + "embedding": "Embedding", + "rerank": "Rerank", + "allBackends": "All Backends", + "searchBackends": "Search backends..." + }, + "search": { + "placeholder": "Search models...", + "clearFilters": "Clear filters" + }, + "table": { + "modelName": "Model Name", + "description": "Description", + "backend": "Backend", + "sizeVram": "Size / VRAM", + "status": "Status", + "actions": "Actions", + "size": "Size: {{size}}", + "vram": "VRAM: {{vram}}", + "fits": "Fits", + "mayNotFit": "May not fit", + "trustRemoteCode": "Trust Remote Code", + "installing": "Installing", + "installingPct": "Installing · {{percent}}%", + "installed": "Installed", + "notInstalled": "Not Installed" + }, + "detail": { + "description": "Description", + "gallery": "Gallery", + "backend": "Backend", + "size": "Size", + "vram": "VRAM", + "license": "License", + "tags": "Tags", + "links": "Links", + "warning": "Warning", + "files": "Files", + "fitsGpu": "Fits in GPU", + "mayNotFitGpu": "May not fit in GPU", + "requiresTrustRemoteCode": "Requires Trust Remote Code", + "fileCount_one": "{{count}} file", + "fileCount_other": "{{count}} files", + "filename": "Filename", + "uri": "URI", + "sha256": "SHA256" + }, + "empty": { + "title": "No models found", + "withFilters": "No models match your current search or filters.", + "noFilters": "The model gallery is empty." + }, + "deleteDialog": { + "title": "Delete Model", + "message": "Delete model {{model}}?", + "confirm": "Delete {{model}}", + "deletingToast": "Deleting {{model}}..." + }, + "errors": { + "loadFailed": "Failed to load models: {{message}}", + "installFailed": "Failed to install: {{message}}", + "deleteFailed": "Failed to delete: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/en/nav.json b/core/http/react-ui/public/locales/en/nav.json new file mode 100644 index 000000000000..b660c7d48ac9 --- /dev/null +++ b/core/http/react-ui/public/locales/en/nav.json @@ -0,0 +1,51 @@ +{ + "appName": "LocalAI", + "openMenu": "Open menu", + "closeMenu": "Close menu", + "primaryNavigation": "Primary navigation", + "switchToLightMode": "Switch to light mode", + "switchToDarkMode": "Switch to dark mode", + "expandSidebar": "Expand sidebar", + "collapseSidebar": "Collapse sidebar", + "changeLanguage": "Change language", + "logout": "Logout", + "accountSettings": "Account settings", + "account": "Account", + "accountFor": "Account: {{name}}", + "sections": { + "tools": "Tools", + "biometrics": "Biometrics", + "agents": "Agents", + "system": "System" + }, + "items": { + "home": "Home", + "installModels": "Install Models", + "chat": "Chat", + "studio": "Studio", + "talk": "Talk", + "fineTune": "Fine-Tune (Experimental)", + "quantize": "Quantize (Experimental)", + "faceRecognition": "Face Recognition", + "voiceRecognition": "Voice Recognition", + "agents": "Agents", + "skills": "Skills", + "memory": "Memory", + "mcpJobs": "MCP CI Jobs", + "usage": "Usage", + "users": "Users", + "backends": "Backends", + "traces": "Traces", + "nodes": "Nodes", + "swarm": "Swarm", + "system": "System", + "settings": "Settings", + "api": "API" + }, + "footer": { + "github": "GitHub", + "documentation": "Documentation", + "author": "Author", + "copyright": "© 2023-{{year}} {{author}}" + } +} diff --git a/core/http/react-ui/public/locales/en/skills.json b/core/http/react-ui/public/locales/en/skills.json new file mode 100644 index 000000000000..5751c228dfc7 --- /dev/null +++ b/core/http/react-ui/public/locales/en/skills.json @@ -0,0 +1,79 @@ +{ + "title": "Skills", + "subtitle": "Manage agent skills (reusable instructions and resources)", + "unavailable": { + "subtitle": "Skills service is not available or the index is rebuilding. Try again in a moment.", + "retry": "Retry" + }, + "actions": { + "newSkill": "New skill", + "createSkill": "Create skill", + "import": "Import", + "importing": "Importing...", + "gitRepos": "Git Repos", + "edit": "Edit", + "delete": "Delete", + "export": "Export", + "sync": "Sync", + "addRepo": "Add repo", + "adding": "Adding...", + "remove": "Remove", + "enable": "Enable", + "disable": "Disable" + }, + "search": { + "placeholder": "Search skills..." + }, + "git": { + "title": "Git repositories", + "description": "Add Git repositories to pull skills from. Skills will appear in the list after sync.", + "urlPlaceholder": "https://github.com/user/repo or git@github.com:user/repo.git", + "noRepos": "No Git repos configured. Add one above.", + "disabled": "Disabled", + "removeRepo": "Remove repo" + }, + "card": { + "noDescription": "No description", + "readOnly": "Read-only", + "editTitle": "Edit skill", + "deleteTitle": "Delete skill", + "exportTitle": "Export as .tar.gz" + }, + "empty": { + "title": "No skills found", + "text": "Create a skill or import one to get started.", + "noPersonal": "You have no skills yet." + }, + "sections": { + "yourSkills": "Your Skills", + "otherUsersSkills": "Other Users' Skills" + }, + "deleteDialog": { + "title": "Delete Skill", + "message": "Delete skill \"{{name}}\"? This action cannot be undone.", + "confirm": "Delete" + }, + "removeRepoDialog": { + "title": "Remove Git Repository", + "message": "Remove this Git repository? Skills from it will no longer be available.", + "confirm": "Remove" + }, + "toasts": { + "loadFailed": "Failed to load skills", + "deleted": "Skill \"{{name}}\" deleted", + "deleteFailed": "Failed to delete skill", + "exported": "Skill \"{{name}}\" exported", + "exportFailed": "Export failed", + "imported": "Skill imported from \"{{file}}\"", + "importFailed": "Import failed", + "loadReposFailed": "Failed to load Git repos", + "repoAdded": "Git repo added and syncing", + "addRepoFailed": "Failed to add repo", + "synced": "Repo synced", + "syncFailed": "Sync failed", + "toggled": "Repo toggled", + "toggleFailed": "Toggle failed", + "removed": "Repo removed", + "removeFailed": "Remove failed" + } +} diff --git a/core/http/react-ui/public/locales/es/admin.json b/core/http/react-ui/public/locales/es/admin.json new file mode 100644 index 000000000000..fee37c1ab959 --- /dev/null +++ b/core/http/react-ui/public/locales/es/admin.json @@ -0,0 +1,62 @@ +{ + "manage": { + "title": "Sistema", + "subtitle": "Administra modelos y backends instalados" + }, + "settings": { + "title": "Configuración", + "subtitle": "Configura los ajustes de tiempo de ejecución de LocalAI", + "saved": "Configuración guardada con éxito", + "saveFailed": "Error al guardar: {{message}}", + "loadFailed": "Error al cargar configuración: {{message}}", + "sections": { + "branding": "Branding", + "watchdog": "Watchdog", + "memory": "Memoria", + "backends": "Backends", + "performance": "Rendimiento", + "tracing": "Tracing", + "api": "API y CORS", + "p2p": "P2P", + "galleries": "Galerías", + "apikeys": "Claves API", + "agents": "Trabajos de agentes", + "agentpool": "Pool de agentes", + "assistant": "LocalAI Assistant", + "responses": "Respuestas" + } + }, + "backends": { + "title": "Administración de backends", + "subtitle": "Descubre e instala backends de IA para tus modelos" + }, + "backendLogs": { + "title": "Logs de backends", + "subtitle": "Visualiza logs de los backends en ejecución", + "empty": "No hay logs disponibles" + }, + "traces": { + "title": "Trazas", + "subtitle": "Visualiza peticiones API, respuestas y operaciones de backend registradas" + }, + "nodes": { + "title": "Nodos distribuidos", + "subtitle": "Administra nodos worker de backends y agentes" + }, + "p2p": { + "title": "Computación de IA distribuida", + "subtitle": "Escala tus cargas de trabajo de IA en múltiples dispositivos con distribución peer-to-peer" + }, + "users": { + "title": "Usuarios", + "subtitle": "Administra usuarios registrados, roles e invitaciones" + }, + "usage": { + "title": "Uso", + "subtitle": "Estadísticas de uso de tokens API" + }, + "explorer": { + "title": "Explorador", + "subtitle": "Explora archivos y configuración" + } +} diff --git a/core/http/react-ui/public/locales/es/agents.json b/core/http/react-ui/public/locales/es/agents.json new file mode 100644 index 000000000000..5b28596a5b8b --- /dev/null +++ b/core/http/react-ui/public/locales/es/agents.json @@ -0,0 +1,55 @@ +{ + "title": "Agentes", + "subtitle": "Administra agentes de IA autónomos", + "actions": { + "agentHub": "Agent Hub", + "import": "Importar", + "createAgent": "Crear agente", + "edit": "Editar", + "chat": "Chat", + "export": "Exportar", + "delete": "Eliminar", + "pause": "Pausar", + "resume": "Reanudar" + }, + "table": { + "name": "Nombre", + "status": "Estado", + "events": "Eventos", + "actions": "Acciones", + "eventsTooltip": "{{count}} eventos - Haz clic para ver" + }, + "search": { + "placeholder": "Buscar agentes...", + "summary_one": "{{shown}} de {{total}} agente", + "summary_other": "{{shown}} de {{total}} agentes" + }, + "empty": { + "noConfigured": "No hay agentes configurados", + "noConfiguredText": "Crea un agente para empezar con flujos de trabajo de IA autónomos.", + "browseHub": "¿No sabes por dónde empezar? Explora el <1>Agent Hub para encontrar configuraciones de agentes listas para importar.", + "noMatching": "No hay agentes coincidentes", + "noMatchingText": "Ningún agente coincide con \"{{query}}\"" + }, + "sections": { + "yourAgents": "Tus agentes", + "otherUsersAgents": "Agentes de otros usuarios" + }, + "deleteDialog": { + "title": "Eliminar agente", + "message": "¿Eliminar el agente \"{{name}}\"? Esta acción no se puede deshacer.", + "confirm": "Eliminar" + }, + "toasts": { + "loadFailed": "Error al cargar agentes: {{message}}", + "deleted": "Agente \"{{name}}\" eliminado", + "deleteFailed": "Error al eliminar agente: {{message}}", + "paused": "Agente \"{{name}}\" pausado", + "resumed": "Agente \"{{name}}\" reanudado", + "pauseFailed": "Error al pausar agente: {{message}}", + "resumeFailed": "Error al reanudar agente: {{message}}", + "exported": "Agente \"{{name}}\" exportado", + "exportFailed": "Error al exportar agente: {{message}}", + "parseFailed": "Error al analizar archivo de agente: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/es/auth.json b/core/http/react-ui/public/locales/es/auth.json new file mode 100644 index 000000000000..c0bdfaef21f2 --- /dev/null +++ b/core/http/react-ui/public/locales/es/auth.json @@ -0,0 +1,112 @@ +{ + "login": { + "subtitle": "Inicia sesión para continuar", + "registerSubtitle": "Crea una cuenta", + "createAdminSubtitle": "Crea tu cuenta de administrador", + "tokenSubtitle": "Introduce tu clave API para continuar", + "email": "Correo electrónico", + "emailPlaceholder": "tu@ejemplo.com", + "name": "Nombre", + "namePlaceholder": "Tu nombre (opcional)", + "password": "Contraseña", + "passwordPlaceholder": "Introduce la contraseña...", + "newPasswordPlaceholder": "Al menos 8 caracteres", + "confirmPassword": "Confirmar contraseña", + "confirmPasswordPlaceholder": "Repite la contraseña", + "inviteCodeLabel": "Código de invitación", + "inviteCodeOptional": " (opcional — evita la espera de aprobación)", + "inviteCodePlaceholder": "Pega tu código de invitación...", + "tokenPlaceholder": "Introduce la clave API...", + "tokenAltPlaceholder": "Introduce el token API...", + "signIn": "Iniciar sesión", + "signingIn": "Iniciando sesión...", + "register": "Registrarse", + "creatingAccount": "Creando cuenta...", + "createAdminAccount": "Crear cuenta de administrador", + "signInWithGitHub": "Iniciar sesión con GitHub", + "signInWithSSO": "Iniciar sesión con SSO", + "loginWithToken": "Iniciar sesión con token", + "showTokenLogin": "Iniciar sesión con token API", + "hideTokenLogin": "Ocultar inicio con token", + "noAccount": "¿No tienes cuenta?", + "hasAccount": "¿Ya tienes cuenta?", + "or": "o", + "errors": { + "loginFailed": "Inicio de sesión fallido", + "registrationFailed": "Registro fallido", + "invalidToken": "Token no válido", + "passwordsDoNotMatch": "Las contraseñas no coinciden", + "enterToken": "Introduce un token", + "networkError": "Error de red", + "inviteRequired": "Se requiere un código de invitación válido para registrarse" + }, + "messages": { + "registrationPending": "Registro exitoso, esperando aprobación." + } + }, + "account": { + "title": "Cuenta", + "subtitle": "Perfil, credenciales y claves API", + "unavailable": "Cuenta no disponible", + "unavailableText": "La autenticación debe estar habilitada para administrar tu cuenta.", + "tabs": { + "profile": "Perfil", + "security": "Seguridad", + "apiKeys": "Claves API" + }, + "profile": { + "displayName": "Nombre para mostrar", + "displayNameDescription": "Tu nombre público", + "avatarUrl": "URL del avatar", + "avatarUrlDescription": "URL de tu imagen de perfil", + "avatarUrlPlaceholder": "https://ejemplo.com/avatar.png", + "save": "Guardar", + "saving": "Guardando...", + "updated": "Perfil actualizado", + "updateFailed": "Error al actualizar el perfil: {{message}}" + }, + "security": { + "currentPassword": "Contraseña actual", + "currentPasswordDescription": "Introduce tu contraseña actual para verificar tu identidad", + "currentPasswordPlaceholder": "Contraseña actual", + "newPassword": "Nueva contraseña", + "newPasswordDescription": "Debe tener al menos 8 caracteres", + "newPasswordPlaceholder": "Nueva contraseña", + "confirmPassword": "Confirmar contraseña", + "confirmPasswordDescription": "Vuelve a introducir tu nueva contraseña", + "confirmPasswordPlaceholder": "Confirmar nueva contraseña", + "changePassword": "Cambiar contraseña", + "changing": "Cambiando...", + "changed": "Contraseña cambiada", + "passwordsDoNotMatch": "Las contraseñas no coinciden", + "tooShort": "La nueva contraseña debe tener al menos 8 caracteres", + "oauthOnly": "La gestión de contraseña no está disponible para cuentas {{provider}}." + }, + "apiKeys": { + "create": "Crear clave API", + "createDescription": "Genera una clave para acceso programático", + "namePlaceholder": "Nombre de la clave (ej. mi-app)", + "createButton": "Crear", + "creating": "Creando...", + "createdToast": "Clave API creada", + "createFailed": "Error al crear clave API: {{message}}", + "loadFailed": "Error al cargar claves API: {{message}}", + "revoke": "Revocar", + "revokeKey": "Revocar clave", + "revokeTitle": "Revocar clave API", + "revokeMessage": "¿Revocar la clave API \"{{name}}\"? Esta acción no se puede deshacer.", + "revoked": "Clave API revocada", + "revokeFailed": "Error al revocar clave API: {{message}}", + "copyNow": "Cópiala ahora — esta clave no se mostrará de nuevo", + "copiedToast": "Copiado al portapapeles", + "copyFailed": "Error al copiar", + "empty": "Aún no hay claves API. Crea una arriba para obtener acceso programático.", + "lastUsed": "último uso {{date}}" + } + }, + "notFound": { + "title": "Página no encontrada", + "text": "Parece que esta página se perdió. Volvamos al buen camino.", + "goHome": "Ir al inicio" + } +} diff --git a/core/http/react-ui/public/locales/es/chat.json b/core/http/react-ui/public/locales/es/chat.json new file mode 100644 index 000000000000..0a57b335c775 --- /dev/null +++ b/core/http/react-ui/public/locales/es/chat.json @@ -0,0 +1,116 @@ +{ + "activity": { + "thought": "Pensamiento", + "tool": "Herramienta", + "result": "Resultado", + "toolResult": "Resultado de {{name}}", + "thinking": "Pensando..." + }, + "header": { + "manageModeTooltip": "Este chat puede instalar modelos, editar configuraciones y administrar backends hablando con LocalAI.", + "modelInfo": "Información del modelo", + "chatSettings": "Ajustes del chat", + "modelInfoTitle": "Información del modelo: {{model}}", + "editConfig": "Editar configuración", + "close": "Cerrar" + }, + "modelInfo": { + "backend": "Backend", + "modelFile": "Archivo del modelo", + "contextSize": "Tamaño del contexto", + "threads": "Hilos", + "mcp": "MCP", + "configured": "Configurado", + "chatTemplate": "Plantilla de chat", + "yes": "Sí", + "gpuLayers": "Capas GPU" + }, + "context": { + "label": "Contexto: {{percent}}%", + "labelWithTokens": "Contexto: {{percent}}% ({{tokens}} tokens)" + }, + "settings": { + "title": "Ajustes del chat", + "manageMode": "Modo administración", + "manageModeDesc": "Permite que este chat instale modelos, cambie backends y edite configuraciones hablando con LocalAI.", + "systemPrompt": "Prompt del sistema", + "systemPromptPlaceholder": "Eres un asistente útil...", + "temperature": "Temperatura", + "topP": "Top P", + "topK": "Top K", + "contextSize": "Tamaño del contexto", + "contextSizePlaceholder": "2048", + "clearHistory": "Borrar historial del chat" + }, + "empty": { + "manageTitle": "Administra LocalAI chateando", + "manageText": "Pide instalar modelos, cambiar backends, editar configuraciones o consultar el estado. El asistente resumirá las acciones y esperará tu confirmación antes de cambiar nada.", + "startTitle": "Inicia una conversación", + "readyText": "Listo para chatear con {{model}}", + "selectModelText": "Selecciona un modelo arriba para comenzar", + "suggestionsManage": [ + "¿Qué está instalado?", + "Instala un modelo de chat", + "Muestra el estado del sistema", + "Actualiza un backend" + ], + "suggestionsChat": [ + "Explica cómo funciona esto", + "Ayúdame a escribir código", + "Resume un documento", + "Lluvia de ideas" + ], + "recent": "Recientes", + "noMessages": "Aún no hay mensajes", + "hintEnter": "Enter para enviar", + "hintShiftEnter": "Shift+Enter para nueva línea", + "hintAttach": "Adjuntar archivos" + }, + "errors": { + "viewTraces": "Ver trazas para más detalles" + }, + "actions": { + "copy": "Copiar", + "regenerate": "Regenerar" + }, + "streaming": { + "transferring": "Transfiriendo modelo...", + "transferringTo": "Transfiriendo modelo a {{node}}..." + }, + "tokens": { + "perSec": "{{count}} tok/s", + "peak": "Pico: {{count}} tok/s", + "usage": "{{prompt}}p + {{completion}}c = {{total}}" + }, + "input": { + "placeholder": "Mensaje...", + "attachFile": "Adjuntar archivo", + "stopGenerating": "Detener generación", + "canvasTitle": "Canvas — extrae bloques de código y multimedia en un panel lateral para vista previa, copia y descarga", + "canvasLabel": "Canvas", + "openCanvas": "Abrir panel canvas" + }, + "deleteAllDialog": { + "title": "Eliminar todos los chats", + "message": "¿Eliminar todos los chats? Esta acción no se puede deshacer.", + "confirm": "Eliminar todo" + }, + "toasts": { + "selectModel": "Por favor selecciona un modelo", + "copied": "Copiado al portapapeles" + }, + "menu": { + "trigger": "Chats", + "triggerTitle": "Conversaciones (Ctrl/Cmd+K)", + "search": "Buscar conversaciones...", + "clearSearch": "Limpiar búsqueda", + "noMatch": "Ninguna conversación coincide con tu búsqueda", + "noConversations": "Aún no hay conversaciones", + "rename": "Renombrar", + "exportMarkdown": "Exportar como Markdown", + "deleteChat": "Eliminar chat", + "newChat": "Nuevo chat", + "clearAll": "Borrar todo", + "deleteAllTitle": "Eliminar todas las conversaciones" + } +} diff --git a/core/http/react-ui/public/locales/es/collections.json b/core/http/react-ui/public/locales/es/collections.json new file mode 100644 index 000000000000..6422067b5f65 --- /dev/null +++ b/core/http/react-ui/public/locales/es/collections.json @@ -0,0 +1,43 @@ +{ + "title": "Base de conocimiento", + "subtitle": "Administra colecciones de documentos para el RAG de los agentes", + "newPlaceholder": "Nombre de la nueva colección...", + "actions": { + "create": "Crear", + "creating": "Creando...", + "details": "Detalles", + "reset": "Restablecer", + "delete": "Eliminar", + "viewDetails": "Ver detalles", + "resetCollection": "Restablecer colección", + "deleteCollection": "Eliminar colección" + }, + "sections": { + "yourCollections": "Tus colecciones", + "otherUsersCollections": "Colecciones de otros usuarios" + }, + "empty": { + "title": "Aún no hay colecciones", + "text": "Las colecciones te permiten organizar documentos en bases de conocimiento que los agentes pueden buscar usando RAG (Retrieval-Augmented Generation). Crea una colección arriba para empezar.", + "noPersonal": "Aún no tienes colecciones." + }, + "deleteDialog": { + "title": "Eliminar colección", + "message": "¿Eliminar la colección \"{{name}}\"? Esto eliminará todas las entradas y no se puede deshacer.", + "confirm": "Eliminar" + }, + "resetDialog": { + "title": "Restablecer colección", + "message": "¿Restablecer la colección \"{{name}}\"? Esto eliminará todas las entradas pero mantendrá la colección.", + "confirm": "Restablecer" + }, + "toasts": { + "loadFailed": "Error al cargar colecciones: {{message}}", + "created": "Colección \"{{name}}\" creada", + "createFailed": "Error al crear colección: {{message}}", + "deleted": "Colección \"{{name}}\" eliminada", + "deleteFailed": "Error al eliminar colección: {{message}}", + "reset": "Colección \"{{name}}\" restablecida", + "resetFailed": "Error al restablecer colección: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/es/common.json b/core/http/react-ui/public/locales/es/common.json new file mode 100644 index 000000000000..97d8fd62ff77 --- /dev/null +++ b/core/http/react-ui/public/locales/es/common.json @@ -0,0 +1,109 @@ +{ + "actions": { + "save": "Guardar", + "saving": "Guardando...", + "cancel": "Cancelar", + "close": "Cerrar", + "confirm": "Confirmar", + "delete": "Eliminar", + "edit": "Editar", + "add": "Añadir", + "remove": "Quitar", + "create": "Crear", + "update": "Actualizar", + "refresh": "Refrescar", + "reload": "Recargar", + "retry": "Reintentar", + "search": "Buscar", + "filter": "Filtrar", + "clear": "Limpiar", + "reset": "Restablecer", + "apply": "Aplicar", + "back": "Atrás", + "next": "Siguiente", + "previous": "Anterior", + "open": "Abrir", + "submit": "Enviar", + "select": "Seleccionar", + "selectAll": "Seleccionar todo", + "copy": "Copiar", + "copied": "Copiado", + "download": "Descargar", + "upload": "Subir", + "import": "Importar", + "export": "Exportar", + "view": "Ver", + "details": "Detalles", + "settings": "Configuración", + "help": "Ayuda", + "yes": "Sí", + "no": "No", + "loading": "Cargando..." + }, + "status": { + "loading": "Cargando...", + "saving": "Guardando...", + "saved": "Guardado", + "ready": "Listo", + "running": "En ejecución", + "stopped": "Detenido", + "starting": "Iniciando...", + "stopping": "Deteniendo...", + "pending": "Pendiente", + "active": "Activo", + "inactive": "Inactivo", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "online": "En línea", + "offline": "Desconectado", + "error": "Error", + "success": "Éxito", + "warning": "Advertencia", + "info": "Info", + "empty": "Sin elementos", + "none": "Ninguno", + "unknown": "Desconocido" + }, + "dialogs": { + "confirmDelete": { + "title": "Confirmar eliminación", + "message": "¿Seguro que quieres eliminarlo? Esta acción no se puede deshacer.", + "confirm": "Eliminar", + "cancel": "Cancelar" + }, + "unsavedChanges": { + "title": "Cambios sin guardar", + "message": "Tienes cambios sin guardar. ¿Quieres descartarlos?", + "discard": "Descartar", + "keepEditing": "Seguir editando" + } + }, + "forms": { + "required": "Obligatorio", + "optional": "Opcional", + "name": "Nombre", + "description": "Descripción", + "type": "Tipo", + "value": "Valor", + "search": "Buscar...", + "selectPlaceholder": "Selecciona una opción..." + }, + "time": { + "now": "ahora", + "secondsAgo_one": "hace {{count}} segundo", + "secondsAgo_other": "hace {{count}} segundos", + "minutesAgo_one": "hace {{count}} minuto", + "minutesAgo_other": "hace {{count}} minutos", + "hoursAgo_one": "hace {{count}} hora", + "hoursAgo_other": "hace {{count}} horas", + "daysAgo_one": "hace {{count}} día", + "daysAgo_other": "hace {{count}} días" + }, + "units": { + "bytes": "B", + "kilobytes": "KB", + "megabytes": "MB", + "gigabytes": "GB", + "terabytes": "TB" + } +} diff --git a/core/http/react-ui/public/locales/es/errors.json b/core/http/react-ui/public/locales/es/errors.json new file mode 100644 index 000000000000..7925108a3605 --- /dev/null +++ b/core/http/react-ui/public/locales/es/errors.json @@ -0,0 +1,17 @@ +{ + "generic": "Algo salió mal", + "network": "Error de red. Comprueba tu conexión e inténtalo de nuevo.", + "unauthorized": "No estás autorizado para realizar esta acción.", + "forbidden": "Acceso denegado.", + "notFound": "El recurso solicitado no se encontró.", + "serverError": "Error del servidor. Inténtalo de nuevo más tarde.", + "loadFailed": "Error al cargar: {{message}}", + "saveFailed": "Error al guardar: {{message}}", + "deleteFailed": "Error al eliminar: {{message}}", + "updateFailed": "Error al actualizar: {{message}}", + "createFailed": "Error al crear: {{message}}", + "operationFailed": "Operación fallida: {{message}}", + "invalidInput": "Entrada no válida. Revisa el formulario e inténtalo de nuevo.", + "tryAgain": "Por favor, inténtalo de nuevo.", + "contactAdmin": "Si el problema persiste, contacta con tu administrador." +} diff --git a/core/http/react-ui/public/locales/es/home.json b/core/http/react-ui/public/locales/es/home.json new file mode 100644 index 000000000000..ec71d8db2d82 --- /dev/null +++ b/core/http/react-ui/public/locales/es/home.json @@ -0,0 +1,66 @@ +{ + "cluster": { + "vram": "VRAM del clúster", + "ram": "RAM del clúster", + "nodesOnline": "{{healthy}}/{{total}} nodos en línea" + }, + "resourceGpu": "GPU", + "resourceRam": "RAM", + "assistant": { + "title": "Administra LocalAI chateando", + "description": "Instala modelos, cambia backends, edita configuraciones y consulta el estado hablando con LocalAI.", + "open": "Abrir asistente", + "tooltip": "Administra LocalAI chateando" + }, + "input": { + "placeholder": "Mensaje...", + "attachImage": "Adjuntar imagen", + "attachAudio": "Adjuntar audio", + "attachFile": "Adjuntar archivo", + "enterToSend": "Enter para enviar", + "selectModelFirst": "Selecciona un modelo primero", + "sendMessage": "Enviar mensaje", + "selectModelToast": "Por favor selecciona un modelo primero" + }, + "quickLinks": { + "manageByChat": "Administrar por chat", + "installedModels": "Modelos instalados", + "browseGallery": "Explorar galería", + "importModel": "Importar modelo", + "documentation": "Documentación" + }, + "loadedModels": { + "count_one": "{{count}} modelo cargado", + "count_other": "{{count}} modelos cargados", + "stop": "Detener modelo", + "stopAll": "Detener todos" + }, + "stopDialog": { + "title": "Detener modelo", + "message": "¿Detener el modelo {{model}}?", + "confirm": "Detener {{model}}", + "stopAllTitle": "Detener todos los modelos", + "stopAllMessage": "¿Detener todos los {{count}} modelos cargados?", + "stopAllConfirm": "Detener todos", + "stoppedToast": "{{model}} detenido", + "allStoppedToast": "Todos los modelos detenidos", + "stopFailed": "Error al detener: {{message}}" + }, + "wizard": { + "getStarted": "Empieza con {{name}}", + "intro": "Instala tu primer modelo para comenzar. Explora la galería o importa el tuyo.", + "steps": { + "step1Title": "Explora la galería de modelos", + "step1Body": "Encuentra el modelo adecuado para tus necesidades en nuestra colección.", + "step2Title": "Instala un modelo", + "step2Body": "Haz clic en instalar para descargarlo y configurarlo automáticamente.", + "step3Title": "Empieza a chatear", + "step3Body": "Chatea con tu modelo desde el navegador o usa la API." + }, + "browseGallery": "Explorar galería de modelos", + "importModel": "Importar modelo", + "docs": "Documentación", + "noModelsTitle": "No hay modelos disponibles", + "noModelsBody": "No hay modelos instalados aún. Pídele a tu administrador que configure modelos para que puedas empezar a chatear." + } +} diff --git a/core/http/react-ui/public/locales/es/importModel.json b/core/http/react-ui/public/locales/es/importModel.json new file mode 100644 index 000000000000..96e447a647d7 --- /dev/null +++ b/core/http/react-ui/public/locales/es/importModel.json @@ -0,0 +1,142 @@ +{ + "title": "Importar nuevo modelo", + "subtitle": { + "simple": "Importa un modelo desde una URI — la auto-detección elige el backend.", + "powerYaml": "Escribe la configuración YAML completa del modelo.", + "powerPrefs": "Preferencias de importación detalladas." + }, + "actions": { + "import": "Importar modelo", + "importing": "Importando...", + "create": "Crear", + "saving": "Guardando...", + "browseHF": "Explorar modelos en HF", + "addCustom": "Añadir personalizado", + "copy": "Copiar" + }, + "form": { + "modelUri": "URI del modelo", + "uriPlaceholder": "huggingface://TheBloke/Llama-2-7B-Chat-GGUF o https://example.com/model.gguf", + "uriHint": "Introduce la URI o ruta del archivo de modelo a importar", + "supportedFormats": "Formatos URI soportados", + "options": "Opciones", + "preferences": "Preferencias (opcionales)", + "commonPreferences": "Preferencias comunes", + "customPreferences": "Preferencias personalizadas", + "customKeyValueHint": "Añade pares clave-valor personalizados para configuración avanzada.", + "preferenceKey": "Clave de preferencia para la fila {{index}}", + "preferenceValue": "Valor de preferencia para la fila {{index}}", + "removePref": "Eliminar esta preferencia", + "key": "Clave", + "value": "Valor", + "backend": "Backend", + "backendAuto": "Auto-detectar (basado en la URI)", + "backendLoading": "Cargando backends…", + "backendSearch": "Buscar backends...", + "backendHint": "Forzar un backend específico. Déjalo vacío para auto-detectar desde la URI. Los marcados con \"selección manual\" no son auto-detectables — selecciónalos tú si sabes lo que necesita el modelo.", + "backendErrorHint": "No se pudo cargar la lista de backends — solo auto-detect.", + "backendNotInstalled": "Este backend aún no está instalado. Enviar la importación lo descargará primero.", + "modelName": "Nombre del modelo", + "modelNamePlaceholder": "Déjalo vacío para usar el nombre del archivo", + "modelNameHint": "Nombre personalizado para el modelo. Si está vacío, se usará el nombre del archivo.", + "description": "Descripción", + "descriptionPlaceholder": "Déjalo vacío para usar la descripción predeterminada", + "descriptionHint": "Descripción personalizada para el modelo.", + "quantizations": "Cuantizaciones", + "quantizationsPlaceholder": "q4_k_m,q4_k_s,q3_k_m (separadas por comas)", + "quantizationsHint": "Cuantizaciones preferidas (separadas por comas). Déjalo vacío para el predeterminado (q4_k_m).", + "mmprojQuantizations": "Cuantizaciones MMProj", + "mmprojQuantizationsPlaceholder": "fp16,fp32 (separadas por comas)", + "mmprojQuantizationsHint": "Cuantizaciones MMProj preferidas. Déjalo vacío para el predeterminado (fp16).", + "embeddings": "Embeddings", + "embeddingsHint": "Habilita el soporte de embeddings para este modelo.", + "modelType": "Tipo de modelo", + "modelTypePlaceholder": "AutoModelForCausalLM (para el backend transformers)", + "modelTypeHint": "Tipo de modelo para el backend transformers. Ejemplos: AutoModelForCausalLM, SentenceTransformer, Mamba.", + "pipelineType": "Tipo de pipeline", + "pipelineTypeHint": "Tipo de pipeline para el backend diffusers.", + "schedulerType": "Tipo de scheduler", + "schedulerTypePlaceholder": "k_dpmpp_2m (opcional)", + "schedulerTypeHint": "Tipo de scheduler para el backend diffusers. Ejemplos: k_dpmpp_2m, euler_a, ddim.", + "enableParameters": "Parámetros habilitados", + "enableParametersPlaceholder": "negative_prompt,num_inference_steps (separados por comas)", + "enableParametersHint": "Parámetros habilitados para el backend diffusers (separados por comas).", + "cuda": "CUDA", + "cudaHint": "Habilita el soporte CUDA para aceleración GPU.", + "yamlEditor": "Editor de configuración YAML", + "manualPick": "selección manual", + "manualPickTooltip": "El auto-detect no enrutará a este backend. Selecciónalo aquí si sabes que es lo que quieres." + }, + "modality": { + "text": "LLM de texto", + "asr": "Reconocimiento de voz", + "tts": "Texto a voz", + "image": "Imagen / Video", + "embeddings": "Embeddings", + "reranker": "Rerankers", + "detection": "Detección de objetos", + "vad": "Detección de actividad de voz", + "other": "Otro" + }, + "powerTabs": { + "ariaLabel": "Pestaña de modo avanzado", + "preferences": "Preferencias", + "yaml": "YAML" + }, + "switchDialog": { + "title": "¿Mantener tus preferencias personalizadas?", + "body": "Cambiar al modo Simple oculta las preferencias más allá del backend, nombre y descripción. Se enviarán de todos modos al importar.", + "cancel": "Cancelar", + "discard": "Descartar y cambiar", + "keep": "Mantener y cambiar" + }, + "estimate": { + "title": "Requisitos estimados", + "download": "Descarga: {{size}}", + "vram": "VRAM: {{vram}}" + }, + "toasts": { + "noUri": "Por favor introduce una URI de modelo", + "noYaml": "Por favor introduce la configuración YAML", + "started": "¡Importación iniciada! Siguiendo el progreso...", + "startedWithMeta": "¡Importación iniciada! Siguiendo el progreso... ({{meta}})", + "imported": "¡Modelo importado con éxito!", + "importedYaml": "¡Configuración del modelo importada con éxito!", + "importFailed": "Importación fallida: {{message}}", + "startImportFailed": "Error al iniciar importación: {{message}}", + "backendsLoadFailed": "No se pudo cargar la lista de backends — usando solo auto-detect", + "modalityClearedBackend": "Selección de backend limpiada — no estaba en el grupo {{label}}.", + "copied": "Copiado al portapapeles" + }, + "uriFormats": { + "huggingface": { + "title": "HuggingFace", + "standard": "Formato HuggingFace estándar", + "short": "Formato HuggingFace corto", + "fullUrl": "URL HuggingFace completa" + }, + "http": { + "title": "URLs HTTP/HTTPS", + "direct": "Descarga directa desde cualquier URL HTTPS" + }, + "local": { + "title": "Archivos locales", + "filePath": "Ruta de archivo local (absoluta)", + "directYaml": "Archivo de configuración YAML local directo" + }, + "oci": { + "title": "Registro OCI", + "registry": "Registro de contenedores OCI", + "tarball": "Archivo tarball OCI local" + }, + "ollama": { + "title": "Ollama", + "model": "Formato de modelo Ollama" + }, + "yaml": { + "title": "Archivos de configuración YAML", + "remote": "Archivo de configuración YAML remoto", + "local": "Archivo de configuración YAML local" + } + } +} diff --git a/core/http/react-ui/public/locales/es/media.json b/core/http/react-ui/public/locales/es/media.json new file mode 100644 index 000000000000..b3f25695a1ed --- /dev/null +++ b/core/http/react-ui/public/locales/es/media.json @@ -0,0 +1,154 @@ +{ + "studio": { + "tabs": { + "images": "Imágenes", + "video": "Video", + "tts": "TTS", + "sound": "Sonido" + } + }, + "image": { + "title": "Generación de imágenes", + "labels": { + "model": "Modelo", + "prompt": "Prompt", + "promptPlaceholder": "Describe la imagen que quieres generar...", + "negativePrompt": "Prompt negativo", + "negativePromptPlaceholder": "Qué evitar...", + "size": "Tamaño", + "count": "Cantidad (1-4)", + "advanced": "Configuración avanzada", + "imageInputs": "Entradas de imagen", + "steps": "Pasos", + "stepsPlaceholder": "20", + "seed": "Seed", + "seedPlaceholder": "Aleatorio", + "sourceImage": "Imagen fuente (img2img)", + "refImages": "Imágenes de referencia", + "refImagesAdded_one": "{{count}} imagen añadida", + "refImagesAdded_other": "{{count}} imágenes añadidas" + }, + "actions": { + "generate": "Generar", + "generating": "Generando..." + }, + "empty": "Las imágenes generadas aparecerán aquí", + "toasts": { + "noPrompt": "Por favor introduce un prompt", + "noModel": "Por favor selecciona un modelo", + "noResults": "No se generaron imágenes" + } + }, + "video": { + "title": "Generación de video", + "labels": { + "model": "Modelo", + "prompt": "Prompt", + "promptPlaceholder": "Describe el video que quieres generar...", + "duration": "Duración (s)", + "fps": "FPS", + "size": "Tamaño", + "advanced": "Configuración avanzada", + "seed": "Seed", + "seedPlaceholder": "Aleatorio" + }, + "actions": { + "generate": "Generar video", + "generating": "Generando..." + }, + "empty": "Los videos generados aparecerán aquí", + "toasts": { + "noPrompt": "Por favor introduce un prompt", + "noModel": "Por favor selecciona un modelo", + "noResults": "No se generaron videos" + } + }, + "tts": { + "title": "Texto a voz", + "labels": { + "model": "Modelo", + "voice": "Voz", + "voicePlaceholder": "ID de voz opcional", + "input": "Texto", + "inputPlaceholder": "Introduce el texto a sintetizar..." + }, + "actions": { + "generate": "Generar audio", + "generating": "Generando..." + }, + "empty": "El audio generado aparecerá aquí", + "toasts": { + "noText": "Por favor introduce texto", + "noModel": "Por favor selecciona un modelo", + "generateFailed": "Generación fallida" + } + }, + "sound": { + "title": "Generación de sonido", + "labels": { + "model": "Modelo", + "prompt": "Prompt", + "promptPlaceholder": "Describe el sonido que quieres generar...", + "duration": "Duración (s)", + "language": "Idioma", + "vocalLanguage": "Idioma vocal", + "lyrics": "Letras (opcional)", + "lyricsPlaceholder": "Letras para generación vocal", + "advanced": "Configuración avanzada", + "seed": "Seed", + "seedPlaceholder": "Aleatorio" + }, + "actions": { + "generate": "Generar", + "generating": "Generando..." + }, + "empty": "El audio generado aparecerá aquí", + "toasts": { + "noPrompt": "Por favor introduce un prompt", + "noModel": "Por favor selecciona un modelo", + "generateFailed": "Generación fallida" + } + }, + "talk": { + "title": "Hablar", + "subtitle": "Conversación de voz en tiempo real", + "actions": { + "start": "Iniciar sesión", + "stop": "Terminar sesión", + "connecting": "Conectando...", + "muted": "Silenciado", + "mute": "Silenciar", + "unmute": "Activar sonido" + }, + "labels": { + "model": "Modelo", + "voice": "Voz", + "voicePlaceholder": "alloy", + "language": "Idioma", + "languagePlaceholder": "es", + "instructions": "Instrucciones", + "instructionsPlaceholder": "Define la personalidad del asistente..." + }, + "status": { + "idle": "Inactivo", + "connecting": "Conectando...", + "listening": "Escuchando...", + "speaking": "Hablando...", + "ended": "Sesión finalizada" + }, + "toasts": { + "noModel": "Selecciona un modelo primero", + "connectFailed": "Error al conectar: {{message}}" + } + }, + "history": { + "title": "Historial", + "empty": "Sin historial aún", + "deleteEntry": "Eliminar entrada", + "clear": "Borrar historial", + "clearTitle": "Borrar todo el historial", + "clearMessage": "¿Eliminar todas las entradas del historial? Esto no se puede deshacer.", + "clearConfirm": "Borrar", + "cleared": "Historial borrado" + } +} diff --git a/core/http/react-ui/public/locales/es/models.json b/core/http/react-ui/public/locales/es/models.json new file mode 100644 index 000000000000..4f21effb0092 --- /dev/null +++ b/core/http/react-ui/public/locales/es/models.json @@ -0,0 +1,85 @@ +{ + "title": "Instalar modelos", + "subtitle": "Explora e instala modelos de IA desde la galería", + "stats": { + "available": "Disponibles", + "installed": "Instalados" + }, + "actions": { + "addModel": "Añadir modelo", + "importModel": "Importar modelo", + "install": "Instalar", + "reinstall": "Reinstalar", + "delete": "Eliminar" + }, + "filters": { + "all": "Todos", + "llm": "LLM", + "image": "Imagen", + "multimodal": "Multimodal", + "vision": "Visión", + "tts": "TTS", + "stt": "STT", + "embedding": "Embedding", + "rerank": "Rerank", + "allBackends": "Todos los backends", + "searchBackends": "Buscar backends..." + }, + "search": { + "placeholder": "Buscar modelos...", + "clearFilters": "Limpiar filtros" + }, + "table": { + "modelName": "Nombre del modelo", + "description": "Descripción", + "backend": "Backend", + "sizeVram": "Tamaño / VRAM", + "status": "Estado", + "actions": "Acciones", + "size": "Tamaño: {{size}}", + "vram": "VRAM: {{vram}}", + "fits": "Cabe", + "mayNotFit": "Puede no caber", + "trustRemoteCode": "Trust Remote Code", + "installing": "Instalando", + "installingPct": "Instalando · {{percent}}%", + "installed": "Instalado", + "notInstalled": "No instalado" + }, + "detail": { + "description": "Descripción", + "gallery": "Galería", + "backend": "Backend", + "size": "Tamaño", + "vram": "VRAM", + "license": "Licencia", + "tags": "Etiquetas", + "links": "Enlaces", + "warning": "Advertencia", + "files": "Archivos", + "fitsGpu": "Cabe en la GPU", + "mayNotFitGpu": "Puede no caber en la GPU", + "requiresTrustRemoteCode": "Requiere Trust Remote Code", + "fileCount_one": "{{count}} archivo", + "fileCount_other": "{{count}} archivos", + "filename": "Nombre del archivo", + "uri": "URI", + "sha256": "SHA256" + }, + "empty": { + "title": "No se encontraron modelos", + "withFilters": "Ningún modelo coincide con la búsqueda o filtros actuales.", + "noFilters": "La galería de modelos está vacía." + }, + "deleteDialog": { + "title": "Eliminar modelo", + "message": "¿Eliminar el modelo {{model}}?", + "confirm": "Eliminar {{model}}", + "deletingToast": "Eliminando {{model}}..." + }, + "errors": { + "loadFailed": "Error al cargar modelos: {{message}}", + "installFailed": "Error al instalar: {{message}}", + "deleteFailed": "Error al eliminar: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/es/nav.json b/core/http/react-ui/public/locales/es/nav.json new file mode 100644 index 000000000000..43323f6b99e8 --- /dev/null +++ b/core/http/react-ui/public/locales/es/nav.json @@ -0,0 +1,51 @@ +{ + "appName": "LocalAI", + "openMenu": "Abrir menú", + "closeMenu": "Cerrar menú", + "primaryNavigation": "Navegación principal", + "switchToLightMode": "Cambiar a tema claro", + "switchToDarkMode": "Cambiar a tema oscuro", + "expandSidebar": "Expandir barra lateral", + "collapseSidebar": "Contraer barra lateral", + "changeLanguage": "Cambiar idioma", + "logout": "Cerrar sesión", + "accountSettings": "Configuración de la cuenta", + "account": "Cuenta", + "accountFor": "Cuenta: {{name}}", + "sections": { + "tools": "Herramientas", + "biometrics": "Biometría", + "agents": "Agentes", + "system": "Sistema" + }, + "items": { + "home": "Inicio", + "installModels": "Instalar modelos", + "chat": "Chat", + "studio": "Studio", + "talk": "Hablar", + "fineTune": "Ajuste fino (Experimental)", + "quantize": "Cuantización (Experimental)", + "faceRecognition": "Reconocimiento facial", + "voiceRecognition": "Reconocimiento de voz", + "agents": "Agentes", + "skills": "Habilidades", + "memory": "Memoria", + "mcpJobs": "Trabajos MCP CI", + "usage": "Uso", + "users": "Usuarios", + "backends": "Backends", + "traces": "Trazas", + "nodes": "Nodos", + "swarm": "Swarm", + "system": "Sistema", + "settings": "Configuración", + "api": "API" + }, + "footer": { + "github": "GitHub", + "documentation": "Documentación", + "author": "Autor", + "copyright": "© 2023-{{year}} {{author}}" + } +} diff --git a/core/http/react-ui/public/locales/es/skills.json b/core/http/react-ui/public/locales/es/skills.json new file mode 100644 index 000000000000..07e0873ae0d5 --- /dev/null +++ b/core/http/react-ui/public/locales/es/skills.json @@ -0,0 +1,79 @@ +{ + "title": "Habilidades", + "subtitle": "Administra las habilidades de los agentes (instrucciones y recursos reutilizables)", + "unavailable": { + "subtitle": "El servicio de habilidades no está disponible o el índice se está reconstruyendo. Inténtalo de nuevo en un momento.", + "retry": "Reintentar" + }, + "actions": { + "newSkill": "Nueva habilidad", + "createSkill": "Crear habilidad", + "import": "Importar", + "importing": "Importando...", + "gitRepos": "Repos Git", + "edit": "Editar", + "delete": "Eliminar", + "export": "Exportar", + "sync": "Sincronizar", + "addRepo": "Añadir repo", + "adding": "Añadiendo...", + "remove": "Quitar", + "enable": "Habilitar", + "disable": "Deshabilitar" + }, + "search": { + "placeholder": "Buscar habilidades..." + }, + "git": { + "title": "Repositorios Git", + "description": "Añade repositorios Git de los que obtener habilidades. Las habilidades aparecerán en la lista después de sincronizar.", + "urlPlaceholder": "https://github.com/user/repo o git@github.com:user/repo.git", + "noRepos": "No hay repos Git configurados. Añade uno arriba.", + "disabled": "Deshabilitado", + "removeRepo": "Quitar repo" + }, + "card": { + "noDescription": "Sin descripción", + "readOnly": "Solo lectura", + "editTitle": "Editar habilidad", + "deleteTitle": "Eliminar habilidad", + "exportTitle": "Exportar como .tar.gz" + }, + "empty": { + "title": "No se encontraron habilidades", + "text": "Crea una habilidad o importa una para empezar.", + "noPersonal": "Aún no tienes habilidades." + }, + "sections": { + "yourSkills": "Tus habilidades", + "otherUsersSkills": "Habilidades de otros usuarios" + }, + "deleteDialog": { + "title": "Eliminar habilidad", + "message": "¿Eliminar la habilidad \"{{name}}\"? Esta acción no se puede deshacer.", + "confirm": "Eliminar" + }, + "removeRepoDialog": { + "title": "Quitar repositorio Git", + "message": "¿Quitar este repositorio Git? Las habilidades de él ya no estarán disponibles.", + "confirm": "Quitar" + }, + "toasts": { + "loadFailed": "Error al cargar habilidades", + "deleted": "Habilidad \"{{name}}\" eliminada", + "deleteFailed": "Error al eliminar habilidad", + "exported": "Habilidad \"{{name}}\" exportada", + "exportFailed": "Error al exportar", + "imported": "Habilidad importada desde \"{{file}}\"", + "importFailed": "Error al importar", + "loadReposFailed": "Error al cargar repos Git", + "repoAdded": "Repo Git añadido y sincronizando", + "addRepoFailed": "Error al añadir repo", + "synced": "Repo sincronizado", + "syncFailed": "Error al sincronizar", + "toggled": "Repo actualizado", + "toggleFailed": "Error al actualizar", + "removed": "Repo eliminado", + "removeFailed": "Error al eliminar" + } +} diff --git a/core/http/react-ui/public/locales/it/admin.json b/core/http/react-ui/public/locales/it/admin.json new file mode 100644 index 000000000000..2bd575b661be --- /dev/null +++ b/core/http/react-ui/public/locales/it/admin.json @@ -0,0 +1,62 @@ +{ + "manage": { + "title": "Sistema", + "subtitle": "Gestisci modelli e backend installati" + }, + "settings": { + "title": "Impostazioni", + "subtitle": "Configura le impostazioni runtime di LocalAI", + "saved": "Impostazioni salvate con successo", + "saveFailed": "Salvataggio fallito: {{message}}", + "loadFailed": "Caricamento impostazioni fallito: {{message}}", + "sections": { + "branding": "Branding", + "watchdog": "Watchdog", + "memory": "Memoria", + "backends": "Backend", + "performance": "Prestazioni", + "tracing": "Tracing", + "api": "API e CORS", + "p2p": "P2P", + "galleries": "Gallerie", + "apikeys": "Chiavi API", + "agents": "Job degli agenti", + "agentpool": "Pool agenti", + "assistant": "LocalAI Assistant", + "responses": "Risposte" + } + }, + "backends": { + "title": "Gestione backend", + "subtitle": "Scopri e installa backend AI per i tuoi modelli" + }, + "backendLogs": { + "title": "Log dei backend", + "subtitle": "Visualizza i log dei backend in esecuzione", + "empty": "Nessun log disponibile" + }, + "traces": { + "title": "Tracce", + "subtitle": "Visualizza richieste API, risposte e operazioni dei backend registrate" + }, + "nodes": { + "title": "Nodi distribuiti", + "subtitle": "Gestisci i nodi worker dei backend e degli agenti" + }, + "p2p": { + "title": "Calcolo AI distribuito", + "subtitle": "Scala i tuoi carichi di lavoro AI su più dispositivi con la distribuzione peer-to-peer" + }, + "users": { + "title": "Utenti", + "subtitle": "Gestisci utenti registrati, ruoli e inviti" + }, + "usage": { + "title": "Utilizzo", + "subtitle": "Statistiche di utilizzo dei token API" + }, + "explorer": { + "title": "Esplora risorse", + "subtitle": "Sfoglia file e configurazioni" + } +} diff --git a/core/http/react-ui/public/locales/it/agents.json b/core/http/react-ui/public/locales/it/agents.json new file mode 100644 index 000000000000..e42120109259 --- /dev/null +++ b/core/http/react-ui/public/locales/it/agents.json @@ -0,0 +1,55 @@ +{ + "title": "Agenti", + "subtitle": "Gestisci agenti AI autonomi", + "actions": { + "agentHub": "Agent Hub", + "import": "Importa", + "createAgent": "Crea agente", + "edit": "Modifica", + "chat": "Chat", + "export": "Esporta", + "delete": "Elimina", + "pause": "Pausa", + "resume": "Riprendi" + }, + "table": { + "name": "Nome", + "status": "Stato", + "events": "Eventi", + "actions": "Azioni", + "eventsTooltip": "{{count}} eventi - Clicca per visualizzare" + }, + "search": { + "placeholder": "Cerca agenti...", + "summary_one": "{{shown}} di {{total}} agente", + "summary_other": "{{shown}} di {{total}} agenti" + }, + "empty": { + "noConfigured": "Nessun agente configurato", + "noConfiguredText": "Crea un agente per iniziare con i flussi di lavoro AI autonomi.", + "browseHub": "Non sai da dove iniziare? Sfoglia l'<1>Agent Hub per trovare configurazioni di agenti pronte da importare.", + "noMatching": "Nessun agente corrispondente", + "noMatchingText": "Nessun agente corrisponde a \"{{query}}\"" + }, + "sections": { + "yourAgents": "I tuoi agenti", + "otherUsersAgents": "Agenti di altri utenti" + }, + "deleteDialog": { + "title": "Elimina agente", + "message": "Eliminare l'agente \"{{name}}\"? Questa azione non può essere annullata.", + "confirm": "Elimina" + }, + "toasts": { + "loadFailed": "Caricamento agenti fallito: {{message}}", + "deleted": "Agente \"{{name}}\" eliminato", + "deleteFailed": "Eliminazione agente fallita: {{message}}", + "paused": "Agente \"{{name}}\" in pausa", + "resumed": "Agente \"{{name}}\" ripreso", + "pauseFailed": "Pausa agente fallita: {{message}}", + "resumeFailed": "Ripresa agente fallita: {{message}}", + "exported": "Agente \"{{name}}\" esportato", + "exportFailed": "Esportazione agente fallita: {{message}}", + "parseFailed": "Analisi del file agente fallita: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/it/auth.json b/core/http/react-ui/public/locales/it/auth.json new file mode 100644 index 000000000000..38a606e79bcb --- /dev/null +++ b/core/http/react-ui/public/locales/it/auth.json @@ -0,0 +1,112 @@ +{ + "login": { + "subtitle": "Accedi per continuare", + "registerSubtitle": "Crea un account", + "createAdminSubtitle": "Crea il tuo account amministratore", + "tokenSubtitle": "Inserisci la tua chiave API per continuare", + "email": "Email", + "emailPlaceholder": "tu@esempio.com", + "name": "Nome", + "namePlaceholder": "Il tuo nome (opzionale)", + "password": "Password", + "passwordPlaceholder": "Inserisci la password...", + "newPasswordPlaceholder": "Almeno 8 caratteri", + "confirmPassword": "Conferma password", + "confirmPasswordPlaceholder": "Ripeti la password", + "inviteCodeLabel": "Codice invito", + "inviteCodeOptional": " (opzionale — salta l'attesa di approvazione)", + "inviteCodePlaceholder": "Incolla il codice invito...", + "tokenPlaceholder": "Inserisci la chiave API...", + "tokenAltPlaceholder": "Inserisci il token API...", + "signIn": "Accedi", + "signingIn": "Accesso in corso...", + "register": "Registrati", + "creatingAccount": "Creazione account...", + "createAdminAccount": "Crea account amministratore", + "signInWithGitHub": "Accedi con GitHub", + "signInWithSSO": "Accedi con SSO", + "loginWithToken": "Accedi con token", + "showTokenLogin": "Accedi con token API", + "hideTokenLogin": "Nascondi accesso con token", + "noAccount": "Non hai un account?", + "hasAccount": "Hai già un account?", + "or": "oppure", + "errors": { + "loginFailed": "Accesso non riuscito", + "registrationFailed": "Registrazione non riuscita", + "invalidToken": "Token non valido", + "passwordsDoNotMatch": "Le password non coincidono", + "enterToken": "Inserisci un token", + "networkError": "Errore di rete", + "inviteRequired": "È richiesto un codice invito valido per registrarsi" + }, + "messages": { + "registrationPending": "Registrazione completata, in attesa di approvazione." + } + }, + "account": { + "title": "Account", + "subtitle": "Profilo, credenziali e chiavi API", + "unavailable": "Account non disponibile", + "unavailableText": "L'autenticazione deve essere abilitata per gestire l'account.", + "tabs": { + "profile": "Profilo", + "security": "Sicurezza", + "apiKeys": "Chiavi API" + }, + "profile": { + "displayName": "Nome visualizzato", + "displayNameDescription": "Il tuo nome visualizzato pubblicamente", + "avatarUrl": "URL avatar", + "avatarUrlDescription": "URL della tua immagine del profilo", + "avatarUrlPlaceholder": "https://esempio.com/avatar.png", + "save": "Salva", + "saving": "Salvataggio...", + "updated": "Profilo aggiornato", + "updateFailed": "Aggiornamento profilo non riuscito: {{message}}" + }, + "security": { + "currentPassword": "Password attuale", + "currentPasswordDescription": "Inserisci la tua password attuale per verificare la tua identità", + "currentPasswordPlaceholder": "Password attuale", + "newPassword": "Nuova password", + "newPasswordDescription": "Deve avere almeno 8 caratteri", + "newPasswordPlaceholder": "Nuova password", + "confirmPassword": "Conferma password", + "confirmPasswordDescription": "Reinserisci la nuova password", + "confirmPasswordPlaceholder": "Conferma la nuova password", + "changePassword": "Cambia password", + "changing": "Modifica in corso...", + "changed": "Password modificata", + "passwordsDoNotMatch": "Le password non coincidono", + "tooShort": "La nuova password deve avere almeno 8 caratteri", + "oauthOnly": "La gestione della password non è disponibile per gli account {{provider}}." + }, + "apiKeys": { + "create": "Crea chiave API", + "createDescription": "Genera una chiave per l'accesso programmatico", + "namePlaceholder": "Nome chiave (es. mia-app)", + "createButton": "Crea", + "creating": "Creazione...", + "createdToast": "Chiave API creata", + "createFailed": "Creazione chiave API non riuscita: {{message}}", + "loadFailed": "Caricamento chiavi API non riuscito: {{message}}", + "revoke": "Revoca", + "revokeKey": "Revoca chiave", + "revokeTitle": "Revoca chiave API", + "revokeMessage": "Revocare la chiave API \"{{name}}\"? Questa azione non può essere annullata.", + "revoked": "Chiave API revocata", + "revokeFailed": "Revoca chiave API non riuscita: {{message}}", + "copyNow": "Copia ora — questa chiave non sarà più mostrata", + "copiedToast": "Copiato negli appunti", + "copyFailed": "Copia non riuscita", + "empty": "Nessuna chiave API. Creane una sopra per ottenere l'accesso programmatico.", + "lastUsed": "ultimo utilizzo {{date}}" + } + }, + "notFound": { + "title": "Pagina non trovata", + "text": "Sembra che questa pagina si sia persa. Torniamo sulla strada giusta.", + "goHome": "Torna alla home" + } +} diff --git a/core/http/react-ui/public/locales/it/chat.json b/core/http/react-ui/public/locales/it/chat.json new file mode 100644 index 000000000000..6ee046a32136 --- /dev/null +++ b/core/http/react-ui/public/locales/it/chat.json @@ -0,0 +1,116 @@ +{ + "activity": { + "thought": "Pensiero", + "tool": "Strumento", + "result": "Risultato", + "toolResult": "Risultato di {{name}}", + "thinking": "Sto pensando..." + }, + "header": { + "manageModeTooltip": "Questa chat può installare modelli, modificare configurazioni e gestire i backend parlando con LocalAI.", + "modelInfo": "Info modello", + "chatSettings": "Impostazioni chat", + "modelInfoTitle": "Info modello: {{model}}", + "editConfig": "Modifica configurazione", + "close": "Chiudi" + }, + "modelInfo": { + "backend": "Backend", + "modelFile": "File modello", + "contextSize": "Dimensione contesto", + "threads": "Thread", + "mcp": "MCP", + "configured": "Configurato", + "chatTemplate": "Template chat", + "yes": "Sì", + "gpuLayers": "Layer GPU" + }, + "context": { + "label": "Contesto: {{percent}}%", + "labelWithTokens": "Contesto: {{percent}}% ({{tokens}} token)" + }, + "settings": { + "title": "Impostazioni chat", + "manageMode": "Modalità gestione", + "manageModeDesc": "Permetti a questa chat di installare modelli, cambiare backend e modificare configurazioni parlando con LocalAI.", + "systemPrompt": "Prompt di sistema", + "systemPromptPlaceholder": "Sei un assistente utile...", + "temperature": "Temperatura", + "topP": "Top P", + "topK": "Top K", + "contextSize": "Dimensione contesto", + "contextSizePlaceholder": "2048", + "clearHistory": "Cancella cronologia chat" + }, + "empty": { + "manageTitle": "Gestisci LocalAI chattando", + "manageText": "Chiedi di installare modelli, cambiare backend, modificare configurazioni o controllare lo stato. L'assistente riassumerà le azioni e attenderà la tua conferma prima di apportare qualsiasi modifica.", + "startTitle": "Inizia una conversazione", + "readyText": "Pronto a chattare con {{model}}", + "selectModelText": "Seleziona un modello sopra per iniziare", + "suggestionsManage": [ + "Cosa è installato?", + "Installa un modello chat", + "Mostra lo stato del sistema", + "Aggiorna un backend" + ], + "suggestionsChat": [ + "Spiega come funziona", + "Aiutami a scrivere codice", + "Riassumi un documento", + "Brainstorming di idee" + ], + "recent": "Recenti", + "noMessages": "Nessun messaggio ancora", + "hintEnter": "Invio per inviare", + "hintShiftEnter": "Shift+Invio per andare a capo", + "hintAttach": "Allega file" + }, + "errors": { + "viewTraces": "Visualizza tracce per i dettagli" + }, + "actions": { + "copy": "Copia", + "regenerate": "Rigenera" + }, + "streaming": { + "transferring": "Trasferimento del modello...", + "transferringTo": "Trasferimento del modello a {{node}}..." + }, + "tokens": { + "perSec": "{{count}} tok/s", + "peak": "Picco: {{count}} tok/s", + "usage": "{{prompt}}p + {{completion}}c = {{total}}" + }, + "input": { + "placeholder": "Messaggio...", + "attachFile": "Allega file", + "stopGenerating": "Ferma generazione", + "canvasTitle": "Canvas — estrae blocchi di codice e media in un pannello laterale per anteprima, copia e download", + "canvasLabel": "Canvas", + "openCanvas": "Apri pannello canvas" + }, + "deleteAllDialog": { + "title": "Elimina tutte le chat", + "message": "Eliminare tutte le chat? Questa azione non può essere annullata.", + "confirm": "Elimina tutto" + }, + "toasts": { + "selectModel": "Seleziona un modello", + "copied": "Copiato negli appunti" + }, + "menu": { + "trigger": "Chat", + "triggerTitle": "Conversazioni (Ctrl/Cmd+K)", + "search": "Cerca conversazioni...", + "clearSearch": "Cancella ricerca", + "noMatch": "Nessuna conversazione corrisponde alla ricerca", + "noConversations": "Nessuna conversazione", + "rename": "Rinomina", + "exportMarkdown": "Esporta come Markdown", + "deleteChat": "Elimina chat", + "newChat": "Nuova chat", + "clearAll": "Cancella tutto", + "deleteAllTitle": "Elimina tutte le conversazioni" + } +} diff --git a/core/http/react-ui/public/locales/it/collections.json b/core/http/react-ui/public/locales/it/collections.json new file mode 100644 index 000000000000..e477c94cb3e0 --- /dev/null +++ b/core/http/react-ui/public/locales/it/collections.json @@ -0,0 +1,43 @@ +{ + "title": "Base di conoscenza", + "subtitle": "Gestisci raccolte di documenti per il RAG degli agenti", + "newPlaceholder": "Nome della nuova raccolta...", + "actions": { + "create": "Crea", + "creating": "Creazione...", + "details": "Dettagli", + "reset": "Reimposta", + "delete": "Elimina", + "viewDetails": "Visualizza dettagli", + "resetCollection": "Reimposta raccolta", + "deleteCollection": "Elimina raccolta" + }, + "sections": { + "yourCollections": "Le tue raccolte", + "otherUsersCollections": "Raccolte di altri utenti" + }, + "empty": { + "title": "Nessuna raccolta ancora", + "text": "Le raccolte ti permettono di organizzare i documenti in basi di conoscenza che gli agenti possono consultare con RAG (Retrieval-Augmented Generation). Crea una raccolta sopra per iniziare.", + "noPersonal": "Non hai ancora raccolte." + }, + "deleteDialog": { + "title": "Elimina raccolta", + "message": "Eliminare la raccolta \"{{name}}\"? Verranno rimosse tutte le voci e l'azione non può essere annullata.", + "confirm": "Elimina" + }, + "resetDialog": { + "title": "Reimposta raccolta", + "message": "Reimpostare la raccolta \"{{name}}\"? Verranno rimosse tutte le voci ma la raccolta verrà mantenuta.", + "confirm": "Reimposta" + }, + "toasts": { + "loadFailed": "Caricamento raccolte fallito: {{message}}", + "created": "Raccolta \"{{name}}\" creata", + "createFailed": "Creazione raccolta fallita: {{message}}", + "deleted": "Raccolta \"{{name}}\" eliminata", + "deleteFailed": "Eliminazione raccolta fallita: {{message}}", + "reset": "Raccolta \"{{name}}\" reimpostata", + "resetFailed": "Reimpostazione raccolta fallita: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/it/common.json b/core/http/react-ui/public/locales/it/common.json new file mode 100644 index 000000000000..3b1ced411fe2 --- /dev/null +++ b/core/http/react-ui/public/locales/it/common.json @@ -0,0 +1,109 @@ +{ + "actions": { + "save": "Salva", + "saving": "Salvataggio...", + "cancel": "Annulla", + "close": "Chiudi", + "confirm": "Conferma", + "delete": "Elimina", + "edit": "Modifica", + "add": "Aggiungi", + "remove": "Rimuovi", + "create": "Crea", + "update": "Aggiorna", + "refresh": "Aggiorna", + "reload": "Ricarica", + "retry": "Riprova", + "search": "Cerca", + "filter": "Filtra", + "clear": "Pulisci", + "reset": "Reimposta", + "apply": "Applica", + "back": "Indietro", + "next": "Avanti", + "previous": "Precedente", + "open": "Apri", + "submit": "Invia", + "select": "Seleziona", + "selectAll": "Seleziona tutto", + "copy": "Copia", + "copied": "Copiato", + "download": "Scarica", + "upload": "Carica", + "import": "Importa", + "export": "Esporta", + "view": "Visualizza", + "details": "Dettagli", + "settings": "Impostazioni", + "help": "Aiuto", + "yes": "Sì", + "no": "No", + "loading": "Caricamento..." + }, + "status": { + "loading": "Caricamento...", + "saving": "Salvataggio...", + "saved": "Salvato", + "ready": "Pronto", + "running": "In esecuzione", + "stopped": "Fermo", + "starting": "Avvio in corso...", + "stopping": "Arresto in corso...", + "pending": "In attesa", + "active": "Attivo", + "inactive": "Inattivo", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "online": "Online", + "offline": "Offline", + "error": "Errore", + "success": "Successo", + "warning": "Attenzione", + "info": "Info", + "empty": "Nessun elemento", + "none": "Nessuno", + "unknown": "Sconosciuto" + }, + "dialogs": { + "confirmDelete": { + "title": "Conferma eliminazione", + "message": "Sei sicuro di volerlo eliminare? Questa azione non può essere annullata.", + "confirm": "Elimina", + "cancel": "Annulla" + }, + "unsavedChanges": { + "title": "Modifiche non salvate", + "message": "Hai modifiche non salvate. Vuoi scartarle?", + "discard": "Scarta", + "keepEditing": "Continua a modificare" + } + }, + "forms": { + "required": "Obbligatorio", + "optional": "Opzionale", + "name": "Nome", + "description": "Descrizione", + "type": "Tipo", + "value": "Valore", + "search": "Cerca...", + "selectPlaceholder": "Seleziona un'opzione..." + }, + "time": { + "now": "ora", + "secondsAgo_one": "{{count}} secondo fa", + "secondsAgo_other": "{{count}} secondi fa", + "minutesAgo_one": "{{count}} minuto fa", + "minutesAgo_other": "{{count}} minuti fa", + "hoursAgo_one": "{{count}} ora fa", + "hoursAgo_other": "{{count}} ore fa", + "daysAgo_one": "{{count}} giorno fa", + "daysAgo_other": "{{count}} giorni fa" + }, + "units": { + "bytes": "B", + "kilobytes": "KB", + "megabytes": "MB", + "gigabytes": "GB", + "terabytes": "TB" + } +} diff --git a/core/http/react-ui/public/locales/it/errors.json b/core/http/react-ui/public/locales/it/errors.json new file mode 100644 index 000000000000..ab666f016a48 --- /dev/null +++ b/core/http/react-ui/public/locales/it/errors.json @@ -0,0 +1,17 @@ +{ + "generic": "Si è verificato un errore", + "network": "Errore di rete. Controlla la connessione e riprova.", + "unauthorized": "Non sei autorizzato a eseguire questa azione.", + "forbidden": "Accesso negato.", + "notFound": "La risorsa richiesta non è stata trovata.", + "serverError": "Errore del server. Riprova più tardi.", + "loadFailed": "Caricamento fallito: {{message}}", + "saveFailed": "Salvataggio fallito: {{message}}", + "deleteFailed": "Eliminazione fallita: {{message}}", + "updateFailed": "Aggiornamento fallito: {{message}}", + "createFailed": "Creazione fallita: {{message}}", + "operationFailed": "Operazione fallita: {{message}}", + "invalidInput": "Input non valido. Controlla il modulo e riprova.", + "tryAgain": "Per favore riprova.", + "contactAdmin": "Se il problema persiste, contatta l'amministratore." +} diff --git a/core/http/react-ui/public/locales/it/home.json b/core/http/react-ui/public/locales/it/home.json new file mode 100644 index 000000000000..32adedf7111a --- /dev/null +++ b/core/http/react-ui/public/locales/it/home.json @@ -0,0 +1,66 @@ +{ + "cluster": { + "vram": "VRAM cluster", + "ram": "RAM cluster", + "nodesOnline": "{{healthy}}/{{total}} nodi online" + }, + "resourceGpu": "GPU", + "resourceRam": "RAM", + "assistant": { + "title": "Gestisci LocalAI chattando", + "description": "Installa modelli, cambia backend, modifica configurazioni e controlla lo stato parlando con LocalAI.", + "open": "Apri assistente", + "tooltip": "Gestisci LocalAI chattando" + }, + "input": { + "placeholder": "Messaggio...", + "attachImage": "Allega immagine", + "attachAudio": "Allega audio", + "attachFile": "Allega file", + "enterToSend": "Invio per inviare", + "selectModelFirst": "Seleziona prima un modello", + "sendMessage": "Invia messaggio", + "selectModelToast": "Seleziona prima un modello" + }, + "quickLinks": { + "manageByChat": "Gestisci via chat", + "installedModels": "Modelli installati", + "browseGallery": "Sfoglia galleria", + "importModel": "Importa modello", + "documentation": "Documentazione" + }, + "loadedModels": { + "count_one": "{{count}} modello caricato", + "count_other": "{{count}} modelli caricati", + "stop": "Ferma modello", + "stopAll": "Ferma tutti" + }, + "stopDialog": { + "title": "Ferma modello", + "message": "Fermare il modello {{model}}?", + "confirm": "Ferma {{model}}", + "stopAllTitle": "Ferma tutti i modelli", + "stopAllMessage": "Fermare tutti i {{count}} modelli caricati?", + "stopAllConfirm": "Ferma tutti", + "stoppedToast": "{{model}} fermato", + "allStoppedToast": "Tutti i modelli fermati", + "stopFailed": "Arresto non riuscito: {{message}}" + }, + "wizard": { + "getStarted": "Inizia con {{name}}", + "intro": "Installa il tuo primo modello per iniziare. Sfoglia la galleria o importa il tuo.", + "steps": { + "step1Title": "Sfoglia la galleria modelli", + "step1Body": "Trova il modello giusto per le tue esigenze dalla nostra collezione curata.", + "step2Title": "Installa un modello", + "step2Body": "Clicca installa per scaricarlo e configurarlo automaticamente.", + "step3Title": "Inizia a chattare", + "step3Body": "Chatta con il tuo modello dal browser o usa l'API." + }, + "browseGallery": "Sfoglia galleria modelli", + "importModel": "Importa modello", + "docs": "Documentazione", + "noModelsTitle": "Nessun modello disponibile", + "noModelsBody": "Non ci sono ancora modelli installati. Chiedi al tuo amministratore di configurare i modelli per iniziare a chattare." + } +} diff --git a/core/http/react-ui/public/locales/it/importModel.json b/core/http/react-ui/public/locales/it/importModel.json new file mode 100644 index 000000000000..925f7d0ad719 --- /dev/null +++ b/core/http/react-ui/public/locales/it/importModel.json @@ -0,0 +1,142 @@ +{ + "title": "Importa nuovo modello", + "subtitle": { + "simple": "Importa un modello da un URI — l'auto-detect sceglie il backend.", + "powerYaml": "Scrivi la configurazione YAML completa del modello.", + "powerPrefs": "Preferenze di importazione dettagliate." + }, + "actions": { + "import": "Importa modello", + "importing": "Importazione...", + "create": "Crea", + "saving": "Salvataggio...", + "browseHF": "Sfoglia modelli su HF", + "addCustom": "Aggiungi personalizzato", + "copy": "Copia" + }, + "form": { + "modelUri": "URI modello", + "uriPlaceholder": "huggingface://TheBloke/Llama-2-7B-Chat-GGUF o https://example.com/model.gguf", + "uriHint": "Inserisci l'URI o il percorso del file modello da importare", + "supportedFormats": "Formati URI supportati", + "options": "Opzioni", + "preferences": "Preferenze (opzionali)", + "commonPreferences": "Preferenze comuni", + "customPreferences": "Preferenze personalizzate", + "customKeyValueHint": "Aggiungi coppie chiave-valore personalizzate per configurazioni avanzate.", + "preferenceKey": "Chiave preferenza per la riga {{index}}", + "preferenceValue": "Valore preferenza per la riga {{index}}", + "removePref": "Rimuovi questa preferenza", + "key": "Chiave", + "value": "Valore", + "backend": "Backend", + "backendAuto": "Auto-detect (basato sull'URI)", + "backendLoading": "Caricamento backend…", + "backendSearch": "Cerca backend...", + "backendHint": "Forza un backend specifico. Lascia vuoto per auto-rilevare dall'URI. Le voci segnate \"manual pick\" non sono auto-rilevabili — selezionale tu se sai cosa serve al modello.", + "backendErrorHint": "Impossibile caricare la lista dei backend — solo auto-detect.", + "backendNotInstalled": "Questo backend non è ancora installato. L'invio dell'importazione lo scaricherà prima.", + "modelName": "Nome modello", + "modelNamePlaceholder": "Lascia vuoto per usare il nome file", + "modelNameHint": "Nome personalizzato per il modello. Se vuoto, verrà usato il nome del file.", + "description": "Descrizione", + "descriptionPlaceholder": "Lascia vuoto per usare la descrizione predefinita", + "descriptionHint": "Descrizione personalizzata per il modello.", + "quantizations": "Quantizzazioni", + "quantizationsPlaceholder": "q4_k_m,q4_k_s,q3_k_m (separate da virgola)", + "quantizationsHint": "Quantizzazioni preferite (separate da virgola). Lascia vuoto per il default (q4_k_m).", + "mmprojQuantizations": "Quantizzazioni MMProj", + "mmprojQuantizationsPlaceholder": "fp16,fp32 (separate da virgola)", + "mmprojQuantizationsHint": "Quantizzazioni MMProj preferite. Lascia vuoto per il default (fp16).", + "embeddings": "Embedding", + "embeddingsHint": "Abilita il supporto degli embedding per questo modello.", + "modelType": "Tipo di modello", + "modelTypePlaceholder": "AutoModelForCausalLM (per il backend transformers)", + "modelTypeHint": "Tipo di modello per il backend transformers. Esempi: AutoModelForCausalLM, SentenceTransformer, Mamba.", + "pipelineType": "Tipo di pipeline", + "pipelineTypeHint": "Tipo di pipeline per il backend diffusers.", + "schedulerType": "Tipo di scheduler", + "schedulerTypePlaceholder": "k_dpmpp_2m (opzionale)", + "schedulerTypeHint": "Tipo di scheduler per il backend diffusers. Esempi: k_dpmpp_2m, euler_a, ddim.", + "enableParameters": "Parametri abilitati", + "enableParametersPlaceholder": "negative_prompt,num_inference_steps (separati da virgola)", + "enableParametersHint": "Parametri abilitati per il backend diffusers (separati da virgola).", + "cuda": "CUDA", + "cudaHint": "Abilita il supporto CUDA per l'accelerazione GPU.", + "yamlEditor": "Editor configurazione YAML", + "manualPick": "selezione manuale", + "manualPickTooltip": "L'auto-detect non instraderà a questo backend. Selezionalo qui se sai che è quello che vuoi." + }, + "modality": { + "text": "LLM testuale", + "asr": "Riconoscimento vocale", + "tts": "Sintesi vocale", + "image": "Immagine / Video", + "embeddings": "Embedding", + "reranker": "Reranker", + "detection": "Rilevamento oggetti", + "vad": "Rilevamento attività vocale", + "other": "Altro" + }, + "powerTabs": { + "ariaLabel": "Tab modalità avanzata", + "preferences": "Preferenze", + "yaml": "YAML" + }, + "switchDialog": { + "title": "Mantenere le preferenze personalizzate?", + "body": "Passando alla modalità Semplice si nascondono le preferenze oltre a backend, nome e descrizione. Saranno comunque inviate all'importazione.", + "cancel": "Annulla", + "discard": "Scarta e cambia", + "keep": "Mantieni e cambia" + }, + "estimate": { + "title": "Requisiti stimati", + "download": "Download: {{size}}", + "vram": "VRAM: {{vram}}" + }, + "toasts": { + "noUri": "Inserisci un URI del modello", + "noYaml": "Inserisci la configurazione YAML", + "started": "Importazione avviata! Tracciamento dei progressi...", + "startedWithMeta": "Importazione avviata! Tracciamento dei progressi... ({{meta}})", + "imported": "Modello importato con successo!", + "importedYaml": "Configurazione del modello importata con successo!", + "importFailed": "Importazione fallita: {{message}}", + "startImportFailed": "Avvio importazione fallito: {{message}}", + "backendsLoadFailed": "Impossibile caricare la lista dei backend — uso solo auto-detect", + "modalityClearedBackend": "Selezione backend cancellata — non era nel gruppo {{label}}.", + "copied": "Copiato negli appunti" + }, + "uriFormats": { + "huggingface": { + "title": "HuggingFace", + "standard": "Formato HuggingFace standard", + "short": "Formato HuggingFace breve", + "fullUrl": "URL HuggingFace completo" + }, + "http": { + "title": "URL HTTP/HTTPS", + "direct": "Download diretto da qualsiasi URL HTTPS" + }, + "local": { + "title": "File locali", + "filePath": "Percorso file locale (assoluto)", + "directYaml": "File di configurazione YAML locale diretto" + }, + "oci": { + "title": "Registry OCI", + "registry": "Registry container OCI", + "tarball": "File tarball OCI locale" + }, + "ollama": { + "title": "Ollama", + "model": "Formato modello Ollama" + }, + "yaml": { + "title": "File di configurazione YAML", + "remote": "File di configurazione YAML remoto", + "local": "File di configurazione YAML locale" + } + } +} diff --git a/core/http/react-ui/public/locales/it/media.json b/core/http/react-ui/public/locales/it/media.json new file mode 100644 index 000000000000..cc00e6d212da --- /dev/null +++ b/core/http/react-ui/public/locales/it/media.json @@ -0,0 +1,154 @@ +{ + "studio": { + "tabs": { + "images": "Immagini", + "video": "Video", + "tts": "TTS", + "sound": "Audio" + } + }, + "image": { + "title": "Generazione immagini", + "labels": { + "model": "Modello", + "prompt": "Prompt", + "promptPlaceholder": "Descrivi l'immagine che vuoi generare...", + "negativePrompt": "Prompt negativo", + "negativePromptPlaceholder": "Cosa evitare...", + "size": "Dimensione", + "count": "Quantità (1-4)", + "advanced": "Impostazioni avanzate", + "imageInputs": "Input immagini", + "steps": "Passi", + "stepsPlaceholder": "20", + "seed": "Seed", + "seedPlaceholder": "Casuale", + "sourceImage": "Immagine sorgente (img2img)", + "refImages": "Immagini di riferimento", + "refImagesAdded_one": "{{count}} immagine aggiunta", + "refImagesAdded_other": "{{count}} immagini aggiunte" + }, + "actions": { + "generate": "Genera", + "generating": "Generazione..." + }, + "empty": "Le immagini generate appariranno qui", + "toasts": { + "noPrompt": "Inserisci un prompt", + "noModel": "Seleziona un modello", + "noResults": "Nessuna immagine generata" + } + }, + "video": { + "title": "Generazione video", + "labels": { + "model": "Modello", + "prompt": "Prompt", + "promptPlaceholder": "Descrivi il video che vuoi generare...", + "duration": "Durata (s)", + "fps": "FPS", + "size": "Dimensione", + "advanced": "Impostazioni avanzate", + "seed": "Seed", + "seedPlaceholder": "Casuale" + }, + "actions": { + "generate": "Genera video", + "generating": "Generazione..." + }, + "empty": "I video generati appariranno qui", + "toasts": { + "noPrompt": "Inserisci un prompt", + "noModel": "Seleziona un modello", + "noResults": "Nessun video generato" + } + }, + "tts": { + "title": "Text to Speech", + "labels": { + "model": "Modello", + "voice": "Voce", + "voicePlaceholder": "ID voce opzionale", + "input": "Testo", + "inputPlaceholder": "Inserisci il testo da convertire in voce..." + }, + "actions": { + "generate": "Genera audio", + "generating": "Generazione..." + }, + "empty": "L'audio generato apparirà qui", + "toasts": { + "noText": "Inserisci del testo", + "noModel": "Seleziona un modello", + "generateFailed": "Generazione fallita" + } + }, + "sound": { + "title": "Generazione audio", + "labels": { + "model": "Modello", + "prompt": "Prompt", + "promptPlaceholder": "Descrivi il suono che vuoi generare...", + "duration": "Durata (s)", + "language": "Lingua", + "vocalLanguage": "Lingua vocale", + "lyrics": "Testi (opzionale)", + "lyricsPlaceholder": "Testi per la generazione vocale", + "advanced": "Impostazioni avanzate", + "seed": "Seed", + "seedPlaceholder": "Casuale" + }, + "actions": { + "generate": "Genera", + "generating": "Generazione..." + }, + "empty": "L'audio generato apparirà qui", + "toasts": { + "noPrompt": "Inserisci un prompt", + "noModel": "Seleziona un modello", + "generateFailed": "Generazione fallita" + } + }, + "talk": { + "title": "Conversazione", + "subtitle": "Conversazione vocale in tempo reale", + "actions": { + "start": "Avvia sessione", + "stop": "Termina sessione", + "connecting": "Connessione...", + "muted": "Muto", + "mute": "Disattiva microfono", + "unmute": "Riattiva microfono" + }, + "labels": { + "model": "Modello", + "voice": "Voce", + "voicePlaceholder": "alloy", + "language": "Lingua", + "languagePlaceholder": "it", + "instructions": "Istruzioni", + "instructionsPlaceholder": "Imposta la personalità dell'assistente..." + }, + "status": { + "idle": "Inattivo", + "connecting": "Connessione...", + "listening": "In ascolto...", + "speaking": "Sta parlando...", + "ended": "Sessione terminata" + }, + "toasts": { + "noModel": "Seleziona prima un modello", + "connectFailed": "Connessione fallita: {{message}}" + } + }, + "history": { + "title": "Cronologia", + "empty": "Nessuna cronologia", + "deleteEntry": "Elimina voce", + "clear": "Cancella cronologia", + "clearTitle": "Cancella tutta la cronologia", + "clearMessage": "Rimuovere tutte le voci della cronologia? Questa azione non può essere annullata.", + "clearConfirm": "Cancella", + "cleared": "Cronologia cancellata" + } +} diff --git a/core/http/react-ui/public/locales/it/models.json b/core/http/react-ui/public/locales/it/models.json new file mode 100644 index 000000000000..6e57a280ef59 --- /dev/null +++ b/core/http/react-ui/public/locales/it/models.json @@ -0,0 +1,85 @@ +{ + "title": "Installa modelli", + "subtitle": "Sfoglia e installa modelli AI dalla galleria", + "stats": { + "available": "Disponibili", + "installed": "Installati" + }, + "actions": { + "addModel": "Aggiungi modello", + "importModel": "Importa modello", + "install": "Installa", + "reinstall": "Reinstalla", + "delete": "Elimina" + }, + "filters": { + "all": "Tutti", + "llm": "LLM", + "image": "Immagine", + "multimodal": "Multimodale", + "vision": "Visione", + "tts": "TTS", + "stt": "STT", + "embedding": "Embedding", + "rerank": "Rerank", + "allBackends": "Tutti i backend", + "searchBackends": "Cerca backend..." + }, + "search": { + "placeholder": "Cerca modelli...", + "clearFilters": "Rimuovi filtri" + }, + "table": { + "modelName": "Nome modello", + "description": "Descrizione", + "backend": "Backend", + "sizeVram": "Dimensione / VRAM", + "status": "Stato", + "actions": "Azioni", + "size": "Dimensione: {{size}}", + "vram": "VRAM: {{vram}}", + "fits": "Compatibile", + "mayNotFit": "Potrebbe non entrare", + "trustRemoteCode": "Trust Remote Code", + "installing": "Installazione", + "installingPct": "Installazione · {{percent}}%", + "installed": "Installato", + "notInstalled": "Non installato" + }, + "detail": { + "description": "Descrizione", + "gallery": "Galleria", + "backend": "Backend", + "size": "Dimensione", + "vram": "VRAM", + "license": "Licenza", + "tags": "Tag", + "links": "Link", + "warning": "Attenzione", + "files": "File", + "fitsGpu": "Entra nella GPU", + "mayNotFitGpu": "Potrebbe non entrare nella GPU", + "requiresTrustRemoteCode": "Richiede Trust Remote Code", + "fileCount_one": "{{count}} file", + "fileCount_other": "{{count}} file", + "filename": "Nome file", + "uri": "URI", + "sha256": "SHA256" + }, + "empty": { + "title": "Nessun modello trovato", + "withFilters": "Nessun modello corrisponde ai filtri attuali.", + "noFilters": "La galleria modelli è vuota." + }, + "deleteDialog": { + "title": "Elimina modello", + "message": "Eliminare il modello {{model}}?", + "confirm": "Elimina {{model}}", + "deletingToast": "Eliminazione di {{model}} in corso..." + }, + "errors": { + "loadFailed": "Caricamento modelli fallito: {{message}}", + "installFailed": "Installazione fallita: {{message}}", + "deleteFailed": "Eliminazione fallita: {{message}}" + } +} diff --git a/core/http/react-ui/public/locales/it/nav.json b/core/http/react-ui/public/locales/it/nav.json new file mode 100644 index 000000000000..83c08b125e5f --- /dev/null +++ b/core/http/react-ui/public/locales/it/nav.json @@ -0,0 +1,51 @@ +{ + "appName": "LocalAI", + "openMenu": "Apri menu", + "closeMenu": "Chiudi menu", + "primaryNavigation": "Navigazione principale", + "switchToLightMode": "Passa al tema chiaro", + "switchToDarkMode": "Passa al tema scuro", + "expandSidebar": "Espandi barra laterale", + "collapseSidebar": "Comprimi barra laterale", + "changeLanguage": "Cambia lingua", + "logout": "Esci", + "accountSettings": "Impostazioni account", + "account": "Account", + "accountFor": "Account: {{name}}", + "sections": { + "tools": "Strumenti", + "biometrics": "Biometria", + "agents": "Agenti", + "system": "Sistema" + }, + "items": { + "home": "Home", + "installModels": "Installa modelli", + "chat": "Chat", + "studio": "Studio", + "talk": "Conversazione", + "fineTune": "Fine-Tuning (Sperimentale)", + "quantize": "Quantizzazione (Sperimentale)", + "faceRecognition": "Riconoscimento volti", + "voiceRecognition": "Riconoscimento vocale", + "agents": "Agenti", + "skills": "Competenze", + "memory": "Memoria", + "mcpJobs": "Job MCP CI", + "usage": "Utilizzo", + "users": "Utenti", + "backends": "Backend", + "traces": "Tracce", + "nodes": "Nodi", + "swarm": "Swarm", + "system": "Sistema", + "settings": "Impostazioni", + "api": "API" + }, + "footer": { + "github": "GitHub", + "documentation": "Documentazione", + "author": "Autore", + "copyright": "© 2023-{{year}} {{author}}" + } +} diff --git a/core/http/react-ui/public/locales/it/skills.json b/core/http/react-ui/public/locales/it/skills.json new file mode 100644 index 000000000000..5a0c564a36d7 --- /dev/null +++ b/core/http/react-ui/public/locales/it/skills.json @@ -0,0 +1,79 @@ +{ + "title": "Competenze", + "subtitle": "Gestisci le competenze degli agenti (istruzioni e risorse riutilizzabili)", + "unavailable": { + "subtitle": "Il servizio competenze non è disponibile o l'indice è in ricostruzione. Riprova tra un momento.", + "retry": "Riprova" + }, + "actions": { + "newSkill": "Nuova competenza", + "createSkill": "Crea competenza", + "import": "Importa", + "importing": "Importazione...", + "gitRepos": "Repo Git", + "edit": "Modifica", + "delete": "Elimina", + "export": "Esporta", + "sync": "Sincronizza", + "addRepo": "Aggiungi repo", + "adding": "Aggiunta...", + "remove": "Rimuovi", + "enable": "Abilita", + "disable": "Disabilita" + }, + "search": { + "placeholder": "Cerca competenze..." + }, + "git": { + "title": "Repository Git", + "description": "Aggiungi repository Git da cui ottenere le competenze. Le competenze appariranno nella lista dopo la sincronizzazione.", + "urlPlaceholder": "https://github.com/user/repo o git@github.com:user/repo.git", + "noRepos": "Nessun repo Git configurato. Aggiungine uno sopra.", + "disabled": "Disabilitato", + "removeRepo": "Rimuovi repo" + }, + "card": { + "noDescription": "Nessuna descrizione", + "readOnly": "Sola lettura", + "editTitle": "Modifica competenza", + "deleteTitle": "Elimina competenza", + "exportTitle": "Esporta come .tar.gz" + }, + "empty": { + "title": "Nessuna competenza trovata", + "text": "Crea una competenza o importane una per iniziare.", + "noPersonal": "Non hai ancora competenze." + }, + "sections": { + "yourSkills": "Le tue competenze", + "otherUsersSkills": "Competenze di altri utenti" + }, + "deleteDialog": { + "title": "Elimina competenza", + "message": "Eliminare la competenza \"{{name}}\"? Questa azione non può essere annullata.", + "confirm": "Elimina" + }, + "removeRepoDialog": { + "title": "Rimuovi repository Git", + "message": "Rimuovere questo repository Git? Le competenze al suo interno non saranno più disponibili.", + "confirm": "Rimuovi" + }, + "toasts": { + "loadFailed": "Caricamento competenze fallito", + "deleted": "Competenza \"{{name}}\" eliminata", + "deleteFailed": "Eliminazione competenza fallita", + "exported": "Competenza \"{{name}}\" esportata", + "exportFailed": "Esportazione fallita", + "imported": "Competenza importata da \"{{file}}\"", + "importFailed": "Importazione fallita", + "loadReposFailed": "Caricamento repo Git fallito", + "repoAdded": "Repo Git aggiunto e in sincronizzazione", + "addRepoFailed": "Aggiunta repo fallita", + "synced": "Repo sincronizzato", + "syncFailed": "Sincronizzazione fallita", + "toggled": "Repo modificato", + "toggleFailed": "Modifica fallita", + "removed": "Repo rimosso", + "removeFailed": "Rimozione fallita" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/admin.json b/core/http/react-ui/public/locales/zh-CN/admin.json new file mode 100644 index 000000000000..d55487e69e88 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/admin.json @@ -0,0 +1,62 @@ +{ + "manage": { + "title": "系统", + "subtitle": "管理已安装的模型和后端" + }, + "settings": { + "title": "设置", + "subtitle": "配置 LocalAI 运行时设置", + "saved": "设置保存成功", + "saveFailed": "保存失败:{{message}}", + "loadFailed": "加载设置失败:{{message}}", + "sections": { + "branding": "品牌", + "watchdog": "看门狗", + "memory": "内存", + "backends": "后端", + "performance": "性能", + "tracing": "追踪", + "api": "API 和 CORS", + "p2p": "P2P", + "galleries": "模型库", + "apikeys": "API 密钥", + "agents": "智能体任务", + "agentpool": "智能体池", + "assistant": "LocalAI Assistant", + "responses": "响应" + } + }, + "backends": { + "title": "后端管理", + "subtitle": "发现并安装为模型提供支持的 AI 后端" + }, + "backendLogs": { + "title": "后端日志", + "subtitle": "查看运行中后端的日志", + "empty": "暂无日志" + }, + "traces": { + "title": "追踪", + "subtitle": "查看记录的 API 请求、响应和后端操作" + }, + "nodes": { + "title": "分布式节点", + "subtitle": "管理后端和智能体工作节点" + }, + "p2p": { + "title": "分布式 AI 计算", + "subtitle": "通过点对点分发将您的 AI 工作负载扩展到多个设备" + }, + "users": { + "title": "用户", + "subtitle": "管理注册用户、角色和邀请" + }, + "usage": { + "title": "使用情况", + "subtitle": "API 令牌使用统计" + }, + "explorer": { + "title": "资源浏览器", + "subtitle": "浏览文件和配置" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/agents.json b/core/http/react-ui/public/locales/zh-CN/agents.json new file mode 100644 index 000000000000..972488fcf6e0 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/agents.json @@ -0,0 +1,55 @@ +{ + "title": "智能体", + "subtitle": "管理自主 AI 智能体", + "actions": { + "agentHub": "Agent Hub", + "import": "导入", + "createAgent": "创建智能体", + "edit": "编辑", + "chat": "聊天", + "export": "导出", + "delete": "删除", + "pause": "暂停", + "resume": "恢复" + }, + "table": { + "name": "名称", + "status": "状态", + "events": "事件", + "actions": "操作", + "eventsTooltip": "{{count}} 个事件 - 点击查看" + }, + "search": { + "placeholder": "搜索智能体...", + "summary_one": "{{total}} 个智能体中的 {{shown}} 个", + "summary_other": "{{total}} 个智能体中的 {{shown}} 个" + }, + "empty": { + "noConfigured": "未配置智能体", + "noConfiguredText": "创建一个智能体以开始自主 AI 工作流。", + "browseHub": "不知从何开始?浏览 <1>Agent Hub 寻找现成的智能体配置以便导入。", + "noMatching": "没有匹配的智能体", + "noMatchingText": "没有智能体匹配 \"{{query}}\"" + }, + "sections": { + "yourAgents": "您的智能体", + "otherUsersAgents": "其他用户的智能体" + }, + "deleteDialog": { + "title": "删除智能体", + "message": "删除智能体 \"{{name}}\"?此操作无法撤销。", + "confirm": "删除" + }, + "toasts": { + "loadFailed": "加载智能体失败:{{message}}", + "deleted": "智能体 \"{{name}}\" 已删除", + "deleteFailed": "删除智能体失败:{{message}}", + "paused": "智能体 \"{{name}}\" 已暂停", + "resumed": "智能体 \"{{name}}\" 已恢复", + "pauseFailed": "暂停智能体失败:{{message}}", + "resumeFailed": "恢复智能体失败:{{message}}", + "exported": "智能体 \"{{name}}\" 已导出", + "exportFailed": "导出智能体失败:{{message}}", + "parseFailed": "解析智能体文件失败:{{message}}" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/auth.json b/core/http/react-ui/public/locales/zh-CN/auth.json new file mode 100644 index 000000000000..4f3229f50fbf --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/auth.json @@ -0,0 +1,112 @@ +{ + "login": { + "subtitle": "登录以继续", + "registerSubtitle": "创建账户", + "createAdminSubtitle": "创建您的管理员账户", + "tokenSubtitle": "输入您的 API 密钥以继续", + "email": "邮箱", + "emailPlaceholder": "you@example.com", + "name": "姓名", + "namePlaceholder": "您的姓名(可选)", + "password": "密码", + "passwordPlaceholder": "输入密码...", + "newPasswordPlaceholder": "至少 8 个字符", + "confirmPassword": "确认密码", + "confirmPasswordPlaceholder": "再次输入密码", + "inviteCodeLabel": "邀请码", + "inviteCodeOptional": "(可选 — 跳过审核等待)", + "inviteCodePlaceholder": "粘贴您的邀请码...", + "tokenPlaceholder": "输入 API 密钥...", + "tokenAltPlaceholder": "输入 API 令牌...", + "signIn": "登录", + "signingIn": "登录中...", + "register": "注册", + "creatingAccount": "创建账户中...", + "createAdminAccount": "创建管理员账户", + "signInWithGitHub": "使用 GitHub 登录", + "signInWithSSO": "使用 SSO 登录", + "loginWithToken": "使用令牌登录", + "showTokenLogin": "使用 API 令牌登录", + "hideTokenLogin": "隐藏令牌登录", + "noAccount": "还没有账户?", + "hasAccount": "已有账户?", + "or": "或", + "errors": { + "loginFailed": "登录失败", + "registrationFailed": "注册失败", + "invalidToken": "无效的令牌", + "passwordsDoNotMatch": "密码不匹配", + "enterToken": "请输入令牌", + "networkError": "网络错误", + "inviteRequired": "注册需要有效的邀请码" + }, + "messages": { + "registrationPending": "注册成功,等待审核。" + } + }, + "account": { + "title": "账户", + "subtitle": "个人资料、凭证和 API 密钥", + "unavailable": "账户不可用", + "unavailableText": "必须启用身份验证才能管理您的账户。", + "tabs": { + "profile": "个人资料", + "security": "安全", + "apiKeys": "API 密钥" + }, + "profile": { + "displayName": "显示名称", + "displayNameDescription": "您的公开显示名称", + "avatarUrl": "头像 URL", + "avatarUrlDescription": "您的头像图片 URL", + "avatarUrlPlaceholder": "https://example.com/avatar.png", + "save": "保存", + "saving": "保存中...", + "updated": "个人资料已更新", + "updateFailed": "更新个人资料失败:{{message}}" + }, + "security": { + "currentPassword": "当前密码", + "currentPasswordDescription": "输入您的现有密码以验证身份", + "currentPasswordPlaceholder": "当前密码", + "newPassword": "新密码", + "newPasswordDescription": "至少需要 8 个字符", + "newPasswordPlaceholder": "新密码", + "confirmPassword": "确认密码", + "confirmPasswordDescription": "再次输入新密码", + "confirmPasswordPlaceholder": "确认新密码", + "changePassword": "更改密码", + "changing": "正在更改...", + "changed": "密码已更改", + "passwordsDoNotMatch": "密码不匹配", + "tooShort": "新密码至少需要 8 个字符", + "oauthOnly": "{{provider}} 账户不支持密码管理。" + }, + "apiKeys": { + "create": "创建 API 密钥", + "createDescription": "为程序化访问生成密钥", + "namePlaceholder": "密钥名称(例如 my-app)", + "createButton": "创建", + "creating": "创建中...", + "createdToast": "API 密钥已创建", + "createFailed": "创建 API 密钥失败:{{message}}", + "loadFailed": "加载 API 密钥失败:{{message}}", + "revoke": "撤销", + "revokeKey": "撤销密钥", + "revokeTitle": "撤销 API 密钥", + "revokeMessage": "撤销 API 密钥 \"{{name}}\"?此操作无法撤销。", + "revoked": "API 密钥已撤销", + "revokeFailed": "撤销 API 密钥失败:{{message}}", + "copyNow": "立即复制 — 此密钥不会再次显示", + "copiedToast": "已复制到剪贴板", + "copyFailed": "复制失败", + "empty": "尚无 API 密钥。在上方创建一个以获得程序化访问。", + "lastUsed": "上次使用 {{date}}" + } + }, + "notFound": { + "title": "页面未找到", + "text": "看起来这个页面走丢了。让我们回到正轨。", + "goHome": "返回首页" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/chat.json b/core/http/react-ui/public/locales/zh-CN/chat.json new file mode 100644 index 000000000000..509bd0f49960 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/chat.json @@ -0,0 +1,116 @@ +{ + "activity": { + "thought": "思考", + "tool": "工具", + "result": "结果", + "toolResult": "{{name}} 的结果", + "thinking": "思考中..." + }, + "header": { + "manageModeTooltip": "此聊天可以通过与 LocalAI 对话来安装模型、编辑配置和管理后端。", + "modelInfo": "模型信息", + "chatSettings": "聊天设置", + "modelInfoTitle": "模型信息:{{model}}", + "editConfig": "编辑配置", + "close": "关闭" + }, + "modelInfo": { + "backend": "后端", + "modelFile": "模型文件", + "contextSize": "上下文大小", + "threads": "线程数", + "mcp": "MCP", + "configured": "已配置", + "chatTemplate": "聊天模板", + "yes": "是", + "gpuLayers": "GPU 层数" + }, + "context": { + "label": "上下文:{{percent}}%", + "labelWithTokens": "上下文:{{percent}}% ({{tokens}} 个 token)" + }, + "settings": { + "title": "聊天设置", + "manageMode": "管理模式", + "manageModeDesc": "允许此聊天通过与 LocalAI 对话来安装模型、切换后端和编辑配置。", + "systemPrompt": "系统提示", + "systemPromptPlaceholder": "你是一个有用的助手...", + "temperature": "温度", + "topP": "Top P", + "topK": "Top K", + "contextSize": "上下文大小", + "contextSizePlaceholder": "2048", + "clearHistory": "清除聊天记录" + }, + "empty": { + "manageTitle": "通过聊天管理 LocalAI", + "manageText": "可以请求安装模型、切换后端、编辑配置或检查状态。助手会总结操作并在更改任何内容前等待您确认。", + "startTitle": "开始对话", + "readyText": "准备与 {{model}} 聊天", + "selectModelText": "在上方选择一个模型以开始", + "suggestionsManage": [ + "已经安装了什么?", + "安装一个聊天模型", + "显示系统状态", + "更新一个后端" + ], + "suggestionsChat": [ + "解释这是如何工作的", + "帮我写代码", + "总结一份文档", + "头脑风暴" + ], + "recent": "最近", + "noMessages": "暂无消息", + "hintEnter": "回车发送", + "hintShiftEnter": "Shift+回车换行", + "hintAttach": "附加文件" + }, + "errors": { + "viewTraces": "查看追踪以了解详情" + }, + "actions": { + "copy": "复制", + "regenerate": "重新生成" + }, + "streaming": { + "transferring": "正在传输模型...", + "transferringTo": "正在传输模型到 {{node}}..." + }, + "tokens": { + "perSec": "{{count}} tok/s", + "peak": "峰值:{{count}} tok/s", + "usage": "{{prompt}}p + {{completion}}c = {{total}}" + }, + "input": { + "placeholder": "消息...", + "attachFile": "附加文件", + "stopGenerating": "停止生成", + "canvasTitle": "Canvas — 将代码块和媒体提取到侧边栏以便预览、复制和下载", + "canvasLabel": "Canvas", + "openCanvas": "打开 Canvas 面板" + }, + "deleteAllDialog": { + "title": "删除所有聊天", + "message": "删除所有聊天?此操作无法撤销。", + "confirm": "全部删除" + }, + "toasts": { + "selectModel": "请选择一个模型", + "copied": "已复制到剪贴板" + }, + "menu": { + "trigger": "聊天", + "triggerTitle": "对话 (Ctrl/Cmd+K)", + "search": "搜索对话...", + "clearSearch": "清除搜索", + "noMatch": "没有对话匹配您的搜索", + "noConversations": "暂无对话", + "rename": "重命名", + "exportMarkdown": "导出为 Markdown", + "deleteChat": "删除聊天", + "newChat": "新对话", + "clearAll": "清除全部", + "deleteAllTitle": "删除所有对话" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/collections.json b/core/http/react-ui/public/locales/zh-CN/collections.json new file mode 100644 index 000000000000..b6b3838a2259 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/collections.json @@ -0,0 +1,43 @@ +{ + "title": "知识库", + "subtitle": "为智能体的 RAG 管理文档集合", + "newPlaceholder": "新集合名称...", + "actions": { + "create": "创建", + "creating": "创建中...", + "details": "详情", + "reset": "重置", + "delete": "删除", + "viewDetails": "查看详情", + "resetCollection": "重置集合", + "deleteCollection": "删除集合" + }, + "sections": { + "yourCollections": "您的集合", + "otherUsersCollections": "其他用户的集合" + }, + "empty": { + "title": "暂无集合", + "text": "集合让您可以将文档组织成知识库,智能体可以通过 RAG(检索增强生成)进行搜索。在上方创建一个集合以开始。", + "noPersonal": "您还没有任何集合。" + }, + "deleteDialog": { + "title": "删除集合", + "message": "删除集合 \"{{name}}\"?这将删除所有条目,且无法撤销。", + "confirm": "删除" + }, + "resetDialog": { + "title": "重置集合", + "message": "重置集合 \"{{name}}\"?这将删除所有条目但保留集合。", + "confirm": "重置" + }, + "toasts": { + "loadFailed": "加载集合失败:{{message}}", + "created": "集合 \"{{name}}\" 已创建", + "createFailed": "创建集合失败:{{message}}", + "deleted": "集合 \"{{name}}\" 已删除", + "deleteFailed": "删除集合失败:{{message}}", + "reset": "集合 \"{{name}}\" 已重置", + "resetFailed": "重置集合失败:{{message}}" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/common.json b/core/http/react-ui/public/locales/zh-CN/common.json new file mode 100644 index 000000000000..8d5860d85608 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/common.json @@ -0,0 +1,109 @@ +{ + "actions": { + "save": "保存", + "saving": "保存中...", + "cancel": "取消", + "close": "关闭", + "confirm": "确认", + "delete": "删除", + "edit": "编辑", + "add": "添加", + "remove": "移除", + "create": "创建", + "update": "更新", + "refresh": "刷新", + "reload": "重新加载", + "retry": "重试", + "search": "搜索", + "filter": "筛选", + "clear": "清除", + "reset": "重置", + "apply": "应用", + "back": "返回", + "next": "下一步", + "previous": "上一步", + "open": "打开", + "submit": "提交", + "select": "选择", + "selectAll": "全选", + "copy": "复制", + "copied": "已复制", + "download": "下载", + "upload": "上传", + "import": "导入", + "export": "导出", + "view": "查看", + "details": "详情", + "settings": "设置", + "help": "帮助", + "yes": "是", + "no": "否", + "loading": "加载中..." + }, + "status": { + "loading": "加载中...", + "saving": "保存中...", + "saved": "已保存", + "ready": "就绪", + "running": "运行中", + "stopped": "已停止", + "starting": "启动中...", + "stopping": "停止中...", + "pending": "等待中", + "active": "活跃", + "inactive": "未活跃", + "enabled": "已启用", + "disabled": "已禁用", + "online": "在线", + "offline": "离线", + "error": "错误", + "success": "成功", + "warning": "警告", + "info": "信息", + "empty": "无内容", + "none": "无", + "unknown": "未知" + }, + "dialogs": { + "confirmDelete": { + "title": "确认删除", + "message": "您确定要删除吗?此操作无法撤销。", + "confirm": "删除", + "cancel": "取消" + }, + "unsavedChanges": { + "title": "未保存的更改", + "message": "您有未保存的更改。是否要放弃?", + "discard": "放弃", + "keepEditing": "继续编辑" + } + }, + "forms": { + "required": "必填", + "optional": "可选", + "name": "名称", + "description": "描述", + "type": "类型", + "value": "值", + "search": "搜索...", + "selectPlaceholder": "选择一个选项..." + }, + "time": { + "now": "刚才", + "secondsAgo_one": "{{count}} 秒前", + "secondsAgo_other": "{{count}} 秒前", + "minutesAgo_one": "{{count}} 分钟前", + "minutesAgo_other": "{{count}} 分钟前", + "hoursAgo_one": "{{count}} 小时前", + "hoursAgo_other": "{{count}} 小时前", + "daysAgo_one": "{{count}} 天前", + "daysAgo_other": "{{count}} 天前" + }, + "units": { + "bytes": "B", + "kilobytes": "KB", + "megabytes": "MB", + "gigabytes": "GB", + "terabytes": "TB" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/errors.json b/core/http/react-ui/public/locales/zh-CN/errors.json new file mode 100644 index 000000000000..59412a8f0796 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/errors.json @@ -0,0 +1,17 @@ +{ + "generic": "出错了", + "network": "网络错误。请检查您的连接并重试。", + "unauthorized": "您无权执行此操作。", + "forbidden": "访问被拒绝。", + "notFound": "未找到请求的资源。", + "serverError": "服务器错误。请稍后再试。", + "loadFailed": "加载失败:{{message}}", + "saveFailed": "保存失败:{{message}}", + "deleteFailed": "删除失败:{{message}}", + "updateFailed": "更新失败:{{message}}", + "createFailed": "创建失败:{{message}}", + "operationFailed": "操作失败:{{message}}", + "invalidInput": "输入无效。请检查表单后重试。", + "tryAgain": "请重试。", + "contactAdmin": "如果问题仍然存在,请联系您的管理员。" +} diff --git a/core/http/react-ui/public/locales/zh-CN/home.json b/core/http/react-ui/public/locales/zh-CN/home.json new file mode 100644 index 000000000000..392e817beabf --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/home.json @@ -0,0 +1,66 @@ +{ + "cluster": { + "vram": "集群 VRAM", + "ram": "集群 RAM", + "nodesOnline": "{{healthy}}/{{total}} 节点在线" + }, + "resourceGpu": "GPU", + "resourceRam": "RAM", + "assistant": { + "title": "通过聊天管理 LocalAI", + "description": "通过与 LocalAI 对话来安装模型、切换后端、编辑配置和查看状态。", + "open": "打开助手", + "tooltip": "通过聊天管理 LocalAI" + }, + "input": { + "placeholder": "消息...", + "attachImage": "附加图片", + "attachAudio": "附加音频", + "attachFile": "附加文件", + "enterToSend": "按回车发送", + "selectModelFirst": "请先选择一个模型", + "sendMessage": "发送消息", + "selectModelToast": "请先选择一个模型" + }, + "quickLinks": { + "manageByChat": "通过聊天管理", + "installedModels": "已安装的模型", + "browseGallery": "浏览模型库", + "importModel": "导入模型", + "documentation": "文档" + }, + "loadedModels": { + "count_one": "已加载 {{count}} 个模型", + "count_other": "已加载 {{count}} 个模型", + "stop": "停止模型", + "stopAll": "全部停止" + }, + "stopDialog": { + "title": "停止模型", + "message": "停止模型 {{model}}?", + "confirm": "停止 {{model}}", + "stopAllTitle": "停止所有模型", + "stopAllMessage": "停止所有 {{count}} 个已加载的模型?", + "stopAllConfirm": "全部停止", + "stoppedToast": "已停止 {{model}}", + "allStoppedToast": "所有模型已停止", + "stopFailed": "停止失败:{{message}}" + }, + "wizard": { + "getStarted": "开始使用 {{name}}", + "intro": "安装您的第一个模型即可开始。浏览模型库或导入您自己的模型。", + "steps": { + "step1Title": "浏览模型库", + "step1Body": "从我们精选的集合中找到适合您需求的模型。", + "step2Title": "安装模型", + "step2Body": "点击安装即可自动下载并配置。", + "step3Title": "开始聊天", + "step3Body": "直接在浏览器中与模型聊天,或使用 API。" + }, + "browseGallery": "浏览模型库", + "importModel": "导入模型", + "docs": "文档", + "noModelsTitle": "暂无可用模型", + "noModelsBody": "尚未安装模型。请联系您的管理员设置模型,以便开始聊天。" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/importModel.json b/core/http/react-ui/public/locales/zh-CN/importModel.json new file mode 100644 index 000000000000..2255a071107f --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/importModel.json @@ -0,0 +1,142 @@ +{ + "title": "导入新模型", + "subtitle": { + "simple": "从 URI 导入模型 — 自动检测会选择后端。", + "powerYaml": "编写完整的模型 YAML 配置。", + "powerPrefs": "细粒度的导入偏好设置。" + }, + "actions": { + "import": "导入模型", + "importing": "导入中...", + "create": "创建", + "saving": "保存中...", + "browseHF": "在 HF 上浏览模型", + "addCustom": "添加自定义", + "copy": "复制" + }, + "form": { + "modelUri": "模型 URI", + "uriPlaceholder": "huggingface://TheBloke/Llama-2-7B-Chat-GGUF 或 https://example.com/model.gguf", + "uriHint": "输入要导入的模型文件的 URI 或路径", + "supportedFormats": "支持的 URI 格式", + "options": "选项", + "preferences": "偏好设置(可选)", + "commonPreferences": "常用偏好", + "customPreferences": "自定义偏好", + "customKeyValueHint": "添加自定义键值对以进行高级配置。", + "preferenceKey": "第 {{index}} 行的偏好键", + "preferenceValue": "第 {{index}} 行的偏好值", + "removePref": "移除此偏好", + "key": "键", + "value": "值", + "backend": "后端", + "backendAuto": "自动检测(基于 URI)", + "backendLoading": "正在加载后端…", + "backendSearch": "搜索后端...", + "backendHint": "强制指定一个后端。留空可从 URI 自动检测。标记为 \"manual pick\" 的项目无法自动检测 — 如果您知道模型需要什么,请自行选择。", + "backendErrorHint": "无法加载后端列表 — 仅使用自动检测。", + "backendNotInstalled": "此后端尚未安装。提交导入将先下载它。", + "modelName": "模型名称", + "modelNamePlaceholder": "留空以使用文件名", + "modelNameHint": "模型的自定义名称。若为空,将使用文件名。", + "description": "描述", + "descriptionPlaceholder": "留空以使用默认描述", + "descriptionHint": "模型的自定义描述。", + "quantizations": "量化", + "quantizationsPlaceholder": "q4_k_m,q4_k_s,q3_k_m(逗号分隔)", + "quantizationsHint": "首选量化(逗号分隔)。留空使用默认值(q4_k_m)。", + "mmprojQuantizations": "MMProj 量化", + "mmprojQuantizationsPlaceholder": "fp16,fp32(逗号分隔)", + "mmprojQuantizationsHint": "首选 MMProj 量化。留空使用默认值(fp16)。", + "embeddings": "嵌入", + "embeddingsHint": "为此模型启用嵌入支持。", + "modelType": "模型类型", + "modelTypePlaceholder": "AutoModelForCausalLM(用于 transformers 后端)", + "modelTypeHint": "transformers 后端的模型类型。例如:AutoModelForCausalLM、SentenceTransformer、Mamba。", + "pipelineType": "管道类型", + "pipelineTypeHint": "diffusers 后端的管道类型。", + "schedulerType": "调度器类型", + "schedulerTypePlaceholder": "k_dpmpp_2m(可选)", + "schedulerTypeHint": "diffusers 后端的调度器类型。例如:k_dpmpp_2m、euler_a、ddim。", + "enableParameters": "启用的参数", + "enableParametersPlaceholder": "negative_prompt,num_inference_steps(逗号分隔)", + "enableParametersHint": "diffusers 后端启用的参数(逗号分隔)。", + "cuda": "CUDA", + "cudaHint": "启用 CUDA 支持以进行 GPU 加速。", + "yamlEditor": "YAML 配置编辑器", + "manualPick": "手动选择", + "manualPickTooltip": "自动检测不会路由到此后端。如果您知道这就是您想要的,请在此处选择。" + }, + "modality": { + "text": "文本 LLM", + "asr": "语音识别", + "tts": "文字转语音", + "image": "图像 / 视频", + "embeddings": "嵌入", + "reranker": "重排器", + "detection": "对象检测", + "vad": "语音活动检测", + "other": "其他" + }, + "powerTabs": { + "ariaLabel": "高级模式标签", + "preferences": "偏好", + "yaml": "YAML" + }, + "switchDialog": { + "title": "保留您的自定义偏好?", + "body": "切换到简单模式会隐藏除后端、名称和描述之外的偏好设置。它们仍会在您导入时被发送。", + "cancel": "取消", + "discard": "放弃并切换", + "keep": "保留并切换" + }, + "estimate": { + "title": "估计需求", + "download": "下载:{{size}}", + "vram": "VRAM:{{vram}}" + }, + "toasts": { + "noUri": "请输入模型 URI", + "noYaml": "请输入 YAML 配置", + "started": "导入已开始!正在跟踪进度...", + "startedWithMeta": "导入已开始!正在跟踪进度...({{meta}})", + "imported": "模型导入成功!", + "importedYaml": "模型配置导入成功!", + "importFailed": "导入失败:{{message}}", + "startImportFailed": "开始导入失败:{{message}}", + "backendsLoadFailed": "无法加载后端列表 — 仅使用自动检测", + "modalityClearedBackend": "已清除后端选择 — 它不在 {{label}} 组中。", + "copied": "已复制到剪贴板" + }, + "uriFormats": { + "huggingface": { + "title": "HuggingFace", + "standard": "标准 HuggingFace 格式", + "short": "简短的 HuggingFace 格式", + "fullUrl": "完整的 HuggingFace URL" + }, + "http": { + "title": "HTTP/HTTPS URL", + "direct": "从任何 HTTPS URL 直接下载" + }, + "local": { + "title": "本地文件", + "filePath": "本地文件路径(绝对路径)", + "directYaml": "直接的本地 YAML 配置文件" + }, + "oci": { + "title": "OCI 注册表", + "registry": "OCI 容器注册表", + "tarball": "本地 OCI tarball 文件" + }, + "ollama": { + "title": "Ollama", + "model": "Ollama 模型格式" + }, + "yaml": { + "title": "YAML 配置文件", + "remote": "远程 YAML 配置文件", + "local": "本地 YAML 配置文件" + } + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/media.json b/core/http/react-ui/public/locales/zh-CN/media.json new file mode 100644 index 000000000000..b23af26f785b --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/media.json @@ -0,0 +1,154 @@ +{ + "studio": { + "tabs": { + "images": "图像", + "video": "视频", + "tts": "TTS", + "sound": "声音" + } + }, + "image": { + "title": "图像生成", + "labels": { + "model": "模型", + "prompt": "提示词", + "promptPlaceholder": "描述您想要生成的图像...", + "negativePrompt": "负面提示词", + "negativePromptPlaceholder": "想要避免的内容...", + "size": "尺寸", + "count": "数量 (1-4)", + "advanced": "高级设置", + "imageInputs": "图像输入", + "steps": "步数", + "stepsPlaceholder": "20", + "seed": "随机种子", + "seedPlaceholder": "随机", + "sourceImage": "源图像 (img2img)", + "refImages": "参考图像", + "refImagesAdded_one": "已添加 {{count}} 张图像", + "refImagesAdded_other": "已添加 {{count}} 张图像" + }, + "actions": { + "generate": "生成", + "generating": "生成中..." + }, + "empty": "生成的图像将显示在此处", + "toasts": { + "noPrompt": "请输入提示词", + "noModel": "请选择一个模型", + "noResults": "未生成图像" + } + }, + "video": { + "title": "视频生成", + "labels": { + "model": "模型", + "prompt": "提示词", + "promptPlaceholder": "描述您想要生成的视频...", + "duration": "时长(秒)", + "fps": "FPS", + "size": "尺寸", + "advanced": "高级设置", + "seed": "随机种子", + "seedPlaceholder": "随机" + }, + "actions": { + "generate": "生成视频", + "generating": "生成中..." + }, + "empty": "生成的视频将显示在此处", + "toasts": { + "noPrompt": "请输入提示词", + "noModel": "请选择一个模型", + "noResults": "未生成视频" + } + }, + "tts": { + "title": "文字转语音", + "labels": { + "model": "模型", + "voice": "声音", + "voicePlaceholder": "可选的声音 ID", + "input": "文本", + "inputPlaceholder": "输入要合成的文本..." + }, + "actions": { + "generate": "生成音频", + "generating": "生成中..." + }, + "empty": "生成的音频将显示在此处", + "toasts": { + "noText": "请输入文本", + "noModel": "请选择一个模型", + "generateFailed": "生成失败" + } + }, + "sound": { + "title": "声音生成", + "labels": { + "model": "模型", + "prompt": "提示词", + "promptPlaceholder": "描述您想要生成的声音...", + "duration": "时长(秒)", + "language": "语言", + "vocalLanguage": "声乐语言", + "lyrics": "歌词(可选)", + "lyricsPlaceholder": "用于声乐生成的歌词", + "advanced": "高级设置", + "seed": "随机种子", + "seedPlaceholder": "随机" + }, + "actions": { + "generate": "生成", + "generating": "生成中..." + }, + "empty": "生成的音频将显示在此处", + "toasts": { + "noPrompt": "请输入提示词", + "noModel": "请选择一个模型", + "generateFailed": "生成失败" + } + }, + "talk": { + "title": "通话", + "subtitle": "实时语音对话", + "actions": { + "start": "开始会话", + "stop": "结束会话", + "connecting": "连接中...", + "muted": "已静音", + "mute": "静音", + "unmute": "取消静音" + }, + "labels": { + "model": "模型", + "voice": "声音", + "voicePlaceholder": "alloy", + "language": "语言", + "languagePlaceholder": "zh", + "instructions": "指令", + "instructionsPlaceholder": "设置助手的角色..." + }, + "status": { + "idle": "空闲", + "connecting": "连接中...", + "listening": "正在聆听...", + "speaking": "正在说话...", + "ended": "会话已结束" + }, + "toasts": { + "noModel": "请先选择一个模型", + "connectFailed": "连接失败:{{message}}" + } + }, + "history": { + "title": "历史", + "empty": "暂无历史", + "deleteEntry": "删除条目", + "clear": "清除历史", + "clearTitle": "清除全部历史", + "clearMessage": "删除所有历史条目?此操作无法撤销。", + "clearConfirm": "清除", + "cleared": "历史已清除" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/models.json b/core/http/react-ui/public/locales/zh-CN/models.json new file mode 100644 index 000000000000..ba6366028472 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/models.json @@ -0,0 +1,85 @@ +{ + "title": "安装模型", + "subtitle": "从模型库浏览和安装 AI 模型", + "stats": { + "available": "可用", + "installed": "已安装" + }, + "actions": { + "addModel": "添加模型", + "importModel": "导入模型", + "install": "安装", + "reinstall": "重新安装", + "delete": "删除" + }, + "filters": { + "all": "全部", + "llm": "LLM", + "image": "图像", + "multimodal": "多模态", + "vision": "视觉", + "tts": "TTS", + "stt": "STT", + "embedding": "嵌入", + "rerank": "重排", + "allBackends": "所有后端", + "searchBackends": "搜索后端..." + }, + "search": { + "placeholder": "搜索模型...", + "clearFilters": "清除筛选" + }, + "table": { + "modelName": "模型名称", + "description": "描述", + "backend": "后端", + "sizeVram": "大小 / VRAM", + "status": "状态", + "actions": "操作", + "size": "大小:{{size}}", + "vram": "VRAM:{{vram}}", + "fits": "适合", + "mayNotFit": "可能不适合", + "trustRemoteCode": "Trust Remote Code", + "installing": "正在安装", + "installingPct": "正在安装 · {{percent}}%", + "installed": "已安装", + "notInstalled": "未安装" + }, + "detail": { + "description": "描述", + "gallery": "模型库", + "backend": "后端", + "size": "大小", + "vram": "VRAM", + "license": "许可证", + "tags": "标签", + "links": "链接", + "warning": "警告", + "files": "文件", + "fitsGpu": "适合 GPU", + "mayNotFitGpu": "可能不适合 GPU", + "requiresTrustRemoteCode": "需要 Trust Remote Code", + "fileCount_one": "{{count}} 个文件", + "fileCount_other": "{{count}} 个文件", + "filename": "文件名", + "uri": "URI", + "sha256": "SHA256" + }, + "empty": { + "title": "未找到模型", + "withFilters": "没有模型与当前搜索或筛选条件匹配。", + "noFilters": "模型库为空。" + }, + "deleteDialog": { + "title": "删除模型", + "message": "删除模型 {{model}}?", + "confirm": "删除 {{model}}", + "deletingToast": "正在删除 {{model}}..." + }, + "errors": { + "loadFailed": "加载模型失败:{{message}}", + "installFailed": "安装失败:{{message}}", + "deleteFailed": "删除失败:{{message}}" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/nav.json b/core/http/react-ui/public/locales/zh-CN/nav.json new file mode 100644 index 000000000000..28ecb42f4b48 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/nav.json @@ -0,0 +1,51 @@ +{ + "appName": "LocalAI", + "openMenu": "打开菜单", + "closeMenu": "关闭菜单", + "primaryNavigation": "主导航", + "switchToLightMode": "切换到浅色模式", + "switchToDarkMode": "切换到深色模式", + "expandSidebar": "展开侧边栏", + "collapseSidebar": "收起侧边栏", + "changeLanguage": "更改语言", + "logout": "退出登录", + "accountSettings": "账户设置", + "account": "账户", + "accountFor": "账户:{{name}}", + "sections": { + "tools": "工具", + "biometrics": "生物识别", + "agents": "智能体", + "system": "系统" + }, + "items": { + "home": "首页", + "installModels": "安装模型", + "chat": "聊天", + "studio": "工作室", + "talk": "通话", + "fineTune": "微调(实验性)", + "quantize": "量化(实验性)", + "faceRecognition": "人脸识别", + "voiceRecognition": "语音识别", + "agents": "智能体", + "skills": "技能", + "memory": "记忆库", + "mcpJobs": "MCP CI 任务", + "usage": "用量", + "users": "用户", + "backends": "后端", + "traces": "追踪", + "nodes": "节点", + "swarm": "Swarm", + "system": "系统", + "settings": "设置", + "api": "API" + }, + "footer": { + "github": "GitHub", + "documentation": "文档", + "author": "作者", + "copyright": "© 2023-{{year}} {{author}}" + } +} diff --git a/core/http/react-ui/public/locales/zh-CN/skills.json b/core/http/react-ui/public/locales/zh-CN/skills.json new file mode 100644 index 000000000000..f9468f1e0420 --- /dev/null +++ b/core/http/react-ui/public/locales/zh-CN/skills.json @@ -0,0 +1,79 @@ +{ + "title": "技能", + "subtitle": "管理智能体技能(可重用的指令和资源)", + "unavailable": { + "subtitle": "技能服务不可用或索引正在重建。请稍后再试。", + "retry": "重试" + }, + "actions": { + "newSkill": "新技能", + "createSkill": "创建技能", + "import": "导入", + "importing": "正在导入...", + "gitRepos": "Git 仓库", + "edit": "编辑", + "delete": "删除", + "export": "导出", + "sync": "同步", + "addRepo": "添加仓库", + "adding": "添加中...", + "remove": "移除", + "enable": "启用", + "disable": "禁用" + }, + "search": { + "placeholder": "搜索技能..." + }, + "git": { + "title": "Git 仓库", + "description": "添加 Git 仓库以拉取技能。同步后技能将出现在列表中。", + "urlPlaceholder": "https://github.com/user/repo 或 git@github.com:user/repo.git", + "noRepos": "未配置 Git 仓库。请在上方添加。", + "disabled": "已禁用", + "removeRepo": "移除仓库" + }, + "card": { + "noDescription": "无描述", + "readOnly": "只读", + "editTitle": "编辑技能", + "deleteTitle": "删除技能", + "exportTitle": "导出为 .tar.gz" + }, + "empty": { + "title": "未找到技能", + "text": "创建或导入一个技能以开始。", + "noPersonal": "您还没有任何技能。" + }, + "sections": { + "yourSkills": "您的技能", + "otherUsersSkills": "其他用户的技能" + }, + "deleteDialog": { + "title": "删除技能", + "message": "删除技能 \"{{name}}\"?此操作无法撤销。", + "confirm": "删除" + }, + "removeRepoDialog": { + "title": "移除 Git 仓库", + "message": "移除此 Git 仓库?其中的技能将不再可用。", + "confirm": "移除" + }, + "toasts": { + "loadFailed": "加载技能失败", + "deleted": "技能 \"{{name}}\" 已删除", + "deleteFailed": "删除技能失败", + "exported": "技能 \"{{name}}\" 已导出", + "exportFailed": "导出失败", + "imported": "已从 \"{{file}}\" 导入技能", + "importFailed": "导入失败", + "loadReposFailed": "加载 Git 仓库失败", + "repoAdded": "Git 仓库已添加并正在同步", + "addRepoFailed": "添加仓库失败", + "synced": "仓库已同步", + "syncFailed": "同步失败", + "toggled": "仓库已切换", + "toggleFailed": "切换失败", + "removed": "仓库已移除", + "removeFailed": "移除失败" + } +} diff --git a/core/http/react-ui/scripts/translate-locales.mjs b/core/http/react-ui/scripts/translate-locales.mjs new file mode 100644 index 000000000000..d9aefc90fae5 --- /dev/null +++ b/core/http/react-ui/scripts/translate-locales.mjs @@ -0,0 +1,180 @@ +#!/usr/bin/env node +// Bootstrap non-English locale JSON files from the English source. +// +// Usage: +// # Fill missing keys in non-English locales by copying the English value +// # (placeholder — translators / community can refine afterwards). +// node scripts/translate-locales.mjs --copy +// +// # Translate via OpenAI (default provider). +// OPENAI_API_KEY=sk-... node scripts/translate-locales.mjs --translate +// +// # Translate via Anthropic. +// ANTHROPIC_API_KEY=sk-ant-... node scripts/translate-locales.mjs --translate --provider=anthropic +// +// # Dry-run to see what would change. +// node scripts/translate-locales.mjs --translate --dry-run +// +// Behavior: +// - Reads public/locales/en/*.json as source of truth. +// - For each other locale (it, es, de, zh-CN), opens the matching file +// (or creates it). Walks the source object; for each leaf string: +// * If the target already has a non-empty translation, leave it. +// * If --copy mode, fill with the English value. +// * If --translate mode, send the value to the LLM with the key path +// and locale name as context. +// - Writes the updated file with sorted keys, 2-space indent. +// +// The script is idempotent: existing translations are preserved unless +// --overwrite is passed. Run it whenever new keys are added in en/. + +import { readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync } from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const ROOT = join(__dirname, '..') +const LOCALES_DIR = join(ROOT, 'public', 'locales') +const SOURCE_LOCALE = 'en' +const TARGET_LOCALES = ['it', 'es', 'de', 'zh-CN'] + +const LANGUAGE_NAMES = { + it: 'Italian', + es: 'Spanish', + de: 'German', + 'zh-CN': 'Simplified Chinese', +} + +const argv = process.argv.slice(2) +const args = new Set(argv) +const MODE = args.has('--translate') ? 'translate' : 'copy' +const DRY_RUN = args.has('--dry-run') +const OVERWRITE = args.has('--overwrite') +const providerArg = argv.find(a => a.startsWith('--provider='))?.split('=')[1] +const PROVIDER = providerArg || (process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY ? 'anthropic' : 'openai') + +function readJson(path) { + return JSON.parse(readFileSync(path, 'utf8')) +} + +function writeJson(path, data) { + if (DRY_RUN) { + console.log(`[dry-run] would write ${path}`) + return + } + const dir = dirname(path) + if (!existsSync(dir)) mkdirSync(dir, { recursive: true }) + writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf8') +} + +function listNamespaces() { + return readdirSync(join(LOCALES_DIR, SOURCE_LOCALE)) + .filter((f) => f.endsWith('.json')) + .map((f) => f.replace(/\.json$/, '')) +} + +async function translateString(value, locale, keyPath) { + if (MODE === 'copy') return value + const language = LANGUAGE_NAMES[locale] || locale + const prompt = `Translate the following UI string from English to ${language}. +Preserve {{interpolation}} placeholders exactly. Preserve trailing punctuation +and ellipses. Do not add quotes around the result. Reply with the translation only. + +Key: ${keyPath} +String: ${value}` + + if (PROVIDER === 'anthropic') { + const apiKey = process.env.ANTHROPIC_API_KEY + if (!apiKey) throw new Error('ANTHROPIC_API_KEY required for --provider=anthropic') + const res = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + }, + body: JSON.stringify({ + model: process.env.TRANSLATE_MODEL || 'claude-haiku-4-5-20251001', + max_tokens: 1024, + system: 'You are a professional UI string translator. Reply with the translation only, no preamble.', + messages: [{ role: 'user', content: prompt }], + }), + }) + if (!res.ok) { + const body = await res.text() + throw new Error(`Anthropic API ${res.status}: ${body}`) + } + const data = await res.json() + return (data.content?.[0]?.text || '').trim() + } + + const apiKey = process.env.OPENAI_API_KEY + if (!apiKey) throw new Error('OPENAI_API_KEY required for --provider=openai') + const res = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + model: process.env.TRANSLATE_MODEL || 'gpt-4o-mini', + temperature: 0.2, + messages: [ + { role: 'system', content: 'You are a professional UI string translator.' }, + { role: 'user', content: prompt }, + ], + }), + }) + if (!res.ok) { + const body = await res.text() + throw new Error(`OpenAI API ${res.status}: ${body}`) + } + const data = await res.json() + return data.choices[0].message.content.trim() +} + +async function walk(source, target, locale, prefix = '') { + for (const key of Object.keys(source)) { + const path = prefix ? `${prefix}.${key}` : key + const sv = source[key] + if (sv && typeof sv === 'object' && !Array.isArray(sv)) { + target[key] = target[key] && typeof target[key] === 'object' ? target[key] : {} + await walk(sv, target[key], locale, path) + } else if (typeof sv === 'string') { + const existing = target[key] + const hasTranslation = typeof existing === 'string' && existing.trim().length > 0 + if (hasTranslation && !OVERWRITE) continue + try { + target[key] = await translateString(sv, locale, path) + process.stdout.write('.') + } catch (err) { + process.stdout.write('!') + console.error(`\n failed at ${locale}/${path}: ${err.message}`) + target[key] = existing ?? '' + } + } + } +} + +async function main() { + const namespaces = listNamespaces() + console.log(`mode=${MODE} provider=${PROVIDER} locales=${TARGET_LOCALES.join(',')} namespaces=${namespaces.join(',')}`) + for (const ns of namespaces) { + const sourcePath = join(LOCALES_DIR, SOURCE_LOCALE, `${ns}.json`) + const source = readJson(sourcePath) + for (const locale of TARGET_LOCALES) { + const targetPath = join(LOCALES_DIR, locale, `${ns}.json`) + const target = existsSync(targetPath) ? readJson(targetPath) : {} + process.stdout.write(`${locale}/${ns} `) + await walk(source, target, locale) + process.stdout.write('\n') + writeJson(targetPath, target) + } + } + console.log('done.') +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/core/http/react-ui/src/App.css b/core/http/react-ui/src/App.css index b0448e3a3040..6e6a155dad1d 100644 --- a/core/http/react-ui/src/App.css +++ b/core/http/react-ui/src/App.css @@ -536,6 +536,100 @@ border-color: var(--color-primary-border); } +/* Language switcher */ +.language-switcher { + position: relative; + display: inline-flex; +} +.language-switcher-trigger { + display: inline-flex; + align-items: center; + gap: 6px; +} +.language-switcher-code { + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.04em; +} +.sidebar.collapsed .language-switcher-code { + display: none; +} +.language-switcher-menu { + position: absolute; + bottom: calc(100% + 8px); + left: 0; + min-width: 160px; + background: var(--color-bg-elevated, var(--color-bg-secondary)); + border: 1px solid var(--color-border-subtle); + border-radius: var(--radius-md); + box-shadow: var(--shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.18)); + list-style: none; + margin: 0; + padding: 4px; + z-index: 1000; +} +.sidebar.collapsed .language-switcher-menu { + left: calc(100% + 8px); + bottom: 0; +} +.language-switcher-menu li { + margin: 0; +} +.language-switcher-option { + width: 100%; + display: flex; + align-items: center; + gap: 8px; + background: transparent; + border: 0; + padding: var(--spacing-xs) var(--spacing-sm); + color: var(--color-text-secondary); + cursor: pointer; + border-radius: var(--radius-sm); + font-size: 0.875rem; + text-align: left; +} +.language-switcher-option:hover { + background: var(--color-bg-tertiary, rgba(255, 255, 255, 0.04)); + color: var(--color-text-primary); +} +.language-switcher-option.active { + color: var(--color-primary); +} +.language-switcher-flag { + font-weight: 600; + font-size: 0.7rem; + width: 22px; + letter-spacing: 0.04em; +} +.language-switcher-name { + flex: 1; +} +.language-switcher-check { + font-size: 0.75rem; +} + +/* App boot fallback (rendered while initial i18n namespaces load) */ +.app-boot-spinner { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: var(--color-bg-primary, #111); +} +.app-boot-spinner-dot { + width: 36px; + height: 36px; + border-radius: 50%; + border: 3px solid var(--color-border-subtle, rgba(255, 255, 255, 0.15)); + border-top-color: var(--color-primary, #4f8cff); + animation: app-boot-spin 0.8s linear infinite; +} +@keyframes app-boot-spin { + to { transform: rotate(360deg); } +} + /* Operations bar */ .operations-bar { background: var(--color-bg-secondary); diff --git a/core/http/react-ui/src/App.jsx b/core/http/react-ui/src/App.jsx index daff71e33867..73a59f81eb00 100644 --- a/core/http/react-ui/src/App.jsx +++ b/core/http/react-ui/src/App.jsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from 'react' import { Outlet, useLocation, useNavigate } from 'react-router-dom' +import { useTranslation } from 'react-i18next' import Sidebar from './components/Sidebar' import OperationsBar from './components/OperationsBar' import { ToastContainer, useToast } from './components/Toast' @@ -22,6 +23,7 @@ export default function App() { const { theme, toggleTheme } = useTheme() const { authEnabled, user } = useAuth() const branding = useBranding() + const { t } = useTranslation('nav') const hamburgerRef = useRef(null) const isChatRoute = location.pathname.match(/\/chat(\/|$)/) || location.pathname.match(/\/agents\/[^/]+\/chat/) @@ -67,7 +69,8 @@ export default function App() { ].filter(Boolean).join(' ') const showAvatar = authEnabled && user - const accountLabel = user?.name || user?.email || 'Account' + const accountLabel = user?.name || user?.email || t('account') + const themeToggleLabel = theme === 'dark' ? t('switchToLightMode') : t('switchToDarkMode') return (
@@ -83,7 +86,7 @@ export default function App() { ref={hamburgerRef} className="hamburger-btn" onClick={() => setSidebarOpen(true)} - aria-label="Open menu" + aria-label={t('openMenu')} aria-expanded={sidebarOpen} aria-controls="app-sidebar" > @@ -95,8 +98,8 @@ export default function App() { type="button" className="mobile-header-btn" onClick={toggleTheme} - aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`} - title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`} + aria-label={themeToggleLabel} + title={themeToggleLabel} >