feat: load plugin frontends at runtime and remove plugin ids from the shell

This commit is contained in:
LukeGus committed 2026-09-22 20:15:52 -05:00
1 parent 49f0286a14
commit e88d5a52e3
721 files changed
+54265 -44416

No files matched your search

+12 -1
View File
@@ -161,7 +161,7 @@ http {
root /app/html;
index index.html index.htm;
expires off;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; connect-src 'self' http: https: ws: wss:; media-src 'self' data: blob: http: https:; worker-src 'self' blob:; frame-src http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'sha256-AOzhY2+y5QtkFA+y4ASX7ouU+zPyffKUNFrJiRrW6nw='; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; connect-src 'self' http: https: ws: wss:; media-src 'self' data: blob: http: https:; worker-src 'self' blob:; frame-src http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
@@ -321,6 +321,17 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Plugin frontend bundles, stylesheets and locales. Public like
# /assets; the backend picks the cache policy from the ?v= query.
location ^~ /plugin-assets/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Every plugin's HTTP routes. One block, no per-plugin entries: a
# plugin serves at /plugin-api/<id>/ on the main backend and never
# gets a port or a block of its own. Buffering is off so SSE streams
+12 -1
View File
@@ -142,7 +142,7 @@ http {
root /app/html;
index index.html index.htm;
expires off;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; connect-src 'self' http: https: ws: wss:; media-src 'self' data: blob: http: https:; worker-src 'self' blob:; frame-src http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'sha256-AOzhY2+y5QtkFA+y4ASX7ouU+zPyffKUNFrJiRrW6nw='; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; connect-src 'self' http: https: ws: wss:; media-src 'self' data: blob: http: https:; worker-src 'self' blob:; frame-src http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
@@ -301,6 +301,17 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Plugin frontend bundles, stylesheets and locales. Public like
# /assets; the backend picks the cache policy from the ?v= query.
location ^~ /plugin-assets/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Every plugin's HTTP routes. One block, no per-plugin entries: a
# plugin serves at /plugin-api/<id>/ on the main backend and never
# gets a port or a block of its own. Buffering is off so SSE streams
+8 -8
View File
@@ -129,21 +129,21 @@ export default tseslint.config([
},
},
{
// The shell still imports plugin components directly (the terminal tab,
// the Guacamole app, the Proxmox dialog). There is no frontend plugin
// loader yet, so there is nothing to move them to: A7 builds the app
// object that registers these surfaces and this becomes an error.
files: ["src/ui/**/*.{ts,tsx}"],
// The shell never imports a plugin. It reaches plugins only through the
// registries their frontends fill via the app object (A7). This block
// also carries the backend rule for src/ui files, because flat config
// replaces a rule's options rather than merging them.
files: ["src/ui/**/*.{ts,tsx}", "src/main.tsx"],
ignores: ["src/ui/tests/**"],
rules: {
"no-restricted-imports": [
"warn",
"error",
{
patterns: [
{
group: ["**/plugins/*/src/frontend/**"],
group: ["**/plugins/*/src/**", "**/plugins/*/dist/**"],
message:
"The shell should not import plugin components directly. A7 replaces these with registrations through the app object.",
"The shell does not import plugins. Register the surface through the app object and read it from a registry.",
},
],
},
+1 -1
View File
@@ -19,7 +19,7 @@
"postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-guacamole-common-js.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs && node scripts/patch-node-pty.cjs && node scripts/patch-xterm-android-ime.cjs && node scripts/patch-ssh2-agent.cjs",
"prepare": "husky || true",
"prebuild": "node scripts/write-electron-build-info.cjs",
"lint": "node scripts/generate-dialect-schema.cjs --check && npm run plugin:validate-manifest && eslint . && node scripts/check-plugin-boundaries.cjs",
"lint": "node scripts/generate-dialect-schema.cjs --check && npm run plugin:validate-manifest && eslint . && node scripts/check-plugin-boundaries.cjs && node scripts/check-importmap-csp.cjs && node scripts/check-shell-plugin-ids.cjs",
"lint:fix": "eslint --fix .",
"type-check": "tsc -b --force",
"test": "vitest run",
+179 -21
View File
@@ -29,7 +29,8 @@ dependencies. Entry points:
| `@termix/plugin-sdk/backend` | `PluginContext`, `definePlugin()`, `PluginCapabilityError` |
| `@termix/plugin-sdk/db` | `defineTable()`, the column builders, the legacy-table map |
| `@termix/plugin-sdk/ddl` | The per-dialect DDL emitter, shared with the CLI |
| `@termix/plugin-sdk/frontend` | The `app` object types (stubs until A7) |
| `@termix/plugin-sdk/frontend` | The `app` object types, the hooks, `invokeAction()` |
| `@termix/plugin-sdk/ui` | Core's shared components, a fixed public list (see UI) |
| `@termix/plugin-sdk/manifest` | Manifest types, validation, the JSON schema |
| `@termix/plugin-sdk/capabilities` | The capability catalog |
| `@termix/plugin-sdk/settings` | Settings field types and the shared value validator |
@@ -271,11 +272,145 @@ allowlist (`CORE_SETTINGS_ALLOWLIST`) rather than the whole settings table.
### 8. UI
A plugin frontend is a built ESM bundle loaded at runtime by the core loader.
It registers everything through the `app` object: rail items, panels, tabs,
host editor sections, host actions and badges, context menu items, palette
entries, dashboard cards, homepage widgets, settings components, action slots,
login/2FA UI, SSH auth editors. The shell contains no plugin ids. **A7.**
A plugin frontend is a built ESM bundle, `dist/frontend.js`, exporting
`activate(app)` and optionally `deactivate()`. The shell has no plugin ids and
no imports from `plugins/`; everything a plugin shows goes through the `app`
object. `scripts/check-shell-plugin-ids.cjs` (run by lint) fails on a quoted
plugin id in `src/ui` outside tests, apart from the entries in
`scripts/shell-plugin-id-allowlist.json`, each with a reason (host protocol
data like `rdp`, which Phase B moves).
#### The loader
`src/ui/plugin-host/loader.ts` runs after login and again whenever plugin
state may have changed: the `termix:plugins-changed` window event (the admin
toggle fires it) and window focus, throttled to 30 seconds. There is no push
channel yet. Each pass:
1. Fetches `GET /plugins` (`frontend`, `css`, `assetVersion`, `locales`,
`dependencies` and `contributes` per plugin).
2. Loads every plugin's locales into the i18next namespace `<id>`, enabled or
not, because the role editor and the plugin list need disabled plugins'
titles.
3. Orders enabled plugins by dependencies. A plugin whose hard dependency is
missing or off is skipped.
4. For each plugin with a frontend: injects `frontend.css`, imports
`/plugin-assets/<id>/frontend.js?v=<assetVersion>` and calls `activate(app)`.
In dev the workspace plugins are imported from
`plugins/<id>/src/frontend/index.tsx` through Vite, so HMR works.
5. Deactivates plugins that went away: runs `deactivate()` then every
disposer. No reload.
A plugin that throws while importing or activating is marked failed, its
partial registrations are disposed, and the rest of the app carries on. The
same `assetVersion` is not retried until it changes.
Anonymous shared-session and collab pages have no session. They load only
plugins whose manifest sets `contributes.guest: true`, from the public
`GET /plugins/public`, and `app.guest` is true there.
#### Shared modules and the import map
A bundle keeps `react`, `react-dom`, `react-dom/client`, `react/jsx-runtime`,
`i18next`, `react-i18next`, `sonner`, `@termix/plugin-sdk/frontend`,
`@termix/plugin-sdk/ui` and `@termix/legacy-core/*` as bare imports. Core's
Vite build (`scripts/vite-plugin-termix-plugins.mjs`) emits one entry per
shared module, writes a stable unhashed shim for each at
`dist/shared/<name>.js`, and injects an import map into `index.html` mapping
the bare names to those shims. In dev the map points at Vite's virtual
modules instead. The map is inline, so both nginx configs allow it by hash in
`script-src`; `scripts/check-importmap-csp.cjs` (run by lint) fails when the
hash is out of date and `--write` updates it. The map changes only when the
list of shared modules does, which includes the legacy-core modules plugins
import.
Electron loads `index.html` from `file://`. The map uses relative URLs, and
plugin bundles come from the embedded backend
(`http://localhost:30001/plugin-assets/...`), which is why `/plugin-assets`
sends `Access-Control-Allow-Origin: *`.
#### Serving bundles
`GET /plugin-assets/<id>/<path>` serves the directory holding a plugin's
frontend bundle and its `locales/` directory, nothing else: never
`backend.js`, never a path outside those two, only `.js`, `.css`, `.json` and
`.map`. It is unauthenticated like `/assets`, since `import()` cannot send the
bearer header and bundles are not secret. With `?v=` the response is cached
as immutable; without it, `no-cache`. nginx has a `^~ /plugin-assets/` block.
#### The app object
Every `register*` call returns a disposer and is also disposed automatically
when the plugin deactivates. Every registered component is wrapped in the
plugin's scope (so the hooks know which plugin they belong to), an error
boundary and Suspense.
| Member | What it does |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `registerRailItem` | A rail button. Hideable items show in Appearance > Sidebar > Navigation. The id must be a declared view |
| `registerPanel` | A rail panel, id in `contributes.panels` or `contributes.tabs` |
| `registerTab` | A tab type, id in `contributes.tabs`. Options cover persistence, layouts, singletons and host needs |
| `registerHostEditorSection` | A host editor tab in the top strip or the SSH group, with `form`, `setField` and `updateForm` |
| `registerHostAction` | A connect or open action on a host: sidebar row, palette, dashboard, default connect |
| `registerHostBadge`, `registerHostContextMenuItem` | Host row badges and context menu entries |
| `registerPaletteEntry` | A global or per-host command palette entry |
| `registerDashboardCard` | A dashboard card, id in `contributes.dashboardCards` |
| `registerHomepageWidget` | A homepage widget, with an optional edit form |
| `registerSettingsComponent` | A component a `type: "custom"` settings field names |
| `registerAction`, `declareActionSlot` | Frontend actions and the slots a plugin owns |
| `registerSlotContribution`, `invokeAction` | Fill a slot with a `button` or a `component`, with an optional `when`; call an action and get its result |
| `registerSshAuthEditor` | An SSH auth method's editor in the host editor |
| `registerLoginMethod`, `registerSecondFactorUI` | Typed now, wired by A8 |
| `api`, `wsUrl(path)` | axios on `/plugin-api/<id>/` and the plugin's WebSocket URL |
| `tabs.open`, `getLayout`, `applyLayout`, `onChange` | Tab control, used by workspaces |
| `guest`, `info`, `onDispose` | Guest mode flag, plugin info, extra cleanup |
Hooks: `useTranslation` (the plugin's namespace), `usePermission` (a short
name resolves to `<id>.<name>`), `useSettings`, `useHost`, `useHosts`,
`useCurrentUser`, `useTheme`, `useToast`, `usePluginApi`, `useTabs`. The SDK
has no runtime dependencies: the hooks delegate to a host bridge core installs.
Slots core owns: `terminal.toolbar`, `terminal.dock`, `terminal.overlay`
(declared by ssh-terminal), `onboarding.steps`, `onboarding.features`,
`onboarding.workflow`, `hosts.importMenu`, `hosts.panel`, `proxmox.hostEditor`
and `session.remoteDisplay`. Cross-plugin frontend calls go through actions:
`automations.list`, `fleets.list`, `session.remoteDisplay.token`.
#### View ownership
Who owns a tab, panel or card comes from manifests, so it is known even for a
disabled plugin whose code never loaded. A saved tab or dashboard card whose
plugin is disabled, failed, still loading or not installed renders "This
needs the <name> plugin" (`PluginViewPlaceholder`) and is never dropped from
the saved layout. Tab restore waits for the first plugin pass, so a restored
tab does not race its plugin.
#### The `ui` entry
`@termix/plugin-sdk/ui` is public API, implemented by
`src/ui/plugin-host/sdk-ui.ts`. Adding to it is a contract change; removing
from it is a breaking one. Today it exports: alert, alert-dialog, badge,
button, card, checkbox, dialog, dropdown-menu, input, label, password-input,
select, select2, separator, switch, textarea, tooltip, section-card,
metric-card, charts, the card grid, `ConnectionScreen` and the connection
status helpers, `SnippetVariablesDialog`, `FullScreenAppWrapper`, the
connection log context, `TOTPDialog`, `SSHAuthDialog`, `WarpgateDialog`,
`useTabs`/`useTabsSafe`, `ActionSlot` and `ComponentSlot`. Publishing its
`.d.ts` for plugins outside this repo is a follow-up for the repo split.
#### Strings
A plugin's strings live in `plugins/<id>/locales/en.json` and load into the
`<id>` namespace, with core's `translation` as the fallback, so a plugin can
still use `common.*`. Manifest keys (`titleKey`, `labelKey`) resolve in the
plugin namespace; `pluginKey(id, key)` in core returns `<id>:<key>`.
Translations go to `plugins/<id>/locales/translated/<xx_YY>.json`, which is
where Crowdin writes them. Only `en.json` is edited by hand.
#### Type checking
`tsconfig.plugins.frontend.json` checks every plugin frontend and its tests
with bundler resolution, and is part of `npm run type-check`.
### 9. Cross-plugin use
@@ -314,8 +449,12 @@ capability, registry, service, secret and shutdown suites.
`@termix/plugin-sdk/testing` provides `createMockCtx()`, which enforces
capabilities the way the runtime does, `createFakeContext()` for tests that do
not care about the gates, and `renderWithApp()` for frontend tests (**A7**
finishes it).
not care about the gates, and `renderWithApp(plugin, options)` for frontend
tests. `renderWithApp` activates the plugin against core's real registries,
records what it registered and the shell calls it made, renders any tab,
panel, card, host editor section, settings component or slot, and
`deactivate()` disposes it all. Every bundled plugin has a
`tests/frontend/activate.test.tsx` built on it.
### 12. Auth
@@ -339,8 +478,8 @@ plugins/<id>/
tsconfig.json extends @termix/plugin-sdk/tsconfig.plugin.json
vitest.config.ts one line, the SDK preset
src/backend/index.ts exports activate(ctx) and deactivate()
src/frontend/index.tsx exports activate(app) and deactivate() (A7)
locales/en.json English strings; Crowdin translates the rest
src/frontend/index.tsx exports activate(app) and deactivate()
locales/en.json English strings; Crowdin writes locales/translated/
migrations/{sqlite,postgres,mysql}/ owned tables, one .sql per dialect
migrations/snapshot.json diff basis for the generator
src/backend/tables.ts defineTable() definitions
@@ -545,17 +684,23 @@ Cross-field rules the JSON schema cannot express, checked by `parseManifest`:
appear in the plugin's own `contributes.permissions`. A dotted id is taken as
given, because the validator cannot know which other plugins exist.
`contributes.panels` and `contributes.dashboardCards` (each `{ id, titleKey,
icon? }`) declare the views a plugin owns besides its tabs; the app object
refuses to register a view the manifest does not declare. `contributes.guest`
opts a plugin into anonymous guest pages. Action contributions and slots
accept the kinds `button` and `component`.
`contributes.hostCapability`, `provides`, `requires`, `providesSecret` and
`requiresSecret` are carried forward from v1 unchanged because the service and
secret registries read them at activation. A6 and A7 reshape them.
secret registries read them at activation. Phase B reshapes them.
Removed in A5: `contributes.permissionGroup` and its `defaultForRole`, replaced
by `contributes.permissions`. A plugin no longer picks its own group name.
Removed in v2: `permissions` (replaced by `capabilities`), `sidecars`, the
`capabilities.{backend,frontend,electron,platforms}` object (replaced by the
`backend`/`frontend`/`platforms` fields), `contributes.dashboardCards`,
`contributes.settingsPanel`, `contributes.apiPrefix`, and
`backend`/`frontend`/`platforms` fields), the v1 shape of
`contributes.dashboardCards`, `contributes.settingsPanel`, `contributes.apiPrefix`, and
`process:transport-owner` (the marker for the tier that no longer exists).
---
@@ -767,11 +912,12 @@ and that is bundled into its output.
`ws`, `multer`, `cookie-parser`, `axios`, `jszip`, `guacamole-lite`,
`@anthropic-ai/sdk`, `drizzle-orm`.
**Frontend**: `react`, `react-dom`, `react/jsx-runtime`, `@termix/plugin-sdk`,
`i18next`, `react-i18next`, and the UI libraries core provides today
(`lucide-react`, `sonner`, `axios`, `cytoscape`, `react-cytoscapejs`,
`guacamole-common-js`, `react-xtermjs`, `@xterm/*`). **A7** serves these
through the SDK `ui` entry.
**Frontend**: `react`, `react-dom`, `react-dom/client`, `react/jsx-runtime`,
`i18next`, `react-i18next`, `sonner`, `@termix/plugin-sdk/frontend`,
`@termix/plugin-sdk/ui` and `@termix/legacy-core/*`, all resolved through the
import map (see UI). Everything else, including `lucide-react`, `axios`,
`cytoscape` and `guacamole-common-js`, is bundled into the plugin that uses
it.
The lists live in `packages/plugin-sdk/cli/lib/externals.mjs`.
@@ -787,6 +933,13 @@ and rewrites to the compiled output path (`../../../backend/backend/...`, or
so a source file at any nesting collapses to the same prefix. D1 deletes that
file.
Frontends do the same with `@/`: in the browser build, `@/x` and
`../src/ui/x` become the bare `@termix/legacy-core/ui/x` (or
`@termix/legacy-core/types/x`), which core's Vite build exposes as a shared
module so plugin and shell use one instance of each core module. The CLI
refuses the rewrite outside a Termix checkout. Every such import is counted
in the `plugin-frontend-to-core` direction below.
This is expected and temporary. The SDK does not yet expose what they need:
the SSH connection pool, host resolution, the repository layer, the shared
component library. D1 removes the debt once A3 through A8 have given them
@@ -798,7 +951,8 @@ What the lint fence enforces today, in `eslint.config.mjs`:
| ------------------------------------------------ | --------- | --------- | ---------- |
| Core importing a plugin backend | **Error** | 0 | - |
| A plugin backend importing frontend code or `@/` | **Error** | 0 | - |
| The shell importing plugin components | Warning | 16 files | A7 |
| The shell importing plugin code | **Error** | 0 | - |
| A plugin frontend importing core through `@/` | Warning | 109 files | D1 |
| A plugin importing core by relative path | Warning | 70 files | D1 |
| A plugin importing another plugin's source | Warning | 3 files | B18 |
@@ -831,5 +985,9 @@ Known specifics:
- Core feature servers that are not plugins yet still own ports: tunnel 30003,
file-manager 30004, dashboard 30006, tmux 30010, serial 30011 and homepage 30012. Each keeps its nginx block until its own Phase B step. No plugin owns
a port any more (A4).
- `src/ui/shell/pluginLoader.ts` still hardcodes plugin ids in
`BUILT_IN_TABS_BY_PLUGIN` and `applyFirstPartyActions`. A7 removes both.
- The terminal component and `TerminalTabContent` stay in core until the
terminal's Phase B step (session manager, split view and file-manager
callbacks). ssh-terminal registers them through the legacy alias.
- Host data columns (`enableDocker`, `enableRdp`, `guacamoleConfig` and so on)
stay in core types until Phase B moves them. Only UI branches on them left
the shell.
+18 -1
View File
@@ -17,6 +17,17 @@ const FRONTEND_ENTRIES = [
"src/frontend/index.js",
];
/** Whether the plugin sits in a Termix checkout, i.e. is a bundled plugin. */
function insideTermixCheckout(cwd) {
let dir = path.resolve(cwd);
for (;;) {
if (fs.existsSync(path.join(dir, "src", "ui", "plugin-host"))) return true;
const parent = path.dirname(dir);
if (parent === dir) return false;
dir = parent;
}
}
export async function build({ cwd }) {
const manifest = readManifest(cwd);
const pluginId = manifest.id ?? path.basename(cwd);
@@ -58,7 +69,13 @@ export async function build({ cwd }) {
sourcemap: true,
logLevel: "warning",
external: FRONTEND_EXTERNALS,
plugins: [legacyCoreImports({ pluginId, platform: "browser" })],
plugins: [
legacyCoreImports({
pluginId,
platform: "browser",
insideTermix: insideTermixCheckout(cwd),
}),
],
});
}
+9 -12
View File
@@ -25,24 +25,21 @@ export const BACKEND_EXTERNALS = [
"drizzle-orm/*",
];
/**
* Resolved by the page's import map to the shell's own copies, because these
* must be one instance: React, i18next, the toast store, and the SDK entries
* that share state with core. Anything else a plugin imports is bundled.
*/
export const FRONTEND_EXTERNALS = [
"react",
"react-dom",
"react/jsx-runtime",
"react-dom/client",
"@termix/plugin-sdk",
"@termix/plugin-sdk/*",
"i18next",
"react-i18next",
// Core-provided UI libraries. A7 serves these through the SDK ui entry.
"lucide-react",
"sonner",
"axios",
"cytoscape",
"react-cytoscapejs",
"guacamole-common-js",
"react-xtermjs",
"@xterm/*",
// Legacy: the shell's own modules, still reached by alias. A7 removes.
"@/*",
"@termix/plugin-sdk/frontend",
"@termix/plugin-sdk/ui",
// Legacy: the shell's own modules, see legacy-core-imports.mjs. D1 removes.
"@termix/legacy-core/*",
];
@@ -12,6 +12,8 @@
* Delete this file in D1, once every plugin reaches core through the SDK.
*/
import { legacyCoreSpecifier } from "./legacy-core-specifier.mjs";
// "../../../src/backend/" or "../../../../src/backend/", any depth.
const CORE_BACKEND = /^(?:\.\.\/)+src\/backend\//;
// Shared types. Mostly erased at compile, but automations imports a real
@@ -27,10 +29,36 @@ const CROSS_PLUGIN = /^(?:\.\.\/)+([a-z0-9-]+)\/(?:src\/)?backend\//;
const COMPILED_CORE = "../../../backend/backend/";
const COMPILED_TYPES = "../../../backend/types/";
export function legacyCoreImports({ pluginId, platform }) {
const CORE_ALIAS = /^@\//;
export function legacyCoreImports({ pluginId, platform, insideTermix = true }) {
return {
name: "legacy-core-imports",
setup(build) {
// The shell's own modules, reached as "@/..." or by relative path.
// Bundling them would give the plugin a second copy of the shell's
// contexts and API client, so each becomes a bare specifier the page's
// import map resolves to the shell's own module.
if (platform === "browser") {
const toLegacy = (args) => {
const specifier = legacyCoreSpecifier(args.path);
if (!specifier) return null;
if (!insideTermix) {
return {
errors: [
{
text: `${pluginId}: "${args.path}" reaches Termix core. Only plugins built inside a Termix checkout may; use @termix/plugin-sdk instead.`,
},
],
};
}
return { path: specifier, external: true };
};
build.onResolve({ filter: CORE_ALIAS }, toLegacy);
build.onResolve({ filter: CORE_UI }, toLegacy);
build.onResolve({ filter: CORE_TYPES }, toLegacy);
}
build.onResolve({ filter: CORE_BACKEND }, (args) => ({
path: args.path.replace(CORE_BACKEND, COMPILED_CORE),
external: true,
@@ -41,16 +69,6 @@ export function legacyCoreImports({ pluginId, platform }) {
external: true,
}));
// The shell's own modules. Only plugins/docker's frontend entry reaches
// core this way; every other frontend uses the "@/" alias, which is
// externalized by name. A7 removes both.
if (platform === "browser") {
build.onResolve({ filter: CORE_UI }, (args) => ({
path: args.path,
external: true,
}));
}
build.onResolve({ filter: CROSS_PLUGIN }, (args) => {
const [, other] = CROSS_PLUGIN.exec(args.path);
if (other === "src" || other === pluginId) return null;
@@ -0,0 +1,62 @@
/**
* Legacy core modules a bundled plugin frontend still imports.
*
* Bundled plugin frontends predate the SDK and import the shell's own modules
* as "@/..." (or, in a couple of places, by relative path into src/). Bundling
* those would give the plugin a second copy of the shell's contexts and API
* client, so the CLI rewrites each one to a bare specifier under
* "@termix/legacy-core/" and core's build exposes exactly that module through
* the import map. Both sides use this one normalizer so they cannot disagree.
*
* Only plugins built inside a Termix checkout may do this. D1 deletes it.
*/
export const LEGACY_CORE_PREFIX = "@termix/legacy-core/";
const EXTENSION = /\.(tsx|ts|jsx|js|mjs)$/;
function clean(rest) {
return rest.replace(EXTENSION, "").replace(/\/+$/, "");
}
/**
* "@/components/button.tsx" -> "@termix/legacy-core/ui/components/button"
* "@/types" -> "@termix/legacy-core/types"
* "@/types/connection-log" -> "@termix/legacy-core/types/connection-log"
* "../../../src/ui/x.tsx" -> "@termix/legacy-core/ui/x"
* Anything else -> null
*/
export function legacyCoreSpecifier(specifier) {
if (specifier.startsWith("@/")) {
const rest = clean(specifier.slice(2));
if (rest === "types" || rest.startsWith("types/")) {
return LEGACY_CORE_PREFIX + rest;
}
return `${LEGACY_CORE_PREFIX}ui/${rest}`;
}
const relative = /^(?:\.\.\/)+src\/(ui|types)(\/.*)?$/.exec(specifier);
if (relative) {
return LEGACY_CORE_PREFIX + clean(`${relative[1]}${relative[2] ?? ""}`);
}
return null;
}
/**
* Every value import in a source file that names a legacy core module.
* Type-only imports are skipped: they are erased and never reach the map.
*/
export function findLegacyCoreImports(source) {
const found = new Set();
const patterns = [
/^\s*(?:import|export)\s+(?!type\s)[^'";]*?\sfrom\s+["']([^"']+)["']/gm,
/^\s*import\s+["']([^"']+)["']/gm,
/import\(\s*["']([^"']+)["']\s*\)/g,
];
for (const pattern of patterns) {
for (const match of source.matchAll(pattern)) {
const normalized = legacyCoreSpecifier(match[1]);
if (normalized) found.add(normalized);
}
}
return found;
}
@@ -263,6 +263,33 @@
}
}
},
"guest": { "type": "boolean" },
"panels": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "titleKey"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"titleKey": { "type": "string", "minLength": 1 },
"icon": { "type": "string", "minLength": 1 }
}
}
},
"dashboardCards": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "titleKey"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"titleKey": { "type": "string", "minLength": 1 },
"icon": { "type": "string", "minLength": 1 }
}
}
},
"actions": {
"type": "array",
"items": {
@@ -296,7 +323,7 @@
},
"kind": {
"type": "string",
"enum": ["button"]
"enum": ["button", "component"]
}
}
}
@@ -317,7 +344,7 @@
"minItems": 1,
"items": {
"type": "string",
"enum": ["button"]
"enum": ["button", "component"]
}
},
"descriptionKey": {
+614 -11
View File
@@ -1,29 +1,478 @@
/**
* The frontend contract: what a plugin's frontend entry receives.
* The frontend contract: what a plugin's frontend entry receives, and the
* hooks its components use.
*
* Stubs only. A7 builds the real loader and fills these in, at which point a
* plugin registers every surface it contributes through this object and the
* shell stops carrying plugin ids of its own.
* A plugin frontend is `src/frontend/index.tsx` exporting `activate(app)` and
* optionally `deactivate()`. Everything it contributes goes through `app`, and
* everything registered there is removed when the plugin is disabled, without
* a page reload.
*
* This module has no runtime dependencies. The hooks delegate to a host that
* core installs at startup (`__setPluginHost`), because React, i18next and the
* shell's state all live in core and must be one instance across the shell
* and every plugin bundle.
*/
import type { ComponentType, ReactNode, Ref } from "react";
import type { PluginManifest } from "./manifest.js";
export type Disposer = () => void;
/** A Lucide icon or anything with the same props. */
export type IconComponent = ComponentType<{
className?: string;
size?: number | string;
strokeWidth?: number | string;
}>;
/**
* A host as the shell holds it. Only the identifying fields are typed; the
* rest of the record is passed through as the shell has it, so a plugin
* that needs a feature column reads it by name.
*/
export interface PluginHostRecord {
id: string;
name: string;
ip: string;
port: number;
username?: string;
folder?: string;
tags?: string[];
[key: string]: unknown;
}
/** An open tab as the shell holds it. `data` is the plugin's own payload. */
export interface PluginTabRecord {
id: string;
type: string;
label: string;
host?: PluginHostRecord;
data?: Record<string, unknown>;
[key: string]: unknown;
}
export interface OpenTabOptions {
label?: string;
/** Open a second tab even when one for this host and type exists. */
forceNewTab?: boolean;
/** Plugin payload, stored on the tab and restored with it. */
data?: Record<string, unknown>;
}
/** A saved arrangement of tabs, used by workspaces. Opaque to plugins. */
export interface ShellLayout {
version: number;
[key: string]: unknown;
}
/** What a plugin may ask of the shell. */
export interface ShellApi {
openTab: (
host: PluginHostRecord | null,
type: string,
options?: OpenTabOptions,
) => void;
openSingletonTab: (type: string, options?: OpenTabOptions) => void;
closeTab: (tabId: string) => void;
renameTab: (tabId: string, label: string) => void;
openFileInEditor: (host: PluginHostRecord, path: string) => void;
openFileManager: (host: PluginHostRecord, path?: string) => void;
openTerminalTab: (host: PluginHostRecord, path?: string) => void;
/** Opens a rail view in the left sidebar. */
openRailView: (id: string) => void;
/** Closes a rail view wherever it is shown. */
closeRailView: (id: string) => void;
}
export interface TabsApi extends Pick<
ShellApi,
"openTab" | "openSingletonTab" | "closeTab"
> {
/** The current tabs and split layout, or null before the shell mounts. */
getLayout: () => ShellLayout | null;
/**
* Replaces the open tabs with a saved layout. `name` labels a restored
* split. Resolves with the tabs that could not be reopened, e.g. because
* their host was deleted.
*/
applyLayout: (
layout: ShellLayout,
options?: { name?: string },
) => Promise<{ skipped: string[] }>;
/** Called whenever tabs open, close or move. */
onChange: (listener: () => void) => Disposer;
/** Called once the shell has restored its tabs after login. */
onReady: (listener: () => void) => Disposer;
}
// ---------------------------------------------------------------------------
// Registrations
// ---------------------------------------------------------------------------
export interface RailItemContribution {
/** Must be declared in manifest contributes.tabs or contributes.panels. */
id: string;
icon: IconComponent;
titleKey: string;
/** "panel" opens the left sidebar, "tab" opens a tab. Default "panel". */
kind?: "panel" | "tab";
/** Users may hide it from Appearance > Sidebar > Navigation. Default true. */
hideable?: boolean;
/** Can also open as a full-width tab. */
promotable?: boolean;
/** Can open in the right dock. */
rightDockable?: boolean;
/** Shown on the mobile bar's primary row. */
mobilePrimary?: boolean;
/** Desktop app only. */
electronOnly?: boolean;
separatorAfter?: boolean;
/** Hidden without being unregistered, e.g. while a feature is switched off. */
hidden?: boolean;
/** Place it after this rail id rather than at the end. */
after?: string;
/** Lower sorts first among plugin items. */
order?: number;
}
export interface PanelProps {
/** Whether the panel is the one currently shown. */
active: boolean;
shell: ShellApi;
/** Tells the shell the panel is editing, which widens the sidebar. */
setEditing: (editing: boolean) => void;
/** Type of the focused tab, if any. */
activeTabType?: string;
/** Where the panel is rendered. */
placement: "left" | "right" | "tab";
}
export interface PanelOptions {
/** Keep the panel mounted after the user switches away. */
keepMounted?: boolean;
}
export interface TabProps {
tab: PluginTabRecord;
host?: PluginHostRecord;
/** The host in the shape the connection APIs take. */
sshHost?: Record<string, unknown>;
label: string;
isVisible: boolean;
isFocusedPane: boolean;
/** Forward to a component exposing `refresh()` or `disconnect()`. */
handleRef: Ref<unknown>;
shell: ShellApi;
}
export interface StandaloneViewProps {
hostId?: string;
view: string;
params: URLSearchParams;
}
export interface TabOptions {
icon?: IconComponent;
/** Label for singleton tabs and anywhere the type is named. */
titleKey?: string;
/** Needs a host; renders `noHostMessageKey` without one. */
requiresHost?: boolean;
noHostMessageKey?: string;
/** Reopened after login. */
persistent?: boolean;
/** One tab of this type at a time, keyed by type. */
singleton?: boolean;
/** A live session: asks before closing and can be refreshed. */
session?: boolean;
/** Restorable without a host. */
hostless?: boolean;
/** Decides whether a saved tab for this host may be restored. */
restore?: (host: PluginHostRecord) => boolean;
/** Recent-activity types that open this tab. */
activityTypes?: string[];
/** Rendered for `?view=<type>` full-screen links. */
standalone?: ComponentType<StandaloneViewProps>;
/** Extra `?view=` names that also open `standalone`. */
standaloneViews?: string[];
/** Wrap in the readable-width frame used by panels opened as tabs. */
panelFrame?: boolean;
/** False keeps the tab out of saved layouts and workspaces. Default true. */
inLayouts?: boolean;
/** Warms the tab's code before it is opened. */
preload?: () => Promise<unknown>;
}
export interface HostEditorSectionProps {
// The editor form is a plain object; a section reads the fields it owns.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
form: any;
setField: (key: string, value: unknown) => void;
/** Sets several fields at once, computed from the latest form. */
updateForm: (
patch: (form: Record<string, unknown>) => Record<string, unknown>,
) => void;
host?: PluginHostRecord;
credentials?: unknown[];
snippets?: unknown[];
/** Which connection protocols are switched on for this host. */
protocols: Record<string, boolean>;
}
export interface HostEditorSectionContribution {
id: string;
/** "top" sits in the main tab strip, "ssh" under the SSH group. */
group: "top" | "ssh";
titleKey: string;
icon?: IconComponent;
/**
* Position among the group's tabs, core ones included. Top: General 0,
* SSH 10. SSH group: General 0, Terminal 10, Tunnels 20, Files 60.
*/
order?: number;
/** Whether to offer the tab, from the host's enabled protocols. */
visible?: (protocols: Record<string, boolean>) => boolean;
component: ComponentType<HostEditorSectionProps>;
}
export interface HostActionContribution {
id: string;
titleKey: string;
icon: IconComponent;
/**
* "connect" is a way to open a session (terminal, RDP) and is offered as a
* host's default action by priority. "open" opens a tool for the host.
*/
kind: "connect" | "open";
/** Higher wins when choosing a host's default connect action. */
priority?: number;
/** Opened with shell.openTab when `run` is absent. */
tabType?: string;
when: (host: PluginHostRecord) => boolean;
run?: (host: PluginHostRecord, shell: ShellApi) => void;
/** Shows a quick button on the host row. Default true. */
tray?: boolean;
/** `?view=` name for "Copy link". */
copyUrlView?: string;
/** Where a host overview (the dashboard's host status list) sends a click. */
overview?: boolean;
/**
* Position in the host row. Core: Files 20, Tunnel 40, Tmux 70. Connect
* actions default to 100 and sit after a separator.
*/
order?: number;
/** A label worked out per host, e.g. a single endpoint's name. */
label?: (host: PluginHostRecord) => string | undefined;
/** Several targets: two or more become a picker, one runs directly. */
items?: (host: PluginHostRecord) => {
id: string;
label: string;
run: (host: PluginHostRecord, shell: ShellApi) => void;
}[];
}
export interface HostBadgeContribution {
id: string;
when: (host: PluginHostRecord) => boolean;
component: ComponentType<{ host: PluginHostRecord }>;
}
export interface HostContextMenuItemContribution {
id: string;
titleKey: string;
icon?: IconComponent;
when: (host: PluginHostRecord) => boolean;
run: (host: PluginHostRecord, shell: ShellApi) => void;
}
export interface PaletteEntryContribution {
id: string;
titleKey: string;
icon?: IconComponent;
keywords?: string[];
/** "global" entries stand alone, "host" entries appear under a host. */
scope: "global" | "host";
when?: (host?: PluginHostRecord) => boolean;
run: (shell: ShellApi, host?: PluginHostRecord) => void;
}
export interface DashboardCardProps {
isVisible: boolean;
shell: ShellApi;
}
export interface DashboardCardContribution {
/** Must be declared in manifest contributes.dashboardCards. */
id: string;
titleKey: string;
defaultHeight?: number;
component: ComponentType<DashboardCardProps>;
}
export interface HomepageWidgetContribution<C = Record<string, unknown>> {
id: string;
name: string;
description: string;
category: "links" | "info" | "system" | "monitoring";
icon: ReactNode;
defaultConfig: C;
defaultSize: { w: number; h: number };
minSize: { w: number; h: number };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: ComponentType<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
editFormComponent?: ComponentType<any>;
}
export interface SettingsComponentProps {
pluginId: string;
values: Record<string, unknown>;
setValue: (key: string, value: unknown) => void;
running: boolean;
}
export type ActionHandler = (...args: never[]) => unknown;
export interface ActionSlotDefinition {
id: string;
/** Contribution kinds the slot renders: "button", "component". */
accepts: string[];
}
export interface SlotContribution {
actionId: string;
titleKey: string;
/** Secondary text, for slots that show one (onboarding cards). */
descriptionKey?: string;
icon?: IconComponent;
kind?: "button" | "component" | string;
/** For kind "component". Receives the slot owner's props. */
component?: ComponentType<Record<string, unknown>>;
/** Extra condition on top of the action's permission. */
when?: (context: Record<string, unknown>) => boolean;
order?: number;
}
export interface SshAuthEditorProps {
form: Record<string, unknown>;
setField: (key: string, value: unknown) => void;
}
export interface SshAuthEditorContribution {
/** The value stored in the host's authType. */
authType: string;
titleKey: string;
hintKey?: string;
component?: ComponentType<SshAuthEditorProps>;
}
/** A7 declares these; A8 wires them into the login screen. */
export interface LoginMethodContribution {
id: string;
titleKey: string;
icon?: IconComponent;
component: ComponentType<Record<string, unknown>>;
}
export interface SecondFactorContribution {
id: string;
titleKey: string;
component: ComponentType<Record<string, unknown>>;
}
/** The subset of axios a plugin uses, rooted at /plugin-api/<id>/. */
export interface PluginApiClient {
get<T = unknown>(url: string, config?: unknown): Promise<{ data: T }>;
delete<T = unknown>(url: string, config?: unknown): Promise<{ data: T }>;
post<T = unknown>(
url: string,
body?: unknown,
config?: unknown,
): Promise<{ data: T }>;
put<T = unknown>(
url: string,
body?: unknown,
config?: unknown,
): Promise<{ data: T }>;
patch<T = unknown>(
url: string,
body?: unknown,
config?: unknown,
): Promise<{ data: T }>;
}
export interface PluginWsTarget {
url: string;
protocols?: string[];
}
export interface TermixAppInfo {
readonly pluginId: string;
readonly manifest: PluginManifest;
}
/**
* Registration surface for a plugin frontend.
*
* A7 adds: rail items, panels, tabs, host editor sections, host actions and
* badges, context menu items, palette entries, dashboard cards, homepage
* widgets, settings components, action slots, login/2FA UI and SSH auth
* editors. Everything registered here is removed when the plugin is disabled.
* Registration surface for a plugin frontend. Every `register*` returns a
* disposer, and anything not disposed by hand is disposed when the plugin is
* disabled.
*/
export interface TermixApp extends TermixAppInfo {
/**
* True on anonymous guest pages (a shared-session link). Only plugins with
* contributes.guest run there, and they should register just what a guest
* sees: there is no user, so API calls needing a login will fail.
*/
readonly guest: boolean;
registerRailItem: (item: RailItemContribution) => Disposer;
registerPanel: (
id: string,
component: ComponentType<PanelProps>,
options?: PanelOptions,
) => Disposer;
registerTab: (
type: string,
component: ComponentType<TabProps>,
options?: TabOptions,
) => Disposer;
registerHostEditorSection: (
section: HostEditorSectionContribution,
) => Disposer;
registerHostAction: (action: HostActionContribution) => Disposer;
registerHostBadge: (badge: HostBadgeContribution) => Disposer;
registerHostContextMenuItem: (
item: HostContextMenuItemContribution,
) => Disposer;
registerPaletteEntry: (entry: PaletteEntryContribution) => Disposer;
registerDashboardCard: (card: DashboardCardContribution) => Disposer;
registerHomepageWidget: (widget: HomepageWidgetContribution) => Disposer;
registerSettingsComponent: (
componentId: string,
component: ComponentType<SettingsComponentProps>,
) => Disposer;
registerAction: (
id: string,
handler: ActionHandler,
options?: { permission?: string },
) => Disposer;
declareActionSlot: (slot: ActionSlotDefinition) => Disposer;
registerSlotContribution: (
slotId: string,
contribution: SlotContribution,
) => Disposer;
invokeAction: (id: string, ...args: unknown[]) => Promise<unknown>;
registerSshAuthEditor: (editor: SshAuthEditorContribution) => Disposer;
registerLoginMethod: (method: LoginMethodContribution) => Disposer;
registerSecondFactorUI: (factor: SecondFactorContribution) => Disposer;
/** HTTP client for this plugin's /plugin-api/<id>/ routes. */
api: PluginApiClient;
/** WebSocket URL and auth subprotocols for /plugin-ws/<id>/<path>. */
wsUrl: (
path: string,
options?: { origin?: unknown },
) => Promise<PluginWsTarget | null>;
tabs: TabsApi;
/** Registered cleanup, run when the plugin is disabled. */
onDispose: (dispose: () => void) => void;
onDispose: (dispose: Disposer) => void;
}
export type FrontendActivate = (app: TermixApp) => void | Promise<void>;
@@ -37,4 +486,158 @@ export function definePluginFrontend(plugin: FrontendModule): FrontendModule {
return plugin;
}
// ---------------------------------------------------------------------------
// Hooks
// ---------------------------------------------------------------------------
export type TranslateFn = (
key: string,
options?: Record<string, unknown> | string,
) => string;
export interface CurrentUser {
userId: string;
username: string;
isAdmin: boolean;
}
export interface ToastApi {
success: (message: string) => void;
error: (message: string) => void;
info: (message: string) => void;
warning: (message: string) => void;
}
export type SettingsScope = "admin" | "user" | "host";
export interface SettingsState {
values: Record<string, unknown>;
loaded: boolean;
save: (values: Record<string, unknown>) => Promise<void>;
}
/**
* What core implements behind the hooks. Internal: a plugin never calls this.
*/
export interface PluginHostBridge {
usePluginId: () => string;
useTranslation: (pluginId: string) => {
t: TranslateFn;
language: string;
};
usePermission: (pluginId: string, permission: string) => boolean;
useSettings: (
pluginId: string,
scope: SettingsScope,
hostId?: number | string,
) => SettingsState;
useHost: (hostId: string | number | undefined) => PluginHostRecord | null;
useHosts: () => { hosts: PluginHostRecord[]; loaded: boolean };
useCurrentUser: () => CurrentUser | null;
useTheme: () => { theme: "light" | "dark" };
toast: ToastApi;
getApi: (pluginId: string) => PluginApiClient;
useTabs: () => TabsApi;
invokeAction: (id: string, ...args: unknown[]) => Promise<unknown>;
}
let host: PluginHostBridge | null = null;
/** Called once by core. Not part of the plugin API. */
export function __setPluginHost(bridge: PluginHostBridge | null): void {
host = bridge;
}
/**
* Set by the shared test setup, so a plugin component rendered on its own in a
* unit test still has hooks. A global rather than an import, because the test
* setup and the component can load different copies of this module.
*/
const TEST_HOST_KEY = "__termixTestPluginHost";
function requireHost(): PluginHostBridge {
const fallback = (globalThis as Record<string, unknown>)[TEST_HOST_KEY] as
PluginHostBridge | undefined;
if (!host && fallback) return fallback;
if (!host) {
throw new Error(
"@termix/plugin-sdk/frontend: no plugin host. Hooks only work inside Termix or renderWithApp().",
);
}
return host;
}
/** The id of the plugin that registered the component being rendered. */
export function usePluginId(): string {
return requireHost().usePluginId();
}
/**
* Translation bound to this plugin's namespace. Keys the plugin does not
* define fall back to core's shared strings (common.*, hosts.*).
*/
export function useTranslation(): { t: TranslateFn; language: string } {
const bridge = requireHost();
return bridge.useTranslation(bridge.usePluginId());
}
/**
* Whether the current user holds a permission. A short name resolves to this
* plugin's own namespace, a dotted id is taken as given. UI only: the backend
* route checks again.
*/
export function usePermission(permission: string): boolean {
const bridge = requireHost();
return bridge.usePermission(bridge.usePluginId(), permission);
}
export function useSettings(
scope: SettingsScope,
hostId?: number | string,
): SettingsState {
const bridge = requireHost();
return bridge.useSettings(bridge.usePluginId(), scope, hostId);
}
export function useHost(
hostId: string | number | undefined,
): PluginHostRecord | null {
return requireHost().useHost(hostId);
}
export function useHosts(): { hosts: PluginHostRecord[]; loaded: boolean } {
return requireHost().useHosts();
}
export function useCurrentUser(): CurrentUser | null {
return requireHost().useCurrentUser();
}
export function useTheme(): { theme: "light" | "dark" } {
return requireHost().useTheme();
}
export function useToast(): ToastApi {
return requireHost().toast;
}
export function usePluginApi(): PluginApiClient {
const bridge = requireHost();
return bridge.getApi(bridge.usePluginId());
}
export function useTabs(): TabsApi {
return requireHost().useTabs();
}
/**
* Runs an action another plugin registered, from anywhere in a plugin's code.
* Resolves undefined when nobody registered it, so an optional dependency
* that is switched off reads as "nothing there".
*/
export function invokeAction(id: string, ...args: unknown[]): Promise<unknown> {
return requireHost().invokeAction(id, ...args);
}
export type { PluginManifest } from "./manifest.js";
export type { ReactNode };
+54 -1
View File
@@ -31,7 +31,7 @@ export const PLUGIN_CATEGORIES = [
export const PLUGIN_PLATFORMS = ["linux", "win32", "darwin"] as const;
export const ACTION_CONTRIBUTION_KINDS = ["button"] as const;
export const ACTION_CONTRIBUTION_KINDS = ["button", "component"] as const;
export type ActionContributionKind = (typeof ACTION_CONTRIBUTION_KINDS)[number];
const OPEN_FROM_VALUES = ["rail", "host-context-menu", "palette"] as const;
@@ -97,6 +97,17 @@ export interface PluginTabContribution {
openFrom: string[];
}
/**
* A sidebar panel or dashboard card the plugin's frontend registers. Declared
* here so core knows who owns a saved view even while the plugin is off and
* its code never loaded.
*/
export interface PluginViewContribution {
id: string;
titleKey: string;
icon?: string;
}
/**
* One role permission a plugin contributes.
*
@@ -224,6 +235,13 @@ export interface PluginSettingsContribution {
export interface PluginContributions {
tabs?: PluginTabContribution[];
panels?: PluginViewContribution[];
dashboardCards?: PluginViewContribution[];
/**
* The frontend also runs on anonymous guest pages (shared-session and
* collab links), where it is activated with app.guest set.
*/
guest?: boolean;
actions?: PluginActionContribution[];
actionSlots?: PluginActionSlot[];
permissions?: PluginPermissionContribution[];
@@ -302,6 +320,9 @@ const ALLOWED_TOP_LEVEL = new Set([
const ALLOWED_CONTRIBUTES = new Set([
"tabs",
"panels",
"dashboardCards",
"guest",
"actions",
"actionSlots",
"permissions",
@@ -650,6 +671,14 @@ function validateContributes(
rejectUnknown(contributes, ALLOWED_CONTRIBUTES, '"contributes"', errors);
validateTabs(contributes.tabs, errors);
validateViews(contributes.panels, "panels", errors);
validateViews(contributes.dashboardCards, "dashboardCards", errors);
if (
contributes.guest !== undefined &&
typeof contributes.guest !== "boolean"
) {
errors.push('Field "contributes.guest" must be a boolean');
}
validatePermissions(contributes.permissions, pluginId, errors);
validateActions(contributes.actions, errors);
validateActionSlots(contributes.actionSlots, errors);
@@ -884,6 +913,30 @@ function validateTabs(tabs: unknown, errors: string[]): void {
});
}
function validateViews(views: unknown, field: string, errors: string[]): void {
if (views === undefined) return;
if (!Array.isArray(views)) {
errors.push(`Field "contributes.${field}" must be an array`);
return;
}
const seen = new Set<string>();
views.forEach((raw, index) => {
const where = `contributes.${field}[${index}]`;
if (!isPlainObject(raw)) {
errors.push(`${where} must be an object`);
return;
}
rejectUnknown(raw, ["id", "titleKey", "icon"], where, errors);
if (requireString(raw.id, `${where}.id`, errors)) {
if (seen.has(raw.id)) errors.push(`${where}.id duplicates "${raw.id}"`);
seen.add(raw.id);
}
requireString(raw.titleKey, `${where}.titleKey`, errors);
if (raw.icon !== undefined)
requireString(raw.icon, `${where}.icon`, errors);
});
}
function validatePermissions(
permissions: unknown,
pluginId: string | undefined,
+79 -34
View File
@@ -443,43 +443,88 @@ export function createMockCtx(
}
/**
* A TermixApp double for frontend tests.
* Rendering a plugin frontend in a test.
*
* A7 builds the real registration surface; until then this records what a
* plugin's frontend entry registered, which is all a test can assert about a
* loader that does not exist yet. Deliberately free of any render library so
* a plugin only needs testing-library if its own tests render something.
* renderWithApp activates the plugin against the real registration surface,
* so a test sees exactly what the shell would: which rail items, tabs, host
* actions and cards it registered, and each of those rendered with the SDK
* hooks working. It is implemented by the Termix host, which is where the
* registries live; the vitest preset points "@termix/plugin-host/testing" at
* core's implementation.
*/
export interface FakeTermixApp {
export interface RenderWithAppOptions {
pluginId?: string;
manifest?: Partial<PluginManifest>;
/** Role permissions the current user holds. */
permissions?: string[];
isAdmin?: boolean;
/** Hosts useHosts() returns. */
hosts?: Array<Record<string, unknown>>;
/** The plugin's locales/en.json, loaded into its namespace. */
locales?: Record<string, unknown>;
/** Render as an anonymous guest page. */
guest?: boolean;
}
/** A call a plugin made on the shell, recorded instead of performed. */
export interface ShellCall {
method: string;
args: unknown[];
}
export interface RenderedPluginApp {
app: TermixApp;
/** Cleanups the plugin handed to app.onDispose. */
disposals: Array<() => void>;
}
export function renderWithApp(
options: { pluginId?: string; manifest?: Partial<PluginManifest> } = {},
): FakeTermixApp {
const pluginId = options.pluginId ?? "test-plugin";
const disposals: Array<() => void> = [];
const manifest = {
id: pluginId,
name: pluginId,
version: "1.0.0",
description: "",
author: { name: "test" },
license: "MIT",
category: "Productivity",
engine: { termix: ">=2.9.0", api: "1" },
capabilities: [],
...options.manifest,
} as PluginManifest;
const app: TermixApp = {
pluginId,
manifest,
onDispose: (dispose) => disposals.push(dispose),
/** Everything this plugin registered, by kind. */
registered: {
railItems: () => Array<{ id: string; hidden?: boolean }>;
tabs: () => string[];
panels: () => string[];
hostActions: () => Array<{ id: string; tabType?: string }>;
hostEditorSections: () => string[];
dashboardCards: () => string[];
settingsComponents: () => string[];
slot: (slotId: string) => string[];
actions: () => string[];
};
/** Shell calls made by the plugin's code. */
shellCalls: ShellCall[];
renderTab: (type: string, props?: Record<string, unknown>) => HTMLElement;
renderPanel: (id: string, props?: Record<string, unknown>) => HTMLElement;
renderDashboardCard: (id: string) => HTMLElement;
renderHostEditorSection: (
id: string,
props?: Record<string, unknown>,
) => HTMLElement;
renderSettingsComponent: (
componentId: string,
props?: Record<string, unknown>,
) => HTMLElement;
/** Renders a slot the way its owner would. */
renderSlot: (slotId: string, props?: Record<string, unknown>) => HTMLElement;
/** Runs deactivate and every disposer, as disabling the plugin does. */
deactivate: () => Promise<void>;
}
return { app, disposals };
export type FrontendPluginModule = {
activate: (app: TermixApp) => void | Promise<void>;
deactivate?: () => void | Promise<void>;
};
interface PluginTestHost {
renderPlugin: (
plugin: FrontendPluginModule,
options: RenderWithAppOptions,
) => Promise<RenderedPluginApp>;
}
const TEST_HOST = "@termix/plugin-host/testing";
export async function renderWithApp(
plugin: FrontendPluginModule,
options: RenderWithAppOptions = {},
): Promise<RenderedPluginApp> {
// A variable specifier keeps bundlers from resolving it at build time; the
// test runner resolves it through its alias.
const host = (await import(/* @vite-ignore */ TEST_HOST)) as PluginTestHost;
return host.renderPlugin(plugin, options);
}
+40
View File
@@ -63,6 +63,46 @@ if (typeof globalThis.localStorage === "undefined") {
});
}
/**
* A plain plugin host for components a test renders on its own: keys come
* back as text, every permission is held, and nothing reaches a server.
* renderWithApp installs Termix's real host instead, which takes precedence.
*/
const noop = () => {};
const offline = () =>
Promise.reject(new Error("No plugin API in a unit test; mock it"));
(globalThis as Record<string, unknown>).__termixTestPluginHost = {
usePluginId: () => "test-plugin",
useTranslation: () => ({
t: (key: string) => key,
language: "en",
}),
usePermission: () => true,
useSettings: () => ({ values: {}, loaded: true, save: async () => {} }),
useHost: () => null,
useHosts: () => ({ hosts: [], loaded: true }),
useCurrentUser: () => null,
useTheme: () => ({ theme: "dark" }),
toast: { success: noop, error: noop, info: noop, warning: noop },
getApi: () => ({
get: offline,
delete: offline,
post: offline,
put: offline,
patch: offline,
}),
useTabs: () => ({
openTab: noop,
openSingletonTab: noop,
closeTab: noop,
getLayout: () => null,
applyLayout: async () => ({ skipped: [] }),
onChange: () => noop,
onReady: () => noop,
}),
invokeAction: async () => undefined,
};
afterEach(() => {
vi.restoreAllMocks();
});
+24
View File
@@ -48,9 +48,33 @@ export function pluginVitestConfig(
);
const repoRoot = findRepoRoot(pluginDir);
// Inside a Termix checkout the SDK's browser entries resolve to source and
// the test host is core's, so renderWithApp exercises the real registries
// and the plugin shares one SDK instance with them.
const alias: Record<string, string> = {
...(repoRoot
? {
"@termix/plugin-sdk/frontend": path.join(
repoRoot,
"packages",
"plugin-sdk",
"src",
"frontend.ts",
),
"@termix/plugin-sdk/ui": path.join(
repoRoot,
"src",
"ui",
"plugin-host",
"sdk-ui.ts",
),
"@termix/plugin-host/testing": path.join(
repoRoot,
"src",
"ui",
"plugin-host",
"testing-host.tsx",
),
"@/types": path.join(repoRoot, "src", "types"),
"@": path.join(repoRoot, "src", "ui"),
}
+107 -1
View File
@@ -1 +1,107 @@
{}
{
"permissions": {
"use": {
"title": "Use the assistant",
"description": "Open the assistant and ask it things."
},
"manage_providers": {
"title": "Manage providers",
"description": "Add, edit and remove the AI providers and their API keys."
},
"apply_proposals": {
"title": "Apply proposals",
"description": "Let the assistant carry out the changes it suggests."
},
"services.use": {
"title": "Use AI from other plugins",
"description": "Other plugins may call the assistant on your behalf."
},
"secrets.share": {
"title": "Share the API key",
"description": "Other plugins may use the stored AI API key without seeing it."
}
},
"settings": {
"assistantUnavailable": "The AI assistant is turned off on this server."
},
"ai": {
"title": "Assistant",
"send": "Send",
"stop": "Stop",
"save": "Save",
"cancel": "Cancel",
"running": "running",
"approve": "Approve",
"reject": "Reject",
"inputPlaceholder": "Ask about your servers, or describe a change to propose",
"selectProvider": "Select a provider",
"providerSettings": "Provider settings",
"addProvider": "Add provider",
"editProvider": "Edit provider",
"removeProvider": "Remove provider",
"providerType": "Provider",
"providerLabel": "Name",
"providerLabelPlaceholder": "For example: Local Ollama",
"baseUrl": "Server address",
"apiKey": "API key",
"defaultModel": "Default model",
"defaultModelPlaceholder": "For example: llama3.1 or claude-opus-5",
"labelRequired": "Give this provider a name",
"providerSaveFailed": "Could not save the provider",
"providerUpdated": "Provider updated",
"providerDeleteFailed": "Could not remove the provider",
"privateEndpointHint": "Addresses on your private network need an administrator to allow the host first.",
"providerOllama": "Ollama",
"providerAnthropic": "Anthropic",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI compatible",
"toolArguments": "Input",
"toolResult": "Result",
"statusApplied": "Applied",
"statusRejected": "Rejected",
"proposalApplied": "Change applied",
"proposalApplyFailed": "Could not apply the change",
"proposalRejectFailed": "Could not reject the change",
"enableTitle": "Enable the AI assistant",
"enableDescription": "Let the assistant read your setup and suggest changes for you to approve.",
"readOnlyCommands": "Allow read-only diagnostic commands",
"readOnlyCommandsDescription": "The assistant may run a short list of safe commands like df and uptime without asking each time. Anything that changes a server is always proposed first.",
"modelDetectFailed": "Could not reach the provider, so this is a general list. You can type any model name.",
"modelCustom": "Enter a model name",
"modelRefresh": "Refresh list",
"modelPlaceholder": "Choose or type a model",
"attachHint": "Type @ to reference a host, snippet or automation. Changes are proposed for you to approve.",
"runCommandTitle": "Run command",
"assistant": "AI Assistant",
"terminalPanelEmpty": "Ask the assistant about this host. Anything that changes state needs your approval first.",
"terminalPanelPlaceholder": "Ask about this host...",
"noProvidersConfigured": "No AI provider is configured yet. Add one in the AI assistant tab.",
"runsInOpenTerminal": "Runs in your open terminal session.",
"runInTerminal": "Run",
"terminalContextPrefill": "Here is my terminal output:\n\n{{output}}\n\n",
"secrets": {
"shareApiKey": "Let other plugins use your AI provider API key for their own requests. The key is never copied, and you can revoke this at any time."
}
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AI assistant",
"aiIntro": "Termix can include an optional AI assistant. You can change this later in your settings.",
"aiSkipTitle": "Leave it off",
"aiSkipDesc": "Termix works as it does now. The assistant stays hidden.",
"aiEnableTitle": "Turn it on",
"aiEnableDesc": "Adds an assistant that reads your setup and suggests changes you approve. Needs a provider you supply, such as a local Ollama.",
"aiNote": "Nothing is sent anywhere until you add a provider and send a message."
},
"admin": {
"aiGloballyEnabled": "AI assistant",
"aiGloballyEnabledDesc": "Let users turn on the AI assistant. While this is off, the assistant is hidden and blocked for everyone.",
"aiPrivateEndpoints": "Allowed private AI hosts",
"aiPrivateEndpointsDesc": "Hosts on your private network that users may point a provider at, such as a self-hosted Ollama. Separate them with commas.",
"updateAiEnabledFailed": "Could not update the AI setting",
"updateAiEndpointsFailed": "Could not update the allowed hosts"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Stuur",
"stop": "Stop",
"save": "Stoor",
"cancel": "Kanselleer",
"running": "hardloop",
"approve": "Goedkeur",
"reject": "Verwerp",
"inputPlaceholder": "Vra oor jou bedieners, of beskryf 'n verandering om voor te stel",
"selectProvider": "Kies 'n verskaffer",
"providerSettings": "Verskafferinstellings",
"addProvider": "Voeg verskaffer by",
"editProvider": "Wysig verskaffer",
"removeProvider": "Verwyder verskaffer",
"providerType": "Verskaffer",
"providerLabel": "Naam",
"providerLabelPlaceholder": "Byvoorbeeld: Plaaslike Ollama",
"baseUrl": "Bedieneradres",
"apiKey": "API-sleutel",
"defaultModel": "Standaardmodel",
"defaultModelPlaceholder": "Byvoorbeeld: llama3.1 of claude-opus-5",
"labelRequired": "Gee hierdie verskaffer 'n naam",
"providerSaveFailed": "Kon nie die verskaffer stoor nie",
"providerUpdated": "Verskaffer opgedateer",
"providerDeleteFailed": "Kon nie die verskaffer verwyder nie",
"privateEndpointHint": "Adresse op jou private netwerk benodig eers 'n administrateur om die gasheer toe te laat.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropiese",
"providerOpenai": "OpenAI",
"providerGemini": "Google Tweeling",
"providerOpenaiCompatible": "OpenAI-versoenbaar",
"toolArguments": "Invoer",
"toolResult": "Resultaat",
"statusApplied": "Toegepas",
"statusRejected": "Verwerp",
"proposalApplied": "Verandering toegepas",
"proposalApplyFailed": "Kon nie die verandering toepas nie",
"proposalRejectFailed": "Kon nie die verandering verwerp nie",
"enableTitle": "Aktiveer die KI-assistent",
"enableDescription": "Laat die assistent jou opstelling lees en veranderinge voorstel wat jy kan goedkeur.",
"readOnlyCommands": "Laat leesalleen-diagnostiese bevele toe",
"readOnlyCommandsDescription": "Die assistent kan 'n kort lys veilige opdragte soos df en uptime uitvoer sonder om elke keer te vra. Enigiets wat 'n bediener verander, word altyd eerste voorgestel.",
"modelDetectFailed": "Kon nie die verskaffer bereik nie, so dit is 'n algemene lys. Jy kan enige modelnaam tik.",
"modelCustom": "Voer 'n modelnaam in",
"modelRefresh": "Verfris lys",
"modelPlaceholder": "Kies of tik 'n model",
"attachHint": "Tik @ om na 'n gasheer, brokkie of outomatisering te verwys. Veranderinge word voorgestel vir jou om goed te keur.",
"runCommandTitle": "Voer opdrag uit",
"assistant": "KI-assistent",
"terminalPanelEmpty": "Vra die assistent oor hierdie gasheer. Enigiets wat van status verander, benodig eers jou goedkeuring.",
"terminalPanelPlaceholder": "Vra oor hierdie gasheer...",
"noProvidersConfigured": "Geen KI-verskaffer is nog gekonfigureer nie. Voeg een by in die KI-assistent-oortjie.",
"runsInOpenTerminal": "Loop in jou oop terminaalsessie.",
"runInTerminal": "Hardloop"
},
"nav": {
"ai": "KI"
},
"onboarding": {
"aiTitle": "KI-assistent",
"aiIntro": "Termix kan 'n opsionele KI-assistent insluit. Jy kan dit later in jou instellings verander.",
"aiSkipTitle": "Los dit af",
"aiSkipDesc": "Termix werk soos dit nou doen. Die assistent bly versteek.",
"aiEnableTitle": "Skakel dit aan",
"aiEnableDesc": "Voeg 'n assistent by wat jou opstelling lees en veranderinge voorstel wat jy goedkeur. Benodig 'n verskaffer wat jy verskaf, soos 'n plaaslike Ollama.",
"aiNote": "Niks word êrens heen gestuur totdat jy 'n verskaffer byvoeg en 'n boodskap stuur nie."
},
"admin": {
"aiGloballyEnabled": "KI-assistent",
"aiGloballyEnabledDesc": "Laat gebruikers die KI-assistent aanskakel. Terwyl dit af is, is die assistent vir almal versteek en geblokkeer.",
"aiPrivateEndpoints": "Toegelate private KI-gashere",
"aiPrivateEndpointsDesc": "Gashere op jou privaat netwerk waarna gebruikers 'n verskaffer kan verwys, soos 'n self-gehoste Ollama. Skei hulle met kommas.",
"updateAiEnabledFailed": "Kon nie die KI-instelling opdateer nie",
"updateAiEndpointsFailed": "Kon nie die toegelate gashere opdateer nie"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "مساعد",
"send": "يرسل",
"stop": "قف",
"save": "يحفظ",
"cancel": "يلغي",
"running": "جري",
"approve": "يعتمد",
"reject": "يرفض",
"inputPlaceholder": "استفسر عن خوادمك، أو صف تغييرًا ترغب في اقتراحه.",
"selectProvider": "اختر مزود الخدمة",
"providerSettings": "إعدادات المزوّد",
"addProvider": "إضافة موفر",
"editProvider": "موفر التحرير",
"removeProvider": "إزالة الموفر",
"providerType": "مزود",
"providerLabel": "اسم",
"providerLabelPlaceholder": "على سبيل المثال: أولاما المحلي",
"baseUrl": "عنوان الخادم",
"apiKey": "مفتاح API",
"defaultModel": "النموذج الافتراضي",
"defaultModelPlaceholder": "على سبيل المثال: llama3.1 أو claude-opus-5",
"labelRequired": "أعطِ هذا المزوّد اسمًا",
"providerSaveFailed": "تعذر حفظ بيانات المزوّد",
"providerUpdated": "تم تحديث المزوّد",
"providerDeleteFailed": "تعذر إزالة الموفر",
"privateEndpointHint": "تحتاج العناوين الموجودة على شبكتك الخاصة إلى موافقة المسؤول أولاً للسماح للمضيف بالوصول إليها.",
"providerOllama": "أولاما",
"providerAnthropic": "الأنثروبيك",
"providerOpenai": "أوبن إيه آي",
"providerGemini": "جوجل جيميني",
"providerOpenaiCompatible": "متوافق مع OpenAI",
"toolArguments": "مدخل",
"toolResult": "نتيجة",
"statusApplied": "مُطبَّق",
"statusRejected": "مرفوض",
"proposalApplied": "تم تطبيق التغيير",
"proposalApplyFailed": "تعذر تطبيق التغيير",
"proposalRejectFailed": "لم يكن بالإمكان رفض التغيير",
"enableTitle": "قم بتفعيل مساعد الذكاء الاصطناعي",
"enableDescription": "دع المساعد يقرأ إعداداتك ويقترح تغييرات لتوافق عليها.",
"readOnlyCommands": "السماح بأوامر التشخيص للقراءة فقط",
"readOnlyCommandsDescription": "قد يقوم المساعد بتشغيل قائمة مختصرة من الأوامر الآمنة مثل df و uptime دون الحاجة إلى طلب ذلك في كل مرة. ويتم اقتراح أي أمر يُغيّر حالة الخادم أولاً.",
"modelDetectFailed": "تعذر الوصول إلى المورّد، لذا فهذه قائمة عامة. يمكنك كتابة أي اسم طراز.",
"modelCustom": "أدخل اسم النموذج",
"modelRefresh": "تحديث القائمة",
"modelPlaceholder": "اختر أو اكتب طرازًا",
"attachHint": "اكتب @ للإشارة إلى مضيف أو مقتطف أو عملية آلية. سيتم اقتراح تغييرات عليك الموافقة عليها.",
"runCommandTitle": "تشغيل الأمر",
"assistant": "مساعد الذكاء الاصطناعي",
"terminalPanelEmpty": "استفسر من المساعد عن هذا المضيف. أي تغيير في حالته يتطلب موافقتك أولاً.",
"terminalPanelPlaceholder": "اسأل عن هذا المضيف...",
"noProvidersConfigured": "لم يتم إعداد أي موفر للذكاء الاصطناعي حتى الآن. أضف واحداً في علامة تبويب مساعد الذكاء الاصطناعي.",
"runsInOpenTerminal": "يتم تشغيله في جلسة الطرفية المفتوحة لديك.",
"runInTerminal": "يجري"
},
"nav": {
"ai": "الذكاء الاصطناعي"
},
"onboarding": {
"aiTitle": "مساعد الذكاء الاصطناعي",
"aiIntro": "يمكن أن يتضمن تطبيق Termix مساعدًا ذكيًا اختياريًا. يمكنك تغيير هذا الخيار لاحقًا من الإعدادات.",
"aiSkipTitle": "اتركه مغلقًا",
"aiSkipDesc": "يعمل برنامج Termix كما هو الآن. ويبقى المساعد مخفياً.",
"aiEnableTitle": "قم بتشغيله",
"aiEnableDesc": "يُضيف هذا البرنامج مساعدًا يقرأ إعداداتك ويقترح تغييرات توافق عليها. يتطلب ذلك مزود خدمة تُحدده بنفسك، مثل مزود خدمة Ollama محلي.",
"aiNote": "لن يتم إرسال أي شيء إلى أي مكان حتى تقوم بإضافة مزود خدمة وإرسال رسالة."
},
"admin": {
"aiGloballyEnabled": "مساعد الذكاء الاصطناعي",
"aiGloballyEnabledDesc": "اسمح للمستخدمين بتفعيل مساعد الذكاء الاصطناعي. عند إيقاف تشغيله، يكون المساعد مخفيًا ومحظورًا عن الجميع.",
"aiPrivateEndpoints": "المضيفون الخاصون المسموح بهم للذكاء الاصطناعي",
"aiPrivateEndpointsDesc": "المضيفون على شبكتك الخاصة الذين يمكن للمستخدمين توجيه مزود الخدمة إليهم، مثل Ollama المستضاف ذاتيًا. افصل بينهم بفواصل.",
"updateAiEnabledFailed": "تعذر تحديث إعدادات الذكاء الاصطناعي",
"updateAiEndpointsFailed": "تعذر تحديث المضيفين المسموح بهم"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Асистент",
"send": "Изпрати",
"stop": "Спри",
"save": "Запазване",
"cancel": "Отказ",
"running": "бягане",
"approve": "Одобряване",
"reject": "Отхвърляне",
"inputPlaceholder": "Попитайте за вашите сървъри или опишете промяна, която да предложите",
"selectProvider": "Изберете доставчик",
"providerSettings": "Настройки на доставчика",
"addProvider": "Добавяне на доставчик",
"editProvider": "Редактиране на доставчика",
"removeProvider": "Премахване на доставчика",
"providerType": "Доставчик",
"providerLabel": "Име",
"providerLabelPlaceholder": "Например: Местна Олама",
"baseUrl": "Адрес на сървъра",
"apiKey": "API ключ",
"defaultModel": "Модел по подразбиране",
"defaultModelPlaceholder": "Например: llama3.1 или claude-opus-5",
"labelRequired": "Дайте име на този доставчик",
"providerSaveFailed": "Доставчикът не можа да бъде запазен",
"providerUpdated": "Доставчикът е актуализиран",
"providerDeleteFailed": "Доставчикът не можа да бъде премахнат",
"privateEndpointHint": "Адресите във вашата частна мрежа се нуждаят първо от администратор, който да разреши хоста.",
"providerOllama": "Олама",
"providerAnthropic": "Антропен",
"providerOpenai": "OpenAI",
"providerGemini": "Google Джемини",
"providerOpenaiCompatible": "Съвместим с OpenAI",
"toolArguments": "Вход",
"toolResult": "Резултат",
"statusApplied": "Приложено",
"statusRejected": "Отхвърлено",
"proposalApplied": "Промяната е приложена",
"proposalApplyFailed": "Промяната не можа да се приложи",
"proposalRejectFailed": "Промяната не можа да бъде отхвърлена",
"enableTitle": "Активирайте AI асистента",
"enableDescription": "Нека асистентът прочете настройката ви и предложи промени, които да одобрите.",
"readOnlyCommands": "Разрешаване на диагностични команди само за четене",
"readOnlyCommandsDescription": "Асистентът може да изпълни кратък списък с безопасни команди като df и uptime, без да пита всеки път. Всичко, което променя сървър, винаги се предлага първо.",
"modelDetectFailed": "Не можахме да се свържем с доставчика, така че това е общ списък. Можете да въведете име на произволен модел.",
"modelCustom": "Въведете име на модел",
"modelRefresh": "Обновяване на списъка",
"modelPlaceholder": "Изберете или въведете модел",
"attachHint": "Въведете @, за да се обърнете към хост, фрагмент или автоматизация. Предлагат се промени за одобрение.",
"runCommandTitle": "Изпълнете командата",
"assistant": "Асистент с изкуствен интелект",
"terminalPanelEmpty": "Попитайте асистента за този хост. Всичко, което променя състоянието, първо изисква вашето одобрение.",
"terminalPanelPlaceholder": "Попитайте за този домакин...",
"noProvidersConfigured": "Все още не е конфигуриран доставчик на ИИ. Добавете такъв в раздела „Асистент за ИИ“.",
"runsInOpenTerminal": "Изпълнява се във вашата отворена терминална сесия.",
"runInTerminal": "Бягане"
},
"nav": {
"ai": "Изкуствен интелект"
},
"onboarding": {
"aiTitle": "Асистент с изкуствен интелект",
"aiIntro": "Termix може да включва опционален AI асистент. Можете да промените това по-късно в настройките си.",
"aiSkipTitle": "Остави го изключено",
"aiSkipDesc": "Termix работи както сега. Асистентът остава скрит.",
"aiEnableTitle": "Включете го",
"aiEnableDesc": "Добавя асистент, който чете настройките ви и предлага промени, които одобрявате. Необходим е доставчик, когото вие предоставяте, например местен Ollama.",
"aiNote": "Нищо не се изпраща никъде, докато не добавите доставчик и не изпратите съобщение."
},
"admin": {
"aiGloballyEnabled": "Асистент с изкуствен интелект",
"aiGloballyEnabledDesc": "Позволете на потребителите да включат асистента с изкуствен интелект. Докато това е изключено, асистентът е скрит и блокиран за всички.",
"aiPrivateEndpoints": "Разрешени частни хостове с изкуствен интелект",
"aiPrivateEndpointsDesc": "Хостове във вашата частна мрежа, към които потребителите могат да насочат доставчик, като например самостоятелно хостван Ollama. Разделете ги със запетаи.",
"updateAiEnabledFailed": "Не можа да се актуализира настройката за изкуствен интелект",
"updateAiEndpointsFailed": "Не можаха да се актуализират разрешените хостове"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "সহকারী",
"send": "পাঠান",
"stop": "থামুন",
"save": "সংরক্ষণ করুন",
"cancel": "বাতিল করুন",
"running": "দৌড়ানো",
"approve": "অনুমোদন করুন",
"reject": "প্রত্যাখ্যান করুন",
"inputPlaceholder": "আপনার সার্ভারগুলো সম্পর্কে জিজ্ঞাসা করুন, অথবা কোনো পরিবর্তনের প্রস্তাব বর্ণনা করুন।",
"selectProvider": "একজন সরবরাহকারী নির্বাচন করুন",
"providerSettings": "প্রদানকারীর সেটিংস",
"addProvider": "প্রদানকারী যোগ করুন",
"editProvider": "সম্পাদনা প্রদানকারী",
"removeProvider": "প্রদানকারীকে অপসারণ করুন",
"providerType": "সরবরাহকারী",
"providerLabel": "নাম",
"providerLabelPlaceholder": "উদাহরণস্বরূপ: স্থানীয় ওলামা",
"baseUrl": "সার্ভারের ঠিকানা",
"apiKey": "এপিআই কী",
"defaultModel": "ডিফল্ট মডেল",
"defaultModelPlaceholder": "উদাহরণস্বরূপ: llama3.1 অথবা claude-opus-5",
"labelRequired": "এই সরবরাহকারীকে একটি নাম দিন",
"providerSaveFailed": "প্রদানকারীকে সংরক্ষণ করা যায়নি",
"providerUpdated": "প্রদানকারী আপডেট করেছেন",
"providerDeleteFailed": "প্রদানকারীকে সরানো যায়নি",
"privateEndpointHint": "আপনার ব্যক্তিগত নেটওয়ার্কের অ্যাড্রেসগুলো ব্যবহার করার জন্য প্রথমে একজন অ্যাডমিনিস্ট্রেটরের কাছ থেকে হোস্টটিকে অনুমতি নিতে হয়।",
"providerOllama": "ওল্লামা",
"providerAnthropic": "অ্যানথ্রোপিক",
"providerOpenai": "ওপেনএআই",
"providerGemini": "গুগল জেমিনি",
"providerOpenaiCompatible": "ওপেনএআই সামঞ্জস্যপূর্ণ",
"toolArguments": "ইনপুট",
"toolResult": "ফলাফল",
"statusApplied": "প্রয়োগ করা হয়েছে",
"statusRejected": "প্রত্যাখ্যাত",
"proposalApplied": "পরিবর্তন প্রয়োগ করা হয়েছে",
"proposalApplyFailed": "পরিবর্তনটি প্রয়োগ করা যায়নি।",
"proposalRejectFailed": "পরিবর্তনটি প্রত্যাখ্যান করা যায়নি",
"enableTitle": "এআই অ্যাসিস্ট্যান্ট সক্রিয় করুন",
"enableDescription": "সহকারীকে আপনার সেটআপটি পড়তে দিন এবং আপনার অনুমোদনের জন্য পরিবর্তনের পরামর্শ দিতে দিন।",
"readOnlyCommands": "শুধুমাত্র পঠনযোগ্য ডায়াগনস্টিক কমান্ডের অনুমতি দিন",
"readOnlyCommandsDescription": "সহকারী প্রতিবার অনুমতি না নিয়েই df এবং uptime-এর মতো কয়েকটি নিরাপদ কমান্ড চালাতে পারে। সার্ভার পরিবর্তন করে এমন যেকোনো কিছু সর্বদা প্রথমে প্রস্তাব করা হয়।",
"modelDetectFailed": "সরবরাহকারীর সাথে যোগাযোগ করা সম্ভব হয়নি, তাই এটি একটি সাধারণ তালিকা। আপনি যেকোনো মডেলের নাম টাইপ করতে পারেন।",
"modelCustom": "একটি মডেলের নাম লিখুন",
"modelRefresh": "তালিকা রিফ্রেশ করুন",
"modelPlaceholder": "একটি মডেল বেছে নিন বা টাইপ করুন",
"attachHint": "কোনো হোস্ট, স্নিপেট বা অটোমেশন উল্লেখ করতে @ টাইপ করুন। আপনার অনুমোদনের জন্য পরিবর্তনগুলো প্রস্তাব করা হবে।",
"runCommandTitle": "রান কমান্ড",
"assistant": "এআই সহকারী",
"terminalPanelEmpty": "এই হোস্ট সম্পর্কে অ্যাসিস্ট্যান্টকে জিজ্ঞাসা করুন। অবস্থা পরিবর্তনের জন্য প্রথমে আপনার অনুমোদন প্রয়োজন।",
"terminalPanelPlaceholder": "এই হোস্ট সম্পর্কে জিজ্ঞাসা করুন...",
"noProvidersConfigured": "এখনো কোনো এআই প্রোভাইডার কনফিগার করা হয়নি। এআই অ্যাসিস্ট্যান্ট ট্যাবে একটি যোগ করুন।",
"runsInOpenTerminal": "আপনার খোলা টার্মিনাল সেশনে চলে।",
"runInTerminal": "দৌড়"
},
"nav": {
"ai": "এআই"
},
"onboarding": {
"aiTitle": "এআই সহকারী",
"aiIntro": "টারমিক্সে একটি ঐচ্ছিক এআই অ্যাসিস্ট্যান্ট অন্তর্ভুক্ত থাকতে পারে। আপনি পরবর্তীতে আপনার সেটিংসে এটি পরিবর্তন করতে পারবেন।",
"aiSkipTitle": "এটা ছেড়ে দাও",
"aiSkipDesc": "টারমিক্স এখন যেমন কাজ করে, তেমনই কাজ করে। অ্যাসিস্ট্যান্টটি লুকানো থাকে।",
"aiEnableTitle": "এটা চালু করুন",
"aiEnableDesc": "একজন সহকারী যোগ করে যে আপনার সেটআপ পড়ে এবং আপনার অনুমোদিত পরিবর্তনের পরামর্শ দেয়। এর জন্য আপনার সরবরাহ করা একজন প্রদানকারীর প্রয়োজন, যেমন একজন স্থানীয় ওলামা।",
"aiNote": "আপনি একজন প্রোভাইডার যোগ করে মেসেজ না পাঠানো পর্যন্ত কোথাও কিছু পাঠানো হয় না।"
},
"admin": {
"aiGloballyEnabled": "এআই সহকারী",
"aiGloballyEnabledDesc": "ব্যবহারকারীদের এআই অ্যাসিস্ট্যান্ট চালু করার সুযোগ দিন। এটি বন্ধ থাকা অবস্থায়, অ্যাসিস্ট্যান্টটি সকলের জন্য লুকানো এবং ব্লক করা থাকবে।",
"aiPrivateEndpoints": "ব্যক্তিগত এআই হোস্ট অনুমোদিত",
"aiPrivateEndpointsDesc": "আপনার প্রাইভেট নেটওয়ার্কের হোস্টগুলো, যেগুলোকে ব্যবহারকারীরা কোনো প্রোভাইডারের দিকে নির্দেশ করতে পারে, যেমন একটি সেলফ-হোস্টেড ওলামা। এগুলোকে কমা দিয়ে আলাদা করুন।",
"updateAiEnabledFailed": "এআই সেটিং আপডেট করা যায়নি",
"updateAiEndpointsFailed": "অনুমোদিত হোস্টগুলি আপডেট করা যায়নি"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Enviar",
"stop": "Atura",
"save": "Desa",
"cancel": "Cancel·la",
"running": "corrent",
"approve": "Aprova",
"reject": "Rebutja",
"inputPlaceholder": "Pregunteu pels vostres cambrers o descriviu un canvi que vulgueu proposar",
"selectProvider": "Seleccioneu un proveïdor",
"providerSettings": "Configuració del proveïdor",
"addProvider": "Afegeix un proveïdor",
"editProvider": "Edita el proveïdor",
"removeProvider": "Elimina el proveïdor",
"providerType": "Proveïdor",
"providerLabel": "Nom",
"providerLabelPlaceholder": "Per exemple: Ollama local",
"baseUrl": "Adreça del servidor",
"apiKey": "Clau API",
"defaultModel": "Model per defecte",
"defaultModelPlaceholder": "Per exemple: llama3.1 o claude-opus-5",
"labelRequired": "Doneu un nom a aquest proveïdor",
"providerSaveFailed": "No s'ha pogut desar el proveïdor",
"providerUpdated": "Proveïdor actualitzat",
"providerDeleteFailed": "No s'ha pogut eliminar el proveïdor",
"privateEndpointHint": "Les adreces de la vostra xarxa privada necessiten primer que un administrador permeti l'accés a l'amfitrió.",
"providerOllama": "Ollama",
"providerAnthropic": "Antròpic",
"providerOpenai": "OpenAI",
"providerGemini": "Google Bessons",
"providerOpenaiCompatible": "Compatible amb OpenAI",
"toolArguments": "Entrada",
"toolResult": "Resultat",
"statusApplied": "Aplicat",
"statusRejected": "Rebutjat",
"proposalApplied": "Canvi aplicat",
"proposalApplyFailed": "No s'ha pogut aplicar el canvi",
"proposalRejectFailed": "No s'ha pogut rebutjar el canvi",
"enableTitle": "Activa l'assistent d'IA",
"enableDescription": "Deixa que l'assistent llegeixi la teva configuració i et suggereixi canvis perquè els aprovis.",
"readOnlyCommands": "Permetre ordres de diagnòstic de només lectura",
"readOnlyCommandsDescription": "L'assistent pot executar una llista curta d'ordres segures com ara df i uptime sense preguntar cada vegada. Qualsevol cosa que canviï un servidor sempre es proposa primer.",
"modelDetectFailed": "No s'ha pogut contactar amb el proveïdor, per tant, aquesta és una llista general. Podeu escriure qualsevol nom de model.",
"modelCustom": "Introduïu un nom de model",
"modelRefresh": "Actualitza la llista",
"modelPlaceholder": "Trieu o escriviu un model",
"attachHint": "Escriviu @ per fer referència a un host, fragment o automatització. Es proposen canvis perquè els aprovi.",
"runCommandTitle": "Executa l'ordre",
"assistant": "Assistent d'IA",
"terminalPanelEmpty": "Pregunta a l'assistent sobre aquest amfitrió. Qualsevol cosa que canviï d'estat necessita primer la teva aprovació.",
"terminalPanelPlaceholder": "Pregunta per aquest amfitrió...",
"noProvidersConfigured": "Encara no hi ha cap proveïdor d'IA configurat. Afegeix-ne un a la pestanya Assistent d'IA.",
"runsInOpenTerminal": "S'executa a la sessió de terminal oberta.",
"runInTerminal": "Corre"
},
"nav": {
"ai": "IA"
},
"onboarding": {
"aiTitle": "Assistent d'IA",
"aiIntro": "Termix pot incloure un assistent d'IA opcional. Pots canviar-ho més tard a la configuració.",
"aiSkipTitle": "Deixa-ho fora",
"aiSkipDesc": "Termix funciona com ho fa ara. L'assistent roman ocult.",
"aiEnableTitle": "Engega-ho",
"aiEnableDesc": "Afegeix un assistent que llegeix la configuració i suggereix els canvis que aproveu. Necessita un proveïdor que subministreu, com ara un Ollama local.",
"aiNote": "No s'envia res enlloc fins que no afegiu un proveïdor i envieu un missatge."
},
"admin": {
"aiGloballyEnabled": "Assistent d'IA",
"aiGloballyEnabledDesc": "Permet que els usuaris activin l'assistent d'IA. Mentre estigui desactivat, l'assistent està ocult i bloquejat per a tothom.",
"aiPrivateEndpoints": "Amfitrions d'IA privats permesos",
"aiPrivateEndpointsDesc": "Amfitrions de la vostra xarxa privada als quals els usuaris poden dirigir un proveïdor, com ara un Ollama autoallotjat. Separeu-los amb comes.",
"updateAiEnabledFailed": "No s'ha pogut actualitzar la configuració de la IA",
"updateAiEndpointsFailed": "No s'han pogut actualitzar els amfitrions permesos"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Asistent",
"send": "Poslat",
"stop": "Zastávka",
"save": "Uložit",
"cancel": "Zrušit",
"running": "běh",
"approve": "Schvalovat",
"reject": "Odmítnout",
"inputPlaceholder": "Zeptejte se na své servery nebo popište změnu, kterou chcete navrhnout",
"selectProvider": "Vyberte poskytovatele",
"providerSettings": "Nastavení poskytovatele",
"addProvider": "Přidat poskytovatele",
"editProvider": "Upravit poskytovatele",
"removeProvider": "Odebrat poskytovatele",
"providerType": "Poskytovatel",
"providerLabel": "Jméno",
"providerLabelPlaceholder": "Například: Místní Ollama",
"baseUrl": "Adresa serveru",
"apiKey": "Klíč API",
"defaultModel": "Výchozí model",
"defaultModelPlaceholder": "Například: llama3.1 nebo claude-opus-5",
"labelRequired": "Zadejte tomuto poskytovateli název",
"providerSaveFailed": "Nepodařilo se uložit poskytovatele",
"providerUpdated": "Poskytovatel aktualizován",
"providerDeleteFailed": "Nepodařilo se odebrat poskytovatele",
"privateEndpointHint": "Adresy ve vaší privátní síti vyžadují, aby je správce nejprve povolil hostiteli.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropický",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Kompatibilní s OpenAI",
"toolArguments": "Vstup",
"toolResult": "Výsledek",
"statusApplied": "Aplikovaný",
"statusRejected": "Zamítnuto",
"proposalApplied": "Změna použita",
"proposalApplyFailed": "Změnu se nepodařilo použít",
"proposalRejectFailed": "Změnu se nepodařilo odmítnout",
"enableTitle": "Povolte asistenta s umělou inteligencí",
"enableDescription": "Nechte asistenta přečíst si vaše nastavení a navrhnout změny ke schválení.",
"readOnlyCommands": "Povolit diagnostické příkazy pouze pro čtení",
"readOnlyCommandsDescription": "Asistent může spustit krátký seznam bezpečných příkazů, jako například df a uptime, aniž by se pokaždé ptal. Vše, co mění server, je vždy navrženo jako první.",
"modelDetectFailed": "Nepodařilo se mi spojit s poskytovatelem, toto je obecný seznam. Můžete zadat libovolný název modelu.",
"modelCustom": "Zadejte název modelu",
"modelRefresh": "Obnovit seznam",
"modelPlaceholder": "Vyberte nebo zadejte model",
"attachHint": "Zadejte @ pro odkaz na hostitele, úryvek kódu nebo automatizaci. Změny jsou navrženy ke schválení.",
"runCommandTitle": "Spustit příkaz",
"assistant": "Asistent umělé inteligence",
"terminalPanelEmpty": "Zeptejte se asistenta na tohoto hostitele. Jakákoli změna stavu vyžaduje váš první souhlas.",
"terminalPanelPlaceholder": "Zeptejte se na tohoto hostitele...",
"noProvidersConfigured": "Zatím není nakonfigurován žádný poskytovatel AI. Přidejte ho na kartě Asistent AI.",
"runsInOpenTerminal": "Spustí se ve vaší otevřené terminálové relaci.",
"runInTerminal": "Běh"
},
"nav": {
"ai": "Umělá inteligence"
},
"onboarding": {
"aiTitle": "Asistent s umělou inteligencí",
"aiIntro": "Termix může obsahovat volitelného asistenta s umělou inteligencí. Toto můžete později změnit v nastavení.",
"aiSkipTitle": "Nech to být",
"aiSkipDesc": "Termix funguje stejně jako teď. Asistent zůstává skrytý.",
"aiEnableTitle": "Zapněte to",
"aiEnableDesc": "Přidá asistenta, který si přečte vaše nastavení a navrhne změny, které schválíte. Vyžaduje poskytovatele, kterého dodáte, například místní Ollamu.",
"aiNote": "Nic se nikam neodesílá, dokud nepřidáte poskytovatele a neodešlete zprávu."
},
"admin": {
"aiGloballyEnabled": "Asistent s umělou inteligencí",
"aiGloballyEnabledDesc": "Umožněte uživatelům zapnout asistenta s umělou inteligencí. Pokud je tato funkce vypnutá, asistent je skrytý a blokovaný pro všechny.",
"aiPrivateEndpoints": "Povolení soukromí hostitelé s umělou inteligencí",
"aiPrivateEndpointsDesc": "Hostitelé ve vaší privátní síti, na které mohou uživatelé odkazovat poskytovatele, například samostatně hostovaný Ollama. Oddělte je čárkami.",
"updateAiEnabledFailed": "Nepodařilo se aktualizovat nastavení umělé inteligence",
"updateAiEndpointsFailed": "Nepodařilo se aktualizovat povolené hostitele"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Sende",
"stop": "Stop",
"save": "Spare",
"cancel": "Ophæve",
"running": "løb",
"approve": "Godkende",
"reject": "Afvise",
"inputPlaceholder": "Spørg om dine servere, eller beskriv en ændring, du vil foreslå",
"selectProvider": "Vælg en udbyder",
"providerSettings": "Udbyderindstillinger",
"addProvider": "Tilføj udbyder",
"editProvider": "Rediger udbyder",
"removeProvider": "Fjern udbyder",
"providerType": "Udbyder",
"providerLabel": "Navn",
"providerLabelPlaceholder": "For eksempel: Lokal Ollama",
"baseUrl": "Serveradresse",
"apiKey": "API-nøgle",
"defaultModel": "Standardmodel",
"defaultModelPlaceholder": "For eksempel: llama3.1 eller claude-opus-5",
"labelRequired": "Giv denne udbyder et navn",
"providerSaveFailed": "Udbyderen kunne ikke gemmes",
"providerUpdated": "Udbyder opdateret",
"providerDeleteFailed": "Udbyderen kunne ikke fjernes",
"privateEndpointHint": "Adresser på dit private netværk kræver først tilladelse fra en administrator til værten.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropisk",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-kompatibel",
"toolArguments": "Input",
"toolResult": "Resultat",
"statusApplied": "Anvendt",
"statusRejected": "Afvist",
"proposalApplied": "Ændring anvendt",
"proposalApplyFailed": "Ændringen kunne ikke anvendes",
"proposalRejectFailed": "Kunne ikke afvise ændringen",
"enableTitle": "Aktivér AI-assistenten",
"enableDescription": "Lad assistenten læse din opsætning og foreslå ændringer, som du kan godkende.",
"readOnlyCommands": "Tillad skrivebeskyttede diagnosticeringskommandoer",
"readOnlyCommandsDescription": "Assistenten kan køre en kort liste med sikre kommandoer som f.eks. df og uptime uden at spørge hver gang. Alt, der ændrer en server, foreslås altid først.",
"modelDetectFailed": "Kunne ikke få kontakt med udbyderen, så dette er en generel liste. Du kan skrive et hvilket som helst modelnavn.",
"modelCustom": "Indtast et modelnavn",
"modelRefresh": "Opdater listen",
"modelPlaceholder": "Vælg eller skriv en model",
"attachHint": "Skriv @ for at referere til en vært, et kodestykke eller en automatisering. Ændringer foreslås, som du skal godkende.",
"runCommandTitle": "Kør kommando",
"assistant": "AI-assistent",
"terminalPanelEmpty": "Spørg assistenten om denne vært. Alt, der ændrer status, kræver din godkendelse først.",
"terminalPanelPlaceholder": "Spørg om denne vært...",
"noProvidersConfigured": "Der er endnu ikke konfigureret nogen AI-udbyder. Tilføj en i fanen AI-assistent.",
"runsInOpenTerminal": "Kører i din åbne terminalsession.",
"runInTerminal": "Løbe"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AI-assistent",
"aiIntro": "Termix kan inkludere en valgfri AI-assistent. Du kan ændre dette senere i dine indstillinger.",
"aiSkipTitle": "Lad det være slukket",
"aiSkipDesc": "Termix fungerer som den gør nu. Assistenten forbliver skjult.",
"aiEnableTitle": "Tænd den",
"aiEnableDesc": "Tilføjer en assistent, der læser din opsætning og foreslår ændringer, du godkender. Kræver en udbyder, du angiver, f.eks. en lokal Ollama.",
"aiNote": "Intet sendes nogen steder, før du tilføjer en udbyder og sender en besked."
},
"admin": {
"aiGloballyEnabled": "AI-assistent",
"aiGloballyEnabledDesc": "Lad brugerne aktivere AI-assistenten. Når dette er slået fra, er assistenten skjult og blokeret for alle.",
"aiPrivateEndpoints": "Tilladte private AI-værter",
"aiPrivateEndpointsDesc": "Værter på dit private netværk, som brugerne kan henvise til en udbyder, f.eks. en selvhostet Ollama. Adskil dem med kommaer.",
"updateAiEnabledFailed": "Kunne ikke opdatere AI-indstillingen",
"updateAiEndpointsFailed": "Kunne ikke opdatere de tilladte værter"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Schicken",
"stop": "Stoppen",
"save": "Speichern",
"cancel": "Stornieren",
"running": "läuft",
"approve": "Genehmigen",
"reject": "Ablehnen",
"inputPlaceholder": "Erkundigen Sie sich nach Ihren Servern oder beschreiben Sie einen Änderungsvorschlag.",
"selectProvider": "Wählen Sie einen Anbieter aus",
"providerSettings": "Anbietereinstellungen",
"addProvider": "Anbieter hinzufügen",
"editProvider": "Anbieter bearbeiten",
"removeProvider": "Anbieter entfernen",
"providerType": "Anbieter",
"providerLabel": "Name",
"providerLabelPlaceholder": "Zum Beispiel: Lokaler Ollama",
"baseUrl": "Serveradresse",
"apiKey": "API-Schlüssel",
"defaultModel": "Standardmodell",
"defaultModelPlaceholder": "Zum Beispiel: llama3.1 oder claude-opus-5",
"labelRequired": "Geben Sie diesem Anbieter einen Namen",
"providerSaveFailed": "Der Anbieter konnte nicht gespeichert werden.",
"providerUpdated": "Anbieter aktualisiert",
"providerDeleteFailed": "Der Anbieter konnte nicht entfernt werden.",
"privateEndpointHint": "Adressen in Ihrem privaten Netzwerk benötigen eine vorherige Genehmigung durch einen Administrator.",
"providerOllama": "Ollama",
"providerAnthropic": "Anthropisch",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-kompatibel",
"toolArguments": "Eingang",
"toolResult": "Ergebnis",
"statusApplied": "Angewandt",
"statusRejected": "Abgelehnt",
"proposalApplied": "Änderung angewendet",
"proposalApplyFailed": "Die Änderung konnte nicht angewendet werden.",
"proposalRejectFailed": "Die Änderung konnte nicht abgelehnt werden.",
"enableTitle": "Aktivieren Sie den KI-Assistenten",
"enableDescription": "Lassen Sie den Assistenten Ihre Einstellungen lesen und Ihnen Änderungen zur Genehmigung vorschlagen.",
"readOnlyCommands": "Zulassen von schreibgeschützten Diagnosebefehlen",
"readOnlyCommandsDescription": "Der Assistent kann eine kurze Liste sicherer Befehle wie df und uptime ausführen, ohne jedes Mal nachzufragen. Alle Änderungen, die einen Server betreffen, werden immer zuerst vorgeschlagen.",
"modelDetectFailed": "Der Anbieter konnte nicht erreicht werden, daher handelt es sich um eine allgemeine Liste. Sie können einen beliebigen Modellnamen eingeben.",
"modelCustom": "Geben Sie einen Modellnamen ein",
"modelRefresh": "Liste aktualisieren",
"modelPlaceholder": "Wählen Sie ein Modell aus oder geben Sie es ein.",
"attachHint": "Geben Sie @ ein, um auf einen Host, ein Code-Snippet oder eine Automatisierung zu verweisen. Die Änderungen werden Ihnen zur Genehmigung vorgeschlagen.",
"runCommandTitle": "Befehl ausführen",
"assistant": "KI-Assistent",
"terminalPanelEmpty": "Fragen Sie den Assistenten nach diesem Gastgeber. Jede Statusänderung bedarf Ihrer vorherigen Zustimmung.",
"terminalPanelPlaceholder": "Erkundigen Sie sich nach diesem Gastgeber...",
"noProvidersConfigured": "Es ist noch kein KI-Anbieter konfiguriert. Fügen Sie einen im Tab „KI-Assistent“ hinzu.",
"runsInOpenTerminal": "Wird in Ihrer geöffneten Terminal-Sitzung ausgeführt.",
"runInTerminal": "Laufen"
},
"nav": {
"ai": "KI"
},
"onboarding": {
"aiTitle": "KI-Assistent",
"aiIntro": "Termix kann optional einen KI-Assistenten beinhalten. Sie können dies später in Ihren Einstellungen ändern.",
"aiSkipTitle": "Lass es weg",
"aiSkipDesc": "Termix funktioniert wie bisher. Der Assistent bleibt verborgen.",
"aiEnableTitle": "Schalte es ein",
"aiEnableDesc": "Fügt einen Assistenten hinzu, der Ihre Einstellungen analysiert und Ihnen von Ihnen genehmigte Änderungen vorschlägt. Benötigt einen von Ihnen angegebenen Anbieter, z. B. einen lokalen Ollama-Händler.",
"aiNote": "Es wird nichts gesendet, bis Sie einen Anbieter hinzufügen und eine Nachricht senden."
},
"admin": {
"aiGloballyEnabled": "KI-Assistent",
"aiGloballyEnabledDesc": "Ermöglichen Sie den Nutzern, den KI-Assistenten zu aktivieren. Solange diese Option deaktiviert ist, ist der Assistent für alle ausgeblendet und gesperrt.",
"aiPrivateEndpoints": "Zulässige private KI-Hosts",
"aiPrivateEndpointsDesc": "Hosts in Ihrem privaten Netzwerk, auf die Benutzer einen Provider verweisen können, wie z. B. ein selbst gehostetes Ollama. Trennen Sie diese durch Kommas.",
"updateAiEnabledFailed": "Die KI-Einstellungen konnten nicht aktualisiert werden.",
"updateAiEndpointsFailed": "Die Liste der zulässigen Hosts konnte nicht aktualisiert werden."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Βοηθός",
"send": "Στέλνω",
"stop": "Στάση",
"save": "Εκτός",
"cancel": "Ματαίωση",
"running": "τρέξιμο",
"approve": "Εγκρίνω",
"reject": "Απορρίπτω",
"inputPlaceholder": "Ρωτήστε για τους διακομιστές σας ή περιγράψτε μια αλλαγή που προτείνετε",
"selectProvider": "Επιλέξτε έναν πάροχο",
"providerSettings": "Ρυθμίσεις παρόχου",
"addProvider": "Προσθήκη παρόχου",
"editProvider": "Επεξεργασία παρόχου",
"removeProvider": "Κατάργηση παρόχου",
"providerType": "Προμηθευτής",
"providerLabel": "Ονομα",
"providerLabelPlaceholder": "Για παράδειγμα: Τοπικό Ollama",
"baseUrl": "Διεύθυνση διακομιστή",
"apiKey": "Κλειδί API",
"defaultModel": "Προεπιλεγμένο μοντέλο",
"defaultModelPlaceholder": "Για παράδειγμα: llama3.1 ή claude-opus-5",
"labelRequired": "Δώστε σε αυτόν τον πάροχο ένα όνομα",
"providerSaveFailed": "Δεν ήταν δυνατή η αποθήκευση του παρόχου",
"providerUpdated": "Ο πάροχος ενημερώθηκε",
"providerDeleteFailed": "Δεν ήταν δυνατή η κατάργηση του παρόχου",
"privateEndpointHint": "Οι διευθύνσεις στο ιδιωτικό σας δίκτυο χρειάζονται πρώτα έναν διαχειριστή για να επιτρέψει στον κεντρικό υπολογιστή.",
"providerOllama": "Ολάμα",
"providerAnthropic": "Ανθρώπινο",
"providerOpenai": "Ανοιχτή Τεχνητή Νοημοσύνη",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Συμβατό με OpenAI",
"toolArguments": "Εισαγωγή",
"toolResult": "Αποτέλεσμα",
"statusApplied": "Εφαρμοσμένος",
"statusRejected": "Απορρίφθηκε",
"proposalApplied": "Η αλλαγή εφαρμόστηκε",
"proposalApplyFailed": "Δεν ήταν δυνατή η εφαρμογή της αλλαγής",
"proposalRejectFailed": "Δεν ήταν δυνατή η απόρριψη της αλλαγής",
"enableTitle": "Ενεργοποίηση του βοηθού τεχνητής νοημοσύνης",
"enableDescription": "Αφήστε τον βοηθό να διαβάσει τη ρύθμισή σας και να σας προτείνει αλλαγές για έγκριση.",
"readOnlyCommands": "Να επιτρέπονται διαγνωστικές εντολές μόνο για ανάγνωση",
"readOnlyCommandsDescription": "Ο βοηθός μπορεί να εκτελέσει μια σύντομη λίστα ασφαλών εντολών όπως df και uptime χωρίς να ρωτάει κάθε φορά. Οτιδήποτε αλλάζει έναν διακομιστή προτείνεται πάντα πρώτο.",
"modelDetectFailed": "Δεν ήταν δυνατή η επικοινωνία με τον πάροχο, επομένως αυτή είναι μια γενική λίστα. Μπορείτε να πληκτρολογήσετε οποιοδήποτε όνομα μοντέλου.",
"modelCustom": "Εισαγάγετε ένα όνομα μοντέλου",
"modelRefresh": "Ανανέωση λίστας",
"modelPlaceholder": "Επιλέξτε ή πληκτρολογήστε ένα μοντέλο",
"attachHint": "Πληκτρολογήστε @ για να αναφέρετε έναν κεντρικό υπολογιστή, ένα απόσπασμα κώδικα ή έναν αυτοματισμό. Προτείνονται αλλαγές για έγκριση.",
"runCommandTitle": "Εκτέλεση εντολής",
"assistant": "Βοηθός Τεχνητής Νοημοσύνης",
"terminalPanelEmpty": "Ρωτήστε τον βοηθό σχετικά με αυτόν τον οικοδεσπότη. Οτιδήποτε αλλάζει κατάσταση χρειάζεται πρώτα την έγκρισή σας.",
"terminalPanelPlaceholder": "Ρωτήστε για αυτόν τον οικοδεσπότη...",
"noProvidersConfigured": "Δεν έχει διαμορφωθεί ακόμη πάροχος τεχνητής νοημοσύνης. Προσθέστε έναν στην καρτέλα βοηθού τεχνητής νοημοσύνης.",
"runsInOpenTerminal": "Εκτελείται στην ανοιχτή συνεδρία τερματικού σας.",
"runInTerminal": "Τρέξιμο"
},
"nav": {
"ai": "Όλα συμπεριλαμβάνονται"
},
"onboarding": {
"aiTitle": "Βοηθός Τεχνητής Νοημοσύνης",
"aiIntro": "Το Termix μπορεί να περιλαμβάνει έναν προαιρετικό βοηθό τεχνητής νοημοσύνης. Μπορείτε να το αλλάξετε αυτό αργότερα στις ρυθμίσεις σας.",
"aiSkipTitle": "Αφήστε το μακριά",
"aiSkipDesc": "Το Termix λειτουργεί όπως τώρα. Ο βοηθός παραμένει κρυφός.",
"aiEnableTitle": "Ενεργοποίησέ το",
"aiEnableDesc": "Προσθέτει έναν βοηθό που διαβάζει τη ρύθμισή σας και προτείνει αλλαγές που εγκρίνετε. Χρειάζεται έναν πάροχο που παρέχετε, όπως ένα τοπικό Ollama.",
"aiNote": "Τίποτα δεν αποστέλλεται πουθενά μέχρι να προσθέσετε έναν πάροχο και να στείλετε ένα μήνυμα."
},
"admin": {
"aiGloballyEnabled": "Βοηθός Τεχνητής Νοημοσύνης",
"aiGloballyEnabledDesc": "Επιτρέψτε στους χρήστες να ενεργοποιήσουν τον βοηθό τεχνητής νοημοσύνης. Όσο είναι απενεργοποιημένος, ο βοηθός είναι κρυφός και αποκλεισμένος για όλους.",
"aiPrivateEndpoints": "Επιτρεπόμενοι ιδιωτικοί κεντρικοί υπολογιστές τεχνητής νοημοσύνης",
"aiPrivateEndpointsDesc": "Κεντρικοί υπολογιστές στο ιδιωτικό σας δίκτυο στους οποίους οι χρήστες μπορούν να υποδείξουν έναν πάροχο, όπως ένα αυτοφιλοξενούμενο Ollama. Διαχωρίστε τους με κόμματα.",
"updateAiEnabledFailed": "Δεν ήταν δυνατή η ενημέρωση της ρύθμισης AI",
"updateAiEndpointsFailed": "Δεν ήταν δυνατή η ενημέρωση των επιτρεπόμενων κεντρικών υπολογιστών"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Asistente",
"send": "Enviar",
"stop": "Detener",
"save": "Ahorrar",
"cancel": "Cancelar",
"running": "correr",
"approve": "Aprobar",
"reject": "Rechazar",
"inputPlaceholder": "Pregunte sobre sus servidores o describa un cambio para proponer.",
"selectProvider": "Seleccione un proveedor",
"providerSettings": "Configuración del proveedor",
"addProvider": "Agregar proveedor",
"editProvider": "Editar proveedor",
"removeProvider": "Eliminar proveedor",
"providerType": "Proveedor",
"providerLabel": "Nombre",
"providerLabelPlaceholder": "Por ejemplo: Ollama local",
"baseUrl": "Dirección del servidor",
"apiKey": "clave API",
"defaultModel": "Modelo predeterminado",
"defaultModelPlaceholder": "Por ejemplo: llama3.1 o claude-opus-5",
"labelRequired": "Dale un nombre a este proveedor.",
"providerSaveFailed": "No se pudo guardar el proveedor",
"providerUpdated": "Proveedor actualizado",
"providerDeleteFailed": "No se pudo eliminar al proveedor.",
"privateEndpointHint": "Las direcciones de su red privada necesitan que un administrador autorice el acceso al host previamente.",
"providerOllama": "Ollama",
"providerAnthropic": "Antrópico",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Compatible con OpenAI",
"toolArguments": "Aporte",
"toolResult": "Resultado",
"statusApplied": "Aplicado",
"statusRejected": "Rechazado",
"proposalApplied": "Cambio aplicado",
"proposalApplyFailed": "No se pudo aplicar el cambio.",
"proposalRejectFailed": "No se pudo rechazar el cambio",
"enableTitle": "Habilitar el asistente de IA",
"enableDescription": "Deja que el asistente lea tu configuración y te sugiera cambios para que los apruebes.",
"readOnlyCommands": "Permitir comandos de diagnóstico de solo lectura",
"readOnlyCommandsDescription": "El asistente puede ejecutar una breve lista de comandos seguros, como df y uptime, sin preguntar cada vez. Cualquier cambio que afecte al servidor siempre se propone primero.",
"modelDetectFailed": "No se pudo contactar con el proveedor, por lo que esta es una lista general. Puede escribir cualquier nombre de modelo.",
"modelCustom": "Introduzca un nombre de modelo",
"modelRefresh": "Actualizar lista",
"modelPlaceholder": "Seleccione o escriba un modelo.",
"attachHint": "Escribe @ para hacer referencia a un host, fragmento de código o automatización. Se proponen cambios para su aprobación.",
"runCommandTitle": "Ejecutar comando",
"assistant": "Asistente de IA",
"terminalPanelEmpty": "Consulta con el asistente sobre este anfitrión. Cualquier cambio de estado requiere tu aprobación previa.",
"terminalPanelPlaceholder": "Pregunta por este anfitrión...",
"noProvidersConfigured": "Aún no hay ningún proveedor de IA configurado. Añada uno en la pestaña del asistente de IA.",
"runsInOpenTerminal": "Se ejecuta en tu sesión de terminal abierta.",
"runInTerminal": "Correr"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "Asistente de IA",
"aiIntro": "Termix puede incluir un asistente de IA opcional. Puedes cambiar esto más adelante en la configuración.",
"aiSkipTitle": "Déjalo apagado",
"aiSkipDesc": "Termix funciona como hasta ahora. El asistente permanece oculto.",
"aiEnableTitle": "Enciéndelo",
"aiEnableDesc": "Agrega un asistente que lee tu configuración y sugiere cambios que apruebas. Requiere un proveedor que tú proporciones, como un Ollama local.",
"aiNote": "No se envía nada a ningún sitio hasta que añadas un proveedor y envíes un mensaje."
},
"admin": {
"aiGloballyEnabled": "Asistente de IA",
"aiGloballyEnabledDesc": "Permita que los usuarios activen el asistente de IA. Mientras esté desactivado, el asistente permanecerá oculto y bloqueado para todos.",
"aiPrivateEndpoints": "Hosts de IA privados permitidos",
"aiPrivateEndpointsDesc": "Hosts en tu red privada a los que los usuarios pueden dirigir un proveedor, como un servidor Ollama autogestionado. Sepáralos con comas.",
"updateAiEnabledFailed": "No se pudo actualizar la configuración de la IA.",
"updateAiEndpointsFailed": "No se pudieron actualizar los hosts permitidos."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistentti",
"send": "Lähetä",
"stop": "Stop",
"save": "Tallentaa",
"cancel": "Peruuttaa",
"running": "juokseminen",
"approve": "Hyväksyä",
"reject": "Hylätä",
"inputPlaceholder": "Kysy tarjoilijoistasi tai kuvaile ehdottamaasi muutosta",
"selectProvider": "Valitse palveluntarjoaja",
"providerSettings": "Palveluntarjoajan asetukset",
"addProvider": "Lisää palveluntarjoaja",
"editProvider": "Muokkaa palveluntarjoajaa",
"removeProvider": "Poista palveluntarjoaja",
"providerType": "Palveluntarjoaja",
"providerLabel": "Nimi",
"providerLabelPlaceholder": "Esimerkiksi: Paikallinen Ollama",
"baseUrl": "Palvelimen osoite",
"apiKey": "API-avain",
"defaultModel": "Oletusmalli",
"defaultModelPlaceholder": "Esimerkiksi: llama3.1 tai claude-opus-5",
"labelRequired": "Anna tälle palveluntarjoajalle nimi",
"providerSaveFailed": "Palveluntarjoajaa ei voitu tallentaa",
"providerUpdated": "Palveluntarjoaja päivitetty",
"providerDeleteFailed": "Palveluntarjoajaa ei voitu poistaa",
"privateEndpointHint": "Yksityisen verkon osoitteiden ylläpitäjän on ensin sallittava isäntä.",
"providerOllama": "Ollama",
"providerAnthropic": "Antrooppinen",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-yhteensopiva",
"toolArguments": "Syöttö",
"toolResult": "Tulos",
"statusApplied": "Käytetty",
"statusRejected": "Hylätty",
"proposalApplied": "Muutos otettu käyttöön",
"proposalApplyFailed": "Muutosta ei voitu ottaa käyttöön",
"proposalRejectFailed": "Muutosta ei voitu hylätä",
"enableTitle": "Ota tekoälyavustaja käyttöön",
"enableDescription": "Anna avustajan lukea asetuksesi ja ehdottaa muutoksia hyväksyttäväksi.",
"readOnlyCommands": "Salli vain luettavat diagnostiikkakomennot",
"readOnlyCommandsDescription": "Assistantti voi suorittaa lyhyen listan turvallisia komentoja, kuten df ja uptime, kysymättä joka kerta. Kaikki palvelinta muuttavat ehdotukset esitetään aina ensin.",
"modelDetectFailed": "Palveluntarjoajaan ei saatu yhteyttä, joten tämä on yleisluettelo. Voit kirjoittaa minkä tahansa mallin nimen.",
"modelCustom": "Anna mallin nimi",
"modelRefresh": "Päivitä lista",
"modelPlaceholder": "Valitse tai kirjoita malli",
"attachHint": "Kirjoita @ viitataksesi isäntään, koodinpätkään tai automaatioon. Muutoksia ehdotetaan hyväksyttäväksi.",
"runCommandTitle": "Suorita komento",
"assistant": "Tekoälyavustaja",
"terminalPanelEmpty": "Kysy avustajalta tästä majoittajasta. Kaikki osavaltion muutokset vaativat ensin hyväksyntäsi.",
"terminalPanelPlaceholder": "Kysy tästä isännöitsijästä...",
"noProvidersConfigured": "Tekoälypalveluntarjoajaa ei ole vielä määritetty. Lisää sellainen tekoälyavustaja-välilehdellä.",
"runsInOpenTerminal": "Suoritetaan avoimessa pääteistunnossa.",
"runInTerminal": "Juokse"
},
"nav": {
"ai": "Tekoäly"
},
"onboarding": {
"aiTitle": "Tekoälyavustaja",
"aiIntro": "Termix voi sisältää valinnaisen tekoälyavustaja. Voit muuttaa tätä myöhemmin asetuksissasi.",
"aiSkipTitle": "Jätä se pois",
"aiSkipDesc": "Termix toimii kuten nytkin. Avustaja pysyy piilossa.",
"aiEnableTitle": "Käynnistä se",
"aiEnableDesc": "Lisää avustajan, joka lukee asetuksesi ja ehdottaa hyväksymiäsi muutoksia. Edellyttää toimittamaasi palveluntarjoajaa, kuten paikallisen Ollaman.",
"aiNote": "Mitään ei lähetetä mihinkään ennen kuin lisäät palveluntarjoajan ja lähetät viestin."
},
"admin": {
"aiGloballyEnabled": "Tekoälyavustaja",
"aiGloballyEnabledDesc": "Salli käyttäjien ottaa tekoälyavustaja käyttöön. Kun tämä on pois päältä, avustaja on piilotettu ja estetty kaikilta.",
"aiPrivateEndpoints": "Sallitut yksityiset tekoälyisännät",
"aiPrivateEndpointsDesc": "Yksityisen verkon isännät, joihin käyttäjät voivat ohjata palveluntarjoajan, kuten itse isännöity Ollama. Erota ne pilkuilla.",
"updateAiEnabledFailed": "Tekoälyasetuksen päivittäminen epäonnistui",
"updateAiEndpointsFailed": "Sallittujen isäntien päivittäminen epäonnistui"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistant",
"send": "Envoyer",
"stop": "Arrêt",
"save": "Sauvegarder",
"cancel": "Annuler",
"running": "en cours d'exécution",
"approve": "Approuver",
"reject": "Rejeter",
"inputPlaceholder": "Renseignez-vous sur vos serveurs ou décrivez un changement à proposer.",
"selectProvider": "Sélectionnez un fournisseur",
"providerSettings": "Paramètres du fournisseur",
"addProvider": "Ajouter un fournisseur",
"editProvider": "Modifier le fournisseur",
"removeProvider": "Supprimer le fournisseur",
"providerType": "Fournisseur",
"providerLabel": "Nom",
"providerLabelPlaceholder": "Par exemple : Ollama local",
"baseUrl": "Adresse du serveur",
"apiKey": "Clé API",
"defaultModel": "Modèle par défaut",
"defaultModelPlaceholder": "Par exemple : llama3.1 ou claude-opus-5",
"labelRequired": "Donnez un nom à ce fournisseur",
"providerSaveFailed": "Impossible de sauver le fournisseur",
"providerUpdated": "Mise à jour du fournisseur",
"providerDeleteFailed": "Impossible de supprimer le fournisseur",
"privateEndpointHint": "Les adresses de votre réseau privé nécessitent l'autorisation préalable d'un administrateur pour accéder à l'hôte.",
"providerOllama": "Ollama",
"providerAnthropic": "Anthropique",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Compatible avec OpenAI",
"toolArguments": "Saisir",
"toolResult": "Résultat",
"statusApplied": "Appliqué",
"statusRejected": "Rejeté",
"proposalApplied": "Changement appliqué",
"proposalApplyFailed": "Impossible d'appliquer la modification",
"proposalRejectFailed": "Impossible de refuser la modification",
"enableTitle": "Activer l'assistant IA",
"enableDescription": "Laissez l'assistant analyser votre configuration et vous suggérer des modifications à approuver.",
"readOnlyCommands": "Autoriser les commandes de diagnostic en lecture seule",
"readOnlyCommandsDescription": "L'assistant peut exécuter une courte liste de commandes sûres, comme `df` et `uptime`, sans demander l'autorisation à chaque fois. Toute modification apportée au serveur est toujours proposée au préalable.",
"modelDetectFailed": "Impossible de joindre le fournisseur ; voici donc une liste générale. Vous pouvez saisir n’importe quel nom de modèle.",
"modelCustom": "Saisissez le nom du modèle",
"modelRefresh": "Actualiser la liste",
"modelPlaceholder": "Choisissez ou saisissez un modèle",
"attachHint": "Saisissez @ pour faire référence à un hôte, un extrait de code ou une automatisation. Les modifications proposées doivent être approuvées.",
"runCommandTitle": "Exécuter la commande",
"assistant": "Assistant IA",
"terminalPanelEmpty": "Renseignez-vous auprès de l'assistant concernant cet hôte. Tout changement d'état nécessite votre approbation préalable.",
"terminalPanelPlaceholder": "Renseignez-vous sur cet hôte...",
"noProvidersConfigured": "Aucun fournisseur d'IA n'est encore configuré. Ajoutez-en un dans l'onglet Assistant IA.",
"runsInOpenTerminal": "S'exécute dans votre session de terminal ouverte.",
"runInTerminal": "Courir"
},
"nav": {
"ai": "IA"
},
"onboarding": {
"aiTitle": "Assistant IA",
"aiIntro": "Termix peut inclure un assistant IA optionnel. Vous pouvez modifier ce paramètre ultérieurement.",
"aiSkipTitle": "Laissez-le de côté",
"aiSkipDesc": "Termix fonctionne comme actuellement. L'assistant reste caché.",
"aiEnableTitle": "Allumez-le",
"aiEnableDesc": "Ajoute un assistant qui analyse votre configuration et suggère des modifications que vous approuvez. Nécessite un fournisseur que vous désignez, comme un Ollama local.",
"aiNote": "Aucun message n'est envoyé tant que vous n'avez pas ajouté de fournisseur et envoyé un message."
},
"admin": {
"aiGloballyEnabled": "Assistant IA",
"aiGloballyEnabledDesc": "Autoriser les utilisateurs à activer l'assistant IA. Lorsqu'il est désactivé, l'assistant est masqué et inaccessible à tous.",
"aiPrivateEndpoints": "Hôtes d'IA privés autorisés",
"aiPrivateEndpointsDesc": "Les hôtes de votre réseau privé vers lesquels les utilisateurs peuvent orienter un fournisseur, comme un serveur Ollama auto-hébergé. Séparez-les par des virgules.",
"updateAiEnabledFailed": "Impossible de mettre à jour les paramètres d'IA",
"updateAiEndpointsFailed": "Impossible de mettre à jour les hôtes autorisés."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "עוֹזֵר",
"send": "לִשְׁלוֹחַ",
"stop": "לְהַפְסִיק",
"save": "לְהַצִיל",
"cancel": "לְבַטֵל",
"running": "רִיצָה",
"approve": "לְאַשֵׁר",
"reject": "לִדחוֹת",
"inputPlaceholder": "שאל לגבי המלצרים שלך, או תאר שינוי להציע",
"selectProvider": "בחר ספק",
"providerSettings": "הגדרות ספק",
"addProvider": "הוסף ספק",
"editProvider": "עריכת ספק",
"removeProvider": "הסר ספק",
"providerType": "ספק",
"providerLabel": "שֵׁם",
"providerLabelPlaceholder": "לדוגמה: אולמה מקומית",
"baseUrl": "כתובת השרת",
"apiKey": "מפתח API",
"defaultModel": "מודל ברירת מחדל",
"defaultModelPlaceholder": "לדוגמה: llama3.1 או claude-opus-5",
"labelRequired": "תן שם לספק הזה",
"providerSaveFailed": "לא ניתן היה לשמור את הספק",
"providerUpdated": "הספק עודכן",
"providerDeleteFailed": "לא ניתן היה להסיר את הספק",
"privateEndpointHint": "כתובות ברשת הפרטית שלך דורשות תחילה אישור ממנהל מערכת למארח.",
"providerOllama": "אולמה",
"providerAnthropic": "אנתרופי",
"providerOpenai": "בינה מלאכותית פתוחה",
"providerGemini": "גוגל ג'מיני",
"providerOpenaiCompatible": "תואם ל-OpenAI",
"toolArguments": "קֶלֶט",
"toolResult": "תוֹצָאָה",
"statusApplied": "יישומי",
"statusRejected": "נִדחֶה",
"proposalApplied": "השינוי הוחל",
"proposalApplyFailed": "לא ניתן היה להחיל את השינוי",
"proposalRejectFailed": "לא ניתן היה לדחות את השינוי",
"enableTitle": "הפעל את עוזר הבינה המלאכותית",
"enableDescription": "תן לעוזר לקרוא את ההגדרות שלך ולהציע שינויים לאישור.",
"readOnlyCommands": "אפשר פקודות אבחון לקריאה בלבד",
"readOnlyCommandsDescription": "העוזר יכול להריץ רשימה קצרה של פקודות בטוחות כמו df ו-uptime מבלי לשאול בכל פעם. כל דבר שמשנה שרת תמיד מוצע ראשון.",
"modelDetectFailed": "לא ניתן היה להגיע לספק, לכן זוהי רשימה כללית. ניתן להקליד כל שם דגם.",
"modelCustom": "הזן שם דגם",
"modelRefresh": "רענון הרשימה",
"modelPlaceholder": "בחר או הקלד מודל",
"attachHint": "הקלד @ כדי להפנות למארח, קטע טקסט או אוטומציה. שינויים מוצעים לאישורך.",
"runCommandTitle": "הפעלה של הפקודה",
"assistant": "עוזר בינה מלאכותית",
"terminalPanelEmpty": "שאל את העוזר לגבי המארח הזה. כל שינוי במצב דורש תחילה את אישורך.",
"terminalPanelPlaceholder": "שאל על המארח הזה...",
"noProvidersConfigured": "עדיין לא הוגדר ספק בינה מלאכותית. הוסף אחד בכרטיסייה עוזר בינה מלאכותית.",
"runsInOpenTerminal": "פועל בסשן הטרמינל הפתוח שלך.",
"runInTerminal": "לָרוּץ"
},
"nav": {
"ai": "בינה מלאכותית"
},
"onboarding": {
"aiTitle": "עוזר בינה מלאכותית",
"aiIntro": "טרמיקס יכול לכלול עוזר בינה מלאכותית אופציונלי. ניתן לשנות זאת מאוחר יותר בהגדרות.",
"aiSkipTitle": "תעזוב את זה",
"aiSkipDesc": "טרמיקס עובד כמו שהוא עובד עכשיו. העוזר נשאר מוסתר.",
"aiEnableTitle": "הפעל אותו",
"aiEnableDesc": "מוסיף עוזר שקורא את ההגדרות שלך ומציע שינויים שאתה מאשר. נדרש ספק שאתה מספק, כגון אולמה מקומי.",
"aiNote": "שום דבר לא נשלח לשום מקום עד שתוסיף ספק ותשלח הודעה."
},
"admin": {
"aiGloballyEnabled": "עוזר בינה מלאכותית",
"aiGloballyEnabledDesc": "אפשר למשתמשים להפעיל את עוזר הבינה המלאכותית. כאשר זה כבוי, העוזר מוסתר וחסום עבור כולם.",
"aiPrivateEndpoints": "מארחים פרטיים של בינה מלאכותית מורשים",
"aiPrivateEndpointsDesc": "מארחים ברשת הפרטית שלך שמשתמשים עשויים להפנות אליהם ספק, כגון Ollama המאוחסן בעצמו. הפרד ביניהם באמצעות פסיקים.",
"updateAiEnabledFailed": "לא ניתן היה לעדכן את הגדרת הבינה המלאכותית",
"updateAiEndpointsFailed": "לא ניתן היה לעדכן את המארחים המותרים"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "सहायक",
"send": "भेजना",
"stop": "रुकना",
"save": "बचाना",
"cancel": "रद्द करना",
"running": "दौड़ना",
"approve": "मंज़ूरी देना",
"reject": "अस्वीकार करना",
"inputPlaceholder": "अपने सर्वरों के बारे में पूछें, या प्रस्तावित परिवर्तन का वर्णन करें।",
"selectProvider": "एक प्रदाता का चयन करें",
"providerSettings": "प्रदाता सेटिंग्स",
"addProvider": "प्रदाता जोड़ें",
"editProvider": "प्रदाता संपादित करें",
"removeProvider": "प्रदाता को हटाएँ",
"providerType": "प्रदाता",
"providerLabel": "नाम",
"providerLabelPlaceholder": "उदाहरण के लिए: स्थानीय ओलामा",
"baseUrl": "सर्वर पता",
"apiKey": "एपीआई कुंजी",
"defaultModel": "डिफ़ॉल्ट मॉडल",
"defaultModelPlaceholder": "उदाहरण के लिए: llama3.1 या claude-opus-5",
"labelRequired": "इस प्रदाता को एक नाम दें",
"providerSaveFailed": "प्रदाता को बचाया नहीं जा सका",
"providerUpdated": "प्रदाता ने अपडेट किया",
"providerDeleteFailed": "प्रदाता को हटाया नहीं जा सका",
"privateEndpointHint": "आपके निजी नेटवर्क पर मौजूद पतों के लिए पहले एक प्रशासक द्वारा होस्ट को अनुमति देना आवश्यक है।",
"providerOllama": "ओलामा",
"providerAnthropic": "anthropic",
"providerOpenai": "ओपनएआई",
"providerGemini": "गूगल जेमिनी",
"providerOpenaiCompatible": "OpenAI संगत",
"toolArguments": "इनपुट",
"toolResult": "परिणाम",
"statusApplied": "लागू किया गया",
"statusRejected": "अस्वीकार कर दिया",
"proposalApplied": "परिवर्तन लागू हो गया",
"proposalApplyFailed": "परिवर्तन लागू नहीं हो सका",
"proposalRejectFailed": "परिवर्तन को अस्वीकार नहीं किया जा सका",
"enableTitle": "एआई सहायक को सक्रिय करें",
"enableDescription": "सहायक को आपकी सेटअप प्रक्रिया पढ़ने दें और वह आपके अनुमोदन के लिए परिवर्तन सुझाएगा।",
"readOnlyCommands": "केवल पढ़ने योग्य डायग्नोस्टिक कमांड की अनुमति दें",
"readOnlyCommandsDescription": "सहायक बिना पूछे ही df और uptime जैसे कुछ सुरक्षित कमांड चला सकता है। सर्वर में कोई भी बदलाव करने का प्रस्ताव हमेशा पहले दिया जाता है।",
"modelDetectFailed": "प्रदाता से संपर्क नहीं हो सका, इसलिए यह एक सामान्य सूची है। आप किसी भी मॉडल का नाम टाइप कर सकते हैं।",
"modelCustom": "मॉडल का नाम दर्ज करें",
"modelRefresh": "सूची रीफ़्रेश करें",
"modelPlaceholder": "कोई मॉडल चुनें या टाइप करें",
"attachHint": "किसी होस्ट, स्निपेट या ऑटोमेशन को संदर्भित करने के लिए @ टाइप करें। आपके अनुमोदन के लिए परिवर्तन प्रस्तावित किए जा रहे हैं।",
"runCommandTitle": "कमांड चलाएँ",
"assistant": "एआई सहायक",
"terminalPanelEmpty": "इस होस्ट के बारे में असिस्टेंट से पूछें। किसी भी स्थिति में बदलाव के लिए पहले आपकी स्वीकृति आवश्यक है।",
"terminalPanelPlaceholder": "इस होस्ट के बारे में पूछें...",
"noProvidersConfigured": "अभी तक कोई एआई प्रदाता कॉन्फ़िगर नहीं किया गया है। एआई सहायक टैब में एक जोड़ें।",
"runsInOpenTerminal": "यह आपके खुले टर्मिनल सत्र में चलता है।",
"runInTerminal": "दौड़ना"
},
"nav": {
"ai": "ऐ"
},
"onboarding": {
"aiTitle": "एआई सहायक",
"aiIntro": "टर्मिक्स में एक वैकल्पिक एआई सहायक शामिल हो सकता है। आप इसे बाद में अपनी सेटिंग्स में बदल सकते हैं।",
"aiSkipTitle": "इसे बंद कर दो",
"aiSkipDesc": "टर्मिक्स अभी की तरह ही काम करता है। असिस्टेंट छिपा रहता है।",
"aiEnableTitle": "इसे चालू करें",
"aiEnableDesc": "यह एक सहायक सुविधा जोड़ता है जो आपके सेटअप को पढ़ता है और आपके द्वारा स्वीकृत किए जाने वाले परिवर्तनों का सुझाव देता है। इसके लिए आपको एक प्रदाता उपलब्ध कराना होगा, जैसे कि स्थानीय ओलामा।",
"aiNote": "जब तक आप कोई प्रदाता नहीं जोड़ते और संदेश नहीं भेजते, तब तक कुछ भी कहीं नहीं भेजा जाता।"
},
"admin": {
"aiGloballyEnabled": "एआई सहायक",
"aiGloballyEnabledDesc": "उपयोगकर्ताओं को एआई सहायक चालू करने की अनुमति दें। जब यह बंद होगा, तो सहायक सभी के लिए छिपा हुआ और अवरुद्ध रहेगा।",
"aiPrivateEndpoints": "निजी एआई होस्ट की अनुमति दी गई",
"aiPrivateEndpointsDesc": "आपके निजी नेटवर्क पर मौजूद होस्ट, जिन्हें उपयोगकर्ता किसी प्रदाता से जोड़ सकते हैं, जैसे कि स्व-होस्टेड ओलामा। इन्हें अल्पविराम से अलग करें।",
"updateAiEnabledFailed": "एआई सेटिंग अपडेट नहीं हो सकी",
"updateAiEndpointsFailed": "अनुमत होस्टों को अपडेट नहीं किया जा सका"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Helyettes",
"send": "Elküld",
"stop": "Stop",
"save": "Megtakarítás",
"cancel": "Mégsem",
"running": "futás",
"approve": "Jóváhagyás",
"reject": "Elutasít",
"inputPlaceholder": "Kérdezzen a felszolgálókról, vagy írjon le egy javasolt változtatást",
"selectProvider": "Válasszon szolgáltatót",
"providerSettings": "Szolgáltatói beállítások",
"addProvider": "Szolgáltató hozzáadása",
"editProvider": "Szolgáltató szerkesztése",
"removeProvider": "Szolgáltató eltávolítása",
"providerType": "Szolgáltató",
"providerLabel": "Név",
"providerLabelPlaceholder": "Például: Helyi Ollama",
"baseUrl": "Szerver címe",
"apiKey": "API-kulcs",
"defaultModel": "Alapértelmezett modell",
"defaultModelPlaceholder": "Például: llama3.1 vagy claude-opus-5",
"labelRequired": "Adjon nevet ennek a szolgáltatónak",
"providerSaveFailed": "Nem sikerült menteni a szolgáltatót",
"providerUpdated": "Szolgáltató frissítve",
"providerDeleteFailed": "Nem sikerült eltávolítani a szolgáltatót",
"privateEndpointHint": "A privát hálózaton lévő címekhez először egy rendszergazdának kell engedélyeznie a gazdagépet.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropikus",
"providerOpenai": "Nyílt mesterséges intelligencia",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-kompatibilis",
"toolArguments": "Bemenet",
"toolResult": "Eredmény",
"statusApplied": "Alkalmazott",
"statusRejected": "Elutasítva",
"proposalApplied": "Módosítás alkalmazva",
"proposalApplyFailed": "Nem sikerült alkalmazni a módosítást",
"proposalRejectFailed": "Nem sikerült elutasítani a módosítást",
"enableTitle": "Engedélyezze a mesterséges intelligencia asszisztenst",
"enableDescription": "Hagyd, hogy az asszisztens felolvassa a beállításokat, és javasoljon módosításokat jóváhagyásra.",
"readOnlyCommands": "Csak olvasható diagnosztikai parancsok engedélyezése",
"readOnlyCommandsDescription": "Az asszisztens lefuttathat egy rövid listát a biztonságos parancsokról, mint például a df és az uptime, anélkül, hogy minden alkalommal rákérdezne. Bármi, ami megváltoztat egy szervert, mindig elsőként kerül felkérésre.",
"modelDetectFailed": "Nem sikerült elérni a szolgáltatót, ezért ez egy általános lista. Bármelyik modellnevet beírhatja.",
"modelCustom": "Adjon meg egy modellnevet",
"modelRefresh": "Lista frissítése",
"modelPlaceholder": "Válasszon vagy írjon be egy modellt",
"attachHint": "Írjon be @ jelet egy gazdagépre, kódrészletre vagy automatizálásra való hivatkozáshoz. A rendszer módosításokat javasol jóváhagyásra.",
"runCommandTitle": "Parancs futtatása",
"assistant": "MI asszisztens",
"terminalPanelEmpty": "Kérdezd meg az asszisztenst erről a házigazdáról. Bármi, ami megváltoztatja az állapotát, először a jóváhagyásodra van szükség.",
"terminalPanelPlaceholder": "Kérdezz rá erre a házigazdára...",
"noProvidersConfigured": "Még nincs konfigurálva AI-szolgáltató. Adjon hozzá egyet az AI-asszisztens fülön.",
"runsInOpenTerminal": "A megnyitott terminál munkamenetben fut.",
"runInTerminal": "Fut"
},
"nav": {
"ai": "MI"
},
"onboarding": {
"aiTitle": "MI asszisztens",
"aiIntro": "A Termix tartalmazhat opcionális mesterséges intelligencia asszisztenst. Ezt később módosíthatja a beállításokban.",
"aiSkipTitle": "Hagyd ki",
"aiSkipDesc": "A Termix ugyanúgy működik, mint most. Az asszisztens rejtve marad.",
"aiEnableTitle": "Kapcsold be",
"aiEnableDesc": "Hozzáad egy asszisztenst, amely beolvassa a beállításait, és javaslatokat tesz az Ön által jóváhagyott módosításokra. Szükség van egy Ön által biztosított szolgáltatóra, például egy helyi Ollama szakemberre.",
"aiNote": "Semmi sem kerül sehova, amíg nem adsz hozzá egy szolgáltatót és nem küldesz üzenetet."
},
"admin": {
"aiGloballyEnabled": "MI asszisztens",
"aiGloballyEnabledDesc": "Engedélyezheti a felhasználóknak a mesterséges intelligencia asszisztens bekapcsolását. Amíg ez ki van kapcsolva, az asszisztens rejtett és blokkolt mindenki számára.",
"aiPrivateEndpoints": "Engedélyezett privát AI-gazdagépek",
"aiPrivateEndpointsDesc": "A privát hálózatodon található olyan hosztok, amelyekre a felhasználók szolgáltatót irányíthatnak, például egy saját hosztolt Ollama. Vesszővel válaszd el őket.",
"updateAiEnabledFailed": "Nem sikerült frissíteni a mesterséges intelligencia beállítását",
"updateAiEndpointsFailed": "Nem sikerült frissíteni az engedélyezett gazdagépeket"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Asisten",
"send": "Mengirim",
"stop": "Berhenti",
"save": "Menyimpan",
"cancel": "Membatalkan",
"running": "berlari",
"approve": "Menyetujui",
"reject": "Menolak",
"inputPlaceholder": "Tanyakan tentang server Anda, atau jelaskan perubahan yang ingin Anda usulkan.",
"selectProvider": "Pilih penyedia",
"providerSettings": "Pengaturan penyedia",
"addProvider": "Tambahkan penyedia",
"editProvider": "Penyedia pengeditan",
"removeProvider": "Hapus penyedia",
"providerType": "Penyedia",
"providerLabel": "Nama",
"providerLabelPlaceholder": "Contohnya: Ollama setempat",
"baseUrl": "Alamat server",
"apiKey": "Kunci API",
"defaultModel": "Model default",
"defaultModelPlaceholder": "Misalnya: llama3.1 atau claude-opus-5",
"labelRequired": "Beri nama penyedia ini",
"providerSaveFailed": "Penyedia layanan tidak dapat disimpan.",
"providerUpdated": "Penyedia diperbarui",
"providerDeleteFailed": "Penyedia layanan tidak dapat dihapus.",
"privateEndpointHint": "Alamat-alamat di jaringan pribadi Anda memerlukan izin administrator terlebih dahulu untuk host tersebut.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropis",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Kompatibel dengan OpenAI",
"toolArguments": "Masukan",
"toolResult": "Hasil",
"statusApplied": "Terapan",
"statusRejected": "Ditolak",
"proposalApplied": "Perubahan diterapkan",
"proposalApplyFailed": "Perubahan tidak dapat diterapkan.",
"proposalRejectFailed": "Tidak bisa menolak perubahan tersebut",
"enableTitle": "Aktifkan asisten AI",
"enableDescription": "Biarkan asisten membaca pengaturan Anda dan menyarankan perubahan yang perlu Anda setujui.",
"readOnlyCommands": "Izinkan perintah diagnostik hanya baca.",
"readOnlyCommandsDescription": "Asisten dapat menjalankan daftar singkat perintah aman seperti df dan uptime tanpa bertanya setiap kali. Apa pun yang mengubah server selalu diusulkan terlebih dahulu.",
"modelDetectFailed": "Tidak dapat menghubungi penyedia, jadi ini adalah daftar umum. Anda dapat mengetikkan nama model apa pun.",
"modelCustom": "Masukkan nama model",
"modelRefresh": "Perbarui daftar",
"modelPlaceholder": "Pilih atau ketik model",
"attachHint": "Ketik @ untuk merujuk ke host, cuplikan kode, atau otomatisasi. Perubahan diajukan untuk Anda setujui.",
"runCommandTitle": "Jalankan perintah",
"assistant": "Asisten AI",
"terminalPanelEmpty": "Tanyakan kepada asisten tentang tuan rumah ini. Segala sesuatu yang berubah status memerlukan persetujuan Anda terlebih dahulu.",
"terminalPanelPlaceholder": "Tanyakan tentang tuan rumah ini...",
"noProvidersConfigured": "Belum ada penyedia AI yang dikonfigurasi. Tambahkan satu di tab asisten AI.",
"runsInOpenTerminal": "Berjalan di sesi terminal Anda yang sedang terbuka.",
"runInTerminal": "Berlari"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "Asisten AI",
"aiIntro": "Termix dapat menyertakan asisten AI opsional. Anda dapat mengubahnya nanti di pengaturan Anda.",
"aiSkipTitle": "Biarkan saja",
"aiSkipDesc": "Termix berfungsi seperti sekarang. Asisten tetap tersembunyi.",
"aiEnableTitle": "Nyalakan",
"aiEnableDesc": "Menambahkan asisten yang membaca pengaturan Anda dan menyarankan perubahan yang Anda setujui. Membutuhkan penyedia yang Anda berikan, seperti Ollama lokal.",
"aiNote": "Tidak ada data yang dikirim ke mana pun sampai Anda menambahkan penyedia dan mengirim pesan."
},
"admin": {
"aiGloballyEnabled": "Asisten AI",
"aiGloballyEnabledDesc": "Izinkan pengguna untuk mengaktifkan asisten AI. Saat dinonaktifkan, asisten akan disembunyikan dan diblokir untuk semua orang.",
"aiPrivateEndpoints": "Host AI pribadi yang diizinkan",
"aiPrivateEndpointsDesc": "Host di jaringan pribadi Anda yang mungkin ditunjuk pengguna untuk penyedia layanan, seperti Ollama yang dihosting sendiri. Pisahkan dengan koma.",
"updateAiEnabledFailed": "Pengaturan AI tidak dapat diperbarui.",
"updateAiEndpointsFailed": "Tidak dapat memperbarui host yang diizinkan."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistente",
"send": "Inviare",
"stop": "Fermare",
"save": "Salva",
"cancel": "Cancellare",
"running": "corsa",
"approve": "Approvare",
"reject": "Rifiutare",
"inputPlaceholder": "Chiedi informazioni sui tuoi server o descrivi una modifica da proporre",
"selectProvider": "Seleziona un fornitore",
"providerSettings": "Impostazioni del provider",
"addProvider": "Aggiungi fornitore",
"editProvider": "Fornitore di modifica",
"removeProvider": "Rimuovi il fornitore",
"providerType": "Fornitore",
"providerLabel": "Nome",
"providerLabelPlaceholder": "Ad esempio: Ollama locale",
"baseUrl": "Indirizzo del server",
"apiKey": "Chiave API",
"defaultModel": "Modello predefinito",
"defaultModelPlaceholder": "Ad esempio: llama3.1 o claude-opus-5",
"labelRequired": "Assegna un nome a questo fornitore",
"providerSaveFailed": "Impossibile salvare il provider",
"providerUpdated": "Fornitore aggiornato",
"providerDeleteFailed": "Impossibile rimuovere il provider",
"privateEndpointHint": "Gli indirizzi sulla tua rete privata richiedono prima l'autorizzazione di un amministratore per l'host.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropico",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Compatibile con OpenAI",
"toolArguments": "Ingresso",
"toolResult": "Risultato",
"statusApplied": "Applicato",
"statusRejected": "Respinto",
"proposalApplied": "Modifica applicata",
"proposalApplyFailed": "Impossibile applicare la modifica",
"proposalRejectFailed": "Impossibile rifiutare la modifica",
"enableTitle": "Abilita l'assistente IA",
"enableDescription": "Lascia che l'assistente legga la tua configurazione e ti suggerisca delle modifiche da approvare.",
"readOnlyCommands": "Consenti comandi diagnostici di sola lettura",
"readOnlyCommandsDescription": "L'assistente può eseguire un breve elenco di comandi sicuri come df e uptime senza chiedere conferma ogni volta. Qualsiasi operazione che modifichi un server viene sempre proposta in anticipo.",
"modelDetectFailed": "Non è stato possibile contattare il fornitore, quindi questo è un elenco generico. È possibile digitare qualsiasi nome di modello.",
"modelCustom": "Inserisci il nome del modello",
"modelRefresh": "Aggiorna elenco",
"modelPlaceholder": "Scegli o digita un modello",
"attachHint": "Digita @ per fare riferimento a un host, uno snippet o un'automazione. Le modifiche proposte sono da approvare.",
"runCommandTitle": "Esegui il comando",
"assistant": "Assistente AI",
"terminalPanelEmpty": "Chiedi all'assistente informazioni su questo host. Qualsiasi modifica allo stato richiede la tua approvazione preventiva.",
"terminalPanelPlaceholder": "Chiedi informazioni su questo host...",
"noProvidersConfigured": "Non è ancora configurato alcun provider di IA. Aggiungine uno nella scheda Assistente IA.",
"runsInOpenTerminal": "Viene eseguito nella sessione del terminale aperta.",
"runInTerminal": "Correre"
},
"nav": {
"ai": "IA"
},
"onboarding": {
"aiTitle": "Assistente basato sull'intelligenza artificiale",
"aiIntro": "Termix può includere un assistente AI opzionale. Puoi modificare questa impostazione in seguito.",
"aiSkipTitle": "Lascialo spento",
"aiSkipDesc": "Termix funziona come ora. L'assistente rimane nascosto.",
"aiEnableTitle": "Accendilo",
"aiEnableDesc": "Aggiunge un assistente che legge la tua configurazione e suggerisce modifiche che approvi. Richiede un provider che tu fornisca, come ad esempio un Ollama locale.",
"aiNote": "Non viene inviato nulla finché non si aggiunge un provider e si invia un messaggio."
},
"admin": {
"aiGloballyEnabled": "Assistente basato sull'intelligenza artificiale",
"aiGloballyEnabledDesc": "Consenti agli utenti di attivare l'assistente AI. Quando è disattivato, l'assistente è nascosto e bloccato per tutti.",
"aiPrivateEndpoints": "Host di IA privati consentiti",
"aiPrivateEndpointsDesc": "Host sulla tua rete privata a cui gli utenti possono indirizzare un provider, come ad esempio un'istanza di Ollama autogestita. Separali con delle virgole.",
"updateAiEnabledFailed": "Impossibile aggiornare le impostazioni dell'IA",
"updateAiEndpointsFailed": "Impossibile aggiornare gli host consentiti"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "アシスタント",
"send": "送信",
"stop": "停止",
"save": "保存",
"cancel": "キャンセル",
"running": "ランニング",
"approve": "承認する",
"reject": "拒否する",
"inputPlaceholder": "サーバーについて質問するか、変更案を説明する",
"selectProvider": "プロバイダーを選択してください",
"providerSettings": "プロバイダー設定",
"addProvider": "プロバイダーを追加",
"editProvider": "プロバイダーを編集",
"removeProvider": "プロバイダーを削除",
"providerType": "プロバイダー",
"providerLabel": "名前",
"providerLabelPlaceholder": "例えば:地元のオラマ",
"baseUrl": "サーバーアドレス",
"apiKey": "APIキー",
"defaultModel": "デフォルトモデル",
"defaultModelPlaceholder": "例えば、llama3.1 や claude-opus-5 など。",
"labelRequired": "このプロバイダーに名前を付けてください",
"providerSaveFailed": "プロバイダーを保存できませんでした",
"providerUpdated": "プロバイダーが更新されました",
"providerDeleteFailed": "プロバイダーを削除できませんでした",
"privateEndpointHint": "プライベートネットワーク上のアドレスを使用するには、まず管理者がホストを許可する必要があります。",
"providerOllama": "オラマ",
"providerAnthropic": "人間",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI対応",
"toolArguments": "入力",
"toolResult": "結果",
"statusApplied": "適用済み",
"statusRejected": "拒否されました",
"proposalApplied": "変更が適用されました",
"proposalApplyFailed": "変更を適用できませんでした",
"proposalRejectFailed": "変更を拒否できませんでした",
"enableTitle": "AIアシスタントを有効にする",
"enableDescription": "アシスタントにあなたの設定を読み取らせ、承認が必要な変更案を提案させましょう。",
"readOnlyCommands": "読み取り専用の診断コマンドを許可する",
"readOnlyCommandsDescription": "アシスタントは、dfやuptimeといった安全なコマンドを毎回確認することなく実行する場合があります。サーバーに変更を加える操作は、常に最初に提案されます。",
"modelDetectFailed": "プロバイダーに連絡が取れなかったため、これは一般的なリストです。任意のモデル名を入力してください。",
"modelCustom": "モデル名を入力してください",
"modelRefresh": "リストを更新",
"modelPlaceholder": "モデルを選択または入力してください",
"attachHint": "ホスト、スニペット、またはオートメーションを参照するには、「@」を入力してください。変更案が表示され、承認が必要となります。",
"runCommandTitle": "コマンドを実行する",
"assistant": "AIアシスタント",
"terminalPanelEmpty": "このホストについてアシスタントにお問い合わせください。状態変更を伴う変更はすべて、事前にあなたの承認が必要です。",
"terminalPanelPlaceholder": "このホストについて質問する...",
"noProvidersConfigured": "AIプロバイダーがまだ設定されていません。AIアシスタントタブで追加してください。",
"runsInOpenTerminal": "開いているターミナルセッションで実行されます。",
"runInTerminal": "走る"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AIアシスタント",
"aiIntro": "TermixにはオプションでAIアシスタントを搭載できます。これは後で設定画面で変更できます。",
"aiSkipTitle": "オフにしておく",
"aiSkipDesc": "Termixは現状のまま動作します。アシスタントは非表示のままです。",
"aiEnableTitle": "電源を入れる",
"aiEnableDesc": "設定内容を読み取り、承認した上で変更案を提案するアシスタント機能を追加します。ローカルのOllamaなど、お客様ご自身で手配するプロバイダーが必要です。",
"aiNote": "プロバイダーを追加してメッセージを送信するまで、データはどこにも送信されません。"
},
"admin": {
"aiGloballyEnabled": "AIアシスタント",
"aiGloballyEnabledDesc": "ユーザーがAIアシスタントをオンにできるようにします。オフになっている間は、アシスタントは非表示になり、誰からも利用できなくなります。",
"aiPrivateEndpoints": "プライベートAIホストを許可",
"aiPrivateEndpointsDesc": "ユーザーがプロバイダに指定できる、プライベートネットワーク上のホスト(自己ホスト型のOllamaなど)を指定します。カンマで区切ってください。",
"updateAiEnabledFailed": "AI設定を更新できませんでした",
"updateAiEndpointsFailed": "許可されたホストを更新できませんでした"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "어시스턴트",
"send": "보내다",
"stop": "멈추다",
"save": "구하다",
"cancel": "취소",
"running": "달리기",
"approve": "승인하다",
"reject": "거부하다",
"inputPlaceholder": "서버에 대해 문의하거나, 변경 사항을 제안해 보세요.",
"selectProvider": "제공업체를 선택하세요",
"providerSettings": "공급자 설정",
"addProvider": "공급자 추가",
"editProvider": "제공자 수정",
"removeProvider": "제공업체를 제거하세요",
"providerType": "공급자",
"providerLabel": "이름",
"providerLabelPlaceholder": "예: 지역 올라마",
"baseUrl": "서버 주소",
"apiKey": "API 키",
"defaultModel": "기본 모델",
"defaultModelPlaceholder": "예를 들어: llama3.1 또는 claude-opus-5",
"labelRequired": "이 제공업체에 이름을 지정하세요",
"providerSaveFailed": "공급자를 저장할 수 없습니다.",
"providerUpdated": "제공업체 업데이트됨",
"providerDeleteFailed": "공급자를 제거할 수 없습니다.",
"privateEndpointHint": "개인 네트워크의 주소에 접근하려면 관리자가 먼저 호스트를 허용해야 합니다.",
"providerOllama": "올라마",
"providerAnthropic": "인류",
"providerOpenai": "오픈아이",
"providerGemini": "구글 제미니",
"providerOpenaiCompatible": "OpenAI와 호환 가능",
"toolArguments": "입력",
"toolResult": "결과",
"statusApplied": "적용된",
"statusRejected": "거절됨",
"proposalApplied": "변경 사항이 적용되었습니다",
"proposalApplyFailed": "변경 사항을 적용할 수 없습니다.",
"proposalRejectFailed": "변경을 거부할 수 없었습니다",
"enableTitle": "AI 비서를 활성화하세요",
"enableDescription": "도우미가 사용자의 설정을 읽고 승인할 변경 사항을 제안하도록 하세요.",
"readOnlyCommands": "읽기 전용 진단 명령을 허용합니다.",
"readOnlyCommandsDescription": "도우미는 매번 묻지 않고 df 및 uptime과 같은 안전한 명령어 목록을 실행할 수 있습니다. 서버를 변경하는 작업은 항상 먼저 제안됩니다.",
"modelDetectFailed": "공급업체에 연결할 수 없어서 일반적인 목록을 제공합니다. 모델명을 입력하셔도 됩니다.",
"modelCustom": "모델명을 입력하세요",
"modelRefresh": "목록 새로 고침",
"modelPlaceholder": "모델을 선택하거나 입력하세요.",
"attachHint": "호스트, 코드 조각 또는 자동화를 참조하려면 @를 입력하세요. 승인을 위해 변경 사항이 제안됩니다.",
"runCommandTitle": "명령어를 실행하세요",
"assistant": "AI 어시스턴트",
"terminalPanelEmpty": "이 호스트에 대해 담당자에게 문의하십시오. 상태가 변경되는 모든 사항은 먼저 사용자의 승인이 필요합니다.",
"terminalPanelPlaceholder": "이 호스트에 대해 문의해 보세요...",
"noProvidersConfigured": "아직 AI 제공업체가 구성되지 않았습니다. AI 도우미 탭에서 추가하세요.",
"runsInOpenTerminal": "열려 있는 터미널 세션에서 실행됩니다.",
"runInTerminal": "달리다"
},
"nav": {
"ai": "일체 포함"
},
"onboarding": {
"aiTitle": "AI 비서",
"aiIntro": "Termix에는 선택적으로 AI 비서 기능이 포함될 수 있습니다. 이 기능은 나중에 설정에서 변경할 수 있습니다.",
"aiSkipTitle": "꺼두세요",
"aiSkipDesc": "Termix는 현재와 같이 작동합니다. 어시스턴트는 숨겨진 상태로 유지됩니다.",
"aiEnableTitle": "켜세요",
"aiEnableDesc": "설정을 읽고 사용자가 승인하는 변경 사항을 제안하는 도우미를 추가합니다. 사용자가 제공하는 공급업체(예: 지역 Ollama)가 필요합니다.",
"aiNote": "공급자를 추가하고 메시지를 보내기 전까지는 아무것도 전송되지 않습니다."
},
"admin": {
"aiGloballyEnabled": "AI 비서",
"aiGloballyEnabledDesc": "사용자가 AI 비서를 켜고 끌 수 있도록 하세요. AI 비서가 꺼져 있으면 모든 사용자에게 비서 기능이 숨겨지고 차단됩니다.",
"aiPrivateEndpoints": "개인 AI 호스트 허용",
"aiPrivateEndpointsDesc": "사용자가 공급자에게 연결할 수 있는 개인 네트워크의 호스트(예: 자체 호스팅 Ollama)를 나열하세요. 호스트는 쉼표로 구분하세요.",
"updateAiEnabledFailed": "AI 설정을 업데이트할 수 없습니다.",
"updateAiEndpointsFailed": "허용된 호스트를 업데이트할 수 없습니다."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Versturen",
"stop": "Stop",
"save": "Redden",
"cancel": "Annuleren",
"running": "rennen",
"approve": "Goedkeuren",
"reject": "Afwijzen",
"inputPlaceholder": "Vraag naar uw servers of beschrijf een voorgestelde wijziging.",
"selectProvider": "Selecteer een aanbieder",
"providerSettings": "Aanbiederinstellingen",
"addProvider": "Aanbieder toevoegen",
"editProvider": "Bewerk provider",
"removeProvider": "Verwijder provider",
"providerType": "Aanbieder",
"providerLabel": "Naam",
"providerLabelPlaceholder": "Bijvoorbeeld: Lokale Ollama",
"baseUrl": "Serveradres",
"apiKey": "API-sleutel",
"defaultModel": "Standaardmodel",
"defaultModelPlaceholder": "Bijvoorbeeld: llama3.1 of claude-opus-5",
"labelRequired": "Geef deze aanbieder een naam",
"providerSaveFailed": "De provider kon niet worden opgeslagen.",
"providerUpdated": "Aanbieder bijgewerkt",
"providerDeleteFailed": "De provider kon niet worden verwijderd.",
"privateEndpointHint": "IP-adressen op uw privénetwerk moeten eerst door een beheerder worden toegestaan.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropisch",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-compatibel",
"toolArguments": "Invoer",
"toolResult": "Resultaat",
"statusApplied": "Toegepast",
"statusRejected": "Afgewezen",
"proposalApplied": "Toegepaste wijziging",
"proposalApplyFailed": "De wijziging kon niet worden toegepast.",
"proposalRejectFailed": "Kon de wijziging niet afwijzen",
"enableTitle": "Schakel de AI-assistent in",
"enableDescription": "Laat de assistent je instellingen lezen en wijzigingen voorstellen die je vervolgens kunt goedkeuren.",
"readOnlyCommands": "Sta alleen-lezen diagnostische opdrachten toe.",
"readOnlyCommandsDescription": "De assistent kan een korte lijst met veilige commando's zoals df en uptime uitvoeren zonder telkens opnieuw toestemming te vragen. Alles wat een server wijzigt, wordt altijd eerst voorgesteld.",
"modelDetectFailed": "De aanbieder kon niet worden bereikt, daarom is dit een algemene lijst. U kunt elke modelnaam invoeren.",
"modelCustom": "Voer een modelnaam in",
"modelRefresh": "Vernieuw de lijst",
"modelPlaceholder": "Kies of typ een model",
"attachHint": "Typ @ om te verwijzen naar een host, codefragment of automatisering. Wijzigingen worden voorgesteld ter goedkeuring.",
"runCommandTitle": "Voer het commando uit",
"assistant": "AI-assistent",
"terminalPanelEmpty": "Vraag de assistent naar deze host. Alles wat de status wijzigt, vereist eerst uw goedkeuring.",
"terminalPanelPlaceholder": "Vraag naar deze host...",
"noProvidersConfigured": "Er is nog geen AI-provider geconfigureerd. Voeg er een toe via het tabblad AI-assistent.",
"runsInOpenTerminal": "Wordt uitgevoerd in uw geopende terminalsessie.",
"runInTerminal": "Loop"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AI-assistent",
"aiIntro": "Termix kan optioneel een AI-assistent bevatten. Je kunt dit later in je instellingen wijzigen.",
"aiSkipTitle": "Laat het weg",
"aiSkipDesc": "Termix werkt zoals het nu werkt. De assistent blijft verborgen.",
"aiEnableTitle": "Schakel het in.",
"aiEnableDesc": "Voegt een assistent toe die je instellingen leest en wijzigingen voorstelt die je kunt goedkeuren. Vereist een leverancier die je zelf aanlevert, zoals een lokale Ollama.",
"aiNote": "Er wordt niets verzonden totdat je een provider toevoegt en een bericht verstuurt."
},
"admin": {
"aiGloballyEnabled": "AI-assistent",
"aiGloballyEnabledDesc": "Gebruikers kunnen de AI-assistent inschakelen. Wanneer deze is uitgeschakeld, is de assistent verborgen en voor iedereen geblokkeerd.",
"aiPrivateEndpoints": "Toegestane privé-AI-hosts",
"aiPrivateEndpointsDesc": "Hosts op uw privénetwerk waarnaar gebruikers een provider kunnen verwijzen, zoals een zelfgehoste Ollama. Scheid ze met komma's.",
"updateAiEnabledFailed": "De AI-instelling kon niet worden bijgewerkt.",
"updateAiEndpointsFailed": "Het lukte niet om de lijst met toegestane hosts bij te werken."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Sende",
"stop": "Stoppe",
"save": "Spare",
"cancel": "Kansellere",
"running": "løping",
"approve": "Vedta",
"reject": "Avvis",
"inputPlaceholder": "Spør om servitørene dine, eller beskriv en endring du vil foreslå",
"selectProvider": "Velg en leverandør",
"providerSettings": "Leverandørinnstillinger",
"addProvider": "Legg til leverandør",
"editProvider": "Rediger leverandør",
"removeProvider": "Fjern leverandør",
"providerType": "Leverandør",
"providerLabel": "Navn",
"providerLabelPlaceholder": "For eksempel: Lokal Ollama",
"baseUrl": "Serveradresse",
"apiKey": "API-nøkkel",
"defaultModel": "Standardmodell",
"defaultModelPlaceholder": "For eksempel: llama3.1 eller claude-opus-5",
"labelRequired": "Gi denne leverandøren et navn",
"providerSaveFailed": "Kunne ikke lagre leverandøren",
"providerUpdated": "Leverandøren er oppdatert",
"providerDeleteFailed": "Kunne ikke fjerne leverandøren",
"privateEndpointHint": "Adresser på ditt private nettverk krever at en administrator tillater verten først.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropisk",
"providerOpenai": "Åpen kunstig intelligens",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-kompatibel",
"toolArguments": "Inndata",
"toolResult": "Resultat",
"statusApplied": "Anvendt",
"statusRejected": "Avvist",
"proposalApplied": "Endringen er brukt",
"proposalApplyFailed": "Kunne ikke bruke endringen",
"proposalRejectFailed": "Kunne ikke avvise endringen",
"enableTitle": "Aktiver AI-assistenten",
"enableDescription": "La assistenten lese oppsettet ditt og foreslå endringer du kan godkjenne.",
"readOnlyCommands": "Tillat skrivebeskyttede diagnostiske kommandoer",
"readOnlyCommandsDescription": "Assistenten kan kjøre en kort liste med sikre kommandoer som df og uptime uten å spørre hver gang. Alt som endrer en server blir alltid foreslått først.",
"modelDetectFailed": "Kunne ikke nå leverandøren, så dette er en generell liste. Du kan skrive inn et hvilket som helst modellnavn.",
"modelCustom": "Skriv inn et modellnavn",
"modelRefresh": "Oppdater listen",
"modelPlaceholder": "Velg eller skriv inn en modell",
"attachHint": "Skriv inn @ for å referere til en vert, et kodeutdrag eller en automatisering. Endringer foreslås som du kan godkjenne.",
"runCommandTitle": "Kjør kommandoen",
"assistant": "AI-assistent",
"terminalPanelEmpty": "Spør assistenten om denne verten. Alt som endrer status trenger din godkjenning først.",
"terminalPanelPlaceholder": "Spør om denne verten...",
"noProvidersConfigured": "Ingen AI-leverandør er konfigurert ennå. Legg til en i AI-assistent-fanen.",
"runsInOpenTerminal": "Kjører i din åpne terminaløkt.",
"runInTerminal": "Løp"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AI-assistent",
"aiIntro": "Termix kan inkludere en valgfri AI-assistent. Du kan endre dette senere i innstillingene dine.",
"aiSkipTitle": "La det være av",
"aiSkipDesc": "Termix fungerer som den gjør nå. Assistenten forblir skjult.",
"aiEnableTitle": "Slå den på",
"aiEnableDesc": "Legger til en assistent som leser oppsettet ditt og foreslår endringer du godkjenner. Krever en leverandør du oppgir, for eksempel en lokal Ollama.",
"aiNote": "Ingenting sendes noe sted før du legger til en leverandør og sender en melding."
},
"admin": {
"aiGloballyEnabled": "AI-assistent",
"aiGloballyEnabledDesc": "La brukere slå på AI-assistenten. Mens dette er av, er assistenten skjult og blokkert for alle.",
"aiPrivateEndpoints": "Tillatte private AI-verter",
"aiPrivateEndpointsDesc": "Verter på ditt private nettverk som brukere kan peke en leverandør til, for eksempel en selvhostet Ollama. Skill dem med komma.",
"updateAiEnabledFailed": "Kunne ikke oppdatere AI-innstillingen",
"updateAiEndpointsFailed": "Kunne ikke oppdatere de tillatte vertene"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Asystent",
"send": "Wyślij",
"stop": "Zatrzymaj",
"save": "Zapisz",
"cancel": "Anuluj",
"running": "działa",
"approve": "Zatwierdź",
"reject": "Odrzuć",
"inputPlaceholder": "Zapytaj o swoje serwery lub opisz proponowaną zmianę",
"selectProvider": "Wybierz dostawcę",
"providerSettings": "Ustawienia dostawcy",
"addProvider": "Dodaj dostawcę",
"editProvider": "Edytuj dostawcę",
"removeProvider": "Usuń dostawcę",
"providerType": "Dostawca",
"providerLabel": "Nazwa",
"providerLabelPlaceholder": "Na przykład: Ollama lokalnie",
"baseUrl": "Adres serwera",
"apiKey": "Klucz API",
"defaultModel": "Model domyślny",
"defaultModelPlaceholder": "Na przykład: llama3.1 lub claude-opus-5",
"labelRequired": "Podaj nazwę tego dostawcy",
"providerSaveFailed": "Nie udało się zapisać dostawcy",
"providerUpdated": "Zaktualizowano dostawcę",
"providerDeleteFailed": "Nie można usunąć dostawcy",
"privateEndpointHint": "Adresy w sieci prywatnej wymagają, aby administrator najpierw dopuścił dany host.",
"providerOllama": "Ollama",
"providerAnthropic": "Anthropic",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Kompatybilny z OpenAI",
"toolArguments": "Dane wejściowe",
"toolResult": "Wynik",
"statusApplied": "Zastosowano",
"statusRejected": "Odrzucono",
"proposalApplied": "Zastosowano zmianę",
"proposalApplyFailed": "Nie można zastosować zmiany",
"proposalRejectFailed": "Nie można odrzucić zmiany",
"enableTitle": "Włącz asystenta AI",
"enableDescription": "Pozwól asystentowi odczytać konfigurację i proponować zmiany do Twojego zatwierdzenia.",
"readOnlyCommands": "Zezwalaj na polecenia diagnostyczne tylko do odczytu",
"readOnlyCommandsDescription": "Asystent może uruchamiać krótką listę bezpiecznych poleceń, takich jak df czy uptime, bez pytania za każdym razem. Wszystko, co zmienia serwer, jest zawsze najpierw proponowane do zatwierdzenia.",
"modelDetectFailed": "Nie udało się skontaktować z dostawcą, więc jest to lista ogólna. Możesz wpisać dowolną nazwę modelu.",
"modelCustom": "Wprowadź nazwę modelu",
"modelRefresh": "Odśwież listę",
"modelPlaceholder": "Wybierz lub wpisz model",
"attachHint": "Wpisz @, aby odwołać się do hosta, snippetu lub automatyzacji. Zmiany są proponowane do Twojego zatwierdzenia.",
"runCommandTitle": "Uruchom polecenie",
"assistant": "Asystent AI",
"terminalPanelEmpty": "Zapytaj asystenta o tego hosta. Każda zmiana stanu wymaga Twojej zgody.",
"terminalPanelPlaceholder": "Zapytaj o tego gospodarza...",
"noProvidersConfigured": "Nie skonfigurowano jeszcze żadnego dostawcy AI. Dodaj go w zakładce Asystent AI.",
"runsInOpenTerminal": "Działa w otwartej sesji terminalowej.",
"runInTerminal": "Uruchomić"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "Asystent AI",
"aiIntro": "Termix może zawierać opcjonalnego asystenta AI. Możesz to później zmienić w ustawieniach.",
"aiSkipTitle": "Zostaw wyłączone",
"aiSkipDesc": "Termix działa tak jak teraz. Asystent pozostaje ukryty.",
"aiEnableTitle": "Włącz",
"aiEnableDesc": "Dodaje asystenta, który odczytuje Twoją konfigurację i sugeruje zmiany, które zatwierdzasz. Wymaga wskazanego przez Ciebie dostawcy, np. lokalnej Ollamy.",
"aiNote": "Dopóki nie dodasz dostawcy i nie wyślesz wiadomości, nic nie zostanie wysłane."
},
"admin": {
"aiGloballyEnabled": "Asystent AI",
"aiGloballyEnabledDesc": "Pozwól użytkownikom włączyć asystenta AI. Gdy ta opcja jest wyłączona, asystent jest ukryty i zablokowany dla wszystkich.",
"aiPrivateEndpoints": "Dozwolone prywatne hosty AI",
"aiPrivateEndpointsDesc": "Hosty w Twojej sieci prywatnej, na które użytkownicy mogą wskazać dostawcę, na przykład własna instancja Ollamy. Rozdziel je przecinkami.",
"updateAiEnabledFailed": "Nie można zaktualizować ustawień AI",
"updateAiEndpointsFailed": "Nie można zaktualizować dozwolonych hostów"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistente",
"send": "Enviar",
"stop": "Parar",
"save": "Salvar",
"cancel": "Cancelar",
"running": "correndo",
"approve": "Aprovar",
"reject": "Rejeitar",
"inputPlaceholder": "Pergunte sobre seus servidores ou descreva uma alteração que você gostaria de propor.",
"selectProvider": "Selecione um fornecedor",
"providerSettings": "Configurações do provedor",
"addProvider": "Adicionar provedor",
"editProvider": "Editar provedor",
"removeProvider": "Remover provedor",
"providerType": "Fornecedor",
"providerLabel": "Nome",
"providerLabelPlaceholder": "Por exemplo: Ollama local",
"baseUrl": "Endereço do servidor",
"apiKey": "Chave de API",
"defaultModel": "Modelo padrão",
"defaultModelPlaceholder": "Por exemplo: llama3.1 ou claude-opus-5",
"labelRequired": "Dê um nome a este fornecedor.",
"providerSaveFailed": "Não foi possível salvar o provedor.",
"providerUpdated": "Fornecedor atualizado",
"providerDeleteFailed": "Não foi possível remover o provedor.",
"privateEndpointHint": "Os endereços na sua rede privada precisam que um administrador autorize o host primeiro.",
"providerOllama": "Ollama",
"providerAnthropic": "Antrópico",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Compatível com OpenAI",
"toolArguments": "Entrada",
"toolResult": "Resultado",
"statusApplied": "Aplicado",
"statusRejected": "Rejeitado",
"proposalApplied": "Alteração aplicada",
"proposalApplyFailed": "Não foi possível aplicar a alteração.",
"proposalRejectFailed": "Não foi possível rejeitar a alteração.",
"enableTitle": "Ative o assistente de IA",
"enableDescription": "Deixe o assistente ler sua configuração e sugerir alterações para você aprovar.",
"readOnlyCommands": "Permitir comandos de diagnóstico somente leitura",
"readOnlyCommandsDescription": "O assistente pode executar uma pequena lista de comandos seguros, como `df` e `uptime`, sem perguntar a cada vez. Qualquer alteração que faça no servidor será sempre sugerida primeiro.",
"modelDetectFailed": "Não foi possível contatar o fornecedor, portanto esta é uma lista genérica. Você pode digitar o nome de qualquer modelo.",
"modelCustom": "Insira o nome do modelo",
"modelRefresh": "Atualizar lista",
"modelPlaceholder": "Selecione ou digite um modelo",
"attachHint": "Digite @ para fazer referência a um host, snippet ou automação. As alterações são propostas para sua aprovação.",
"runCommandTitle": "Executar comando",
"assistant": "Assistente de IA",
"terminalPanelEmpty": "Pergunte ao assistente sobre este anfitrião. Qualquer alteração de estado precisa da sua aprovação prévia.",
"terminalPanelPlaceholder": "Pergunte sobre este anfitrião...",
"noProvidersConfigured": "Nenhum provedor de IA está configurado ainda. Adicione um na aba do assistente de IA.",
"runsInOpenTerminal": "Executa na sua sessão de terminal aberta.",
"runInTerminal": "Correr"
},
"nav": {
"ai": "IA"
},
"onboarding": {
"aiTitle": "assistente de IA",
"aiIntro": "O Termix pode incluir um assistente de IA opcional. Você pode alterar isso posteriormente nas suas configurações.",
"aiSkipTitle": "Deixe desligado",
"aiSkipDesc": "O Termix funciona como está agora. O assistente permanece oculto.",
"aiEnableTitle": "Ligue-o",
"aiEnableDesc": "Adiciona um assistente que lê sua configuração e sugere alterações que você aprova. Requer um provedor fornecido por você, como um Ollama local.",
"aiNote": "Nada será enviado até que você adicione um provedor e envie uma mensagem."
},
"admin": {
"aiGloballyEnabled": "assistente de IA",
"aiGloballyEnabledDesc": "Permita que os usuários ativem o assistente de IA. Quando estiver desativado, o assistente ficará oculto e bloqueado para todos.",
"aiPrivateEndpoints": "Hosts de IA privados permitidos",
"aiPrivateEndpointsDesc": "Hosts em sua rede privada que os usuários podem usar como provedores, como um Ollama auto-hospedado. Separe-os com vírgulas.",
"updateAiEnabledFailed": "Não foi possível atualizar as configurações de IA.",
"updateAiEndpointsFailed": "Não foi possível atualizar os hosts permitidos."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistente",
"send": "Enviar",
"stop": "Parar",
"save": "Guardar",
"cancel": "Cancelar",
"running": "corrida",
"approve": "Aprovar",
"reject": "Rejeitar",
"inputPlaceholder": "Pergunte pelos seus servidores ou descreva uma alteração que gostaria de propor.",
"selectProvider": "Selecione um fornecedor",
"providerSettings": "Configurações do fornecedor",
"addProvider": "Adicionar provedor",
"editProvider": "Editar provedor",
"removeProvider": "Remover fornecedor",
"providerType": "Fornecedor",
"providerLabel": "Nome",
"providerLabelPlaceholder": "Por exemplo: Ollama local",
"baseUrl": "Endereço do servidor",
"apiKey": "Chave API",
"defaultModel": "Modelo padrão",
"defaultModelPlaceholder": "Por exemplo: llama3.1 ou claude-opus-5",
"labelRequired": "Dê um nome a este fornecedor.",
"providerSaveFailed": "Não foi possível guardar o fornecedor.",
"providerUpdated": "Fornecedor atualizado",
"providerDeleteFailed": "Não foi possível remover o fornecedor.",
"privateEndpointHint": "Os endereços na sua rede privada necessitam que um administrador autorize primeiro o host.",
"providerOllama": "Ollama",
"providerAnthropic": "Antrópico",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Compatível com OpenAI",
"toolArguments": "Entrada",
"toolResult": "Resultado",
"statusApplied": "Aplicado",
"statusRejected": "Rejeitado",
"proposalApplied": "Alteração aplicada",
"proposalApplyFailed": "Não foi possível aplicar a alteração.",
"proposalRejectFailed": "Não foi possível rejeitar a alteração.",
"enableTitle": "Ative o assistente de IA",
"enableDescription": "Deixe o assistente ler a sua configuração e sugerir alterações para que a aprove.",
"readOnlyCommands": "Permitir comandos de diagnóstico de leitura apenas",
"readOnlyCommandsDescription": "O assistente pode executar uma pequena lista de comandos seguros, como `df` e `uptime`, sem perguntar de cada vez. Qualquer alteração que faça no servidor será sempre sugerida em primeiro lugar.",
"modelDetectFailed": "Não foi possível contactar o fornecedor, pelo que esta é uma lista genérica. Pode digitar o nome de qualquer modelo.",
"modelCustom": "Introduza o nome do modelo",
"modelRefresh": "Atualizar lista",
"modelPlaceholder": "Selecione ou introduza um modelo",
"attachHint": "Digite @ para fazer referência a um host, snippet ou automação. As alterações são propostas para a sua aprovação.",
"runCommandTitle": "Executar comando",
"assistant": "Assistente de IA",
"terminalPanelEmpty": "Pergunte ao assistente sobre este anfitrião. Qualquer alteração de estado necessita da sua aprovação prévia.",
"terminalPanelPlaceholder": "Pergunte sobre este anfitrião...",
"noProvidersConfigured": "Nenhum fornecedor de IA está configurado ainda. Adicione um no separador do assistente de IA.",
"runsInOpenTerminal": "Executa na sua sessão de terminal aberta.",
"runInTerminal": "Correr"
},
"nav": {
"ai": "IA"
},
"onboarding": {
"aiTitle": "assistente de IA",
"aiIntro": "O Termix pode incluir um assistente de IA opcional. Pode alterar isso posteriormente nas suas definições.",
"aiSkipTitle": "Deixe desligado",
"aiSkipDesc": "O Termix funciona como está agora. O assistente permanece oculto.",
"aiEnableTitle": "Ligue-o",
"aiEnableDesc": "Adiciona um assistente que lê a sua configuração e sugere alterações que aprova. Requer um fornecedor fornecido por si, como um Ollama local.",
"aiNote": "Nada será enviado até que adicione um fornecedor e envie uma mensagem."
},
"admin": {
"aiGloballyEnabled": "assistente de IA",
"aiGloballyEnabledDesc": "Permita que os utilizadores ativem o assistente de IA. Quando estiver desativado, o assistente ficará oculto e bloqueado para todos.",
"aiPrivateEndpoints": "Hosts de IA privados permitidos",
"aiPrivateEndpointsDesc": "Hosts na sua rede privada que os utilizadores podem utilizar como fornecedores, como um Ollama auto-hospedado. Separe-os com vírgulas.",
"updateAiEnabledFailed": "Não foi possível atualizar as definições de IA.",
"updateAiEndpointsFailed": "Não foi possível atualizar os hosts permitidos."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Asistent",
"send": "Trimite",
"stop": "Stop",
"save": "Salva",
"cancel": "Anula",
"running": "funcţionare",
"approve": "Aproba",
"reject": "Respinge",
"inputPlaceholder": "Întreabă despre chelnerii tăi sau descrie o schimbare pe care o propui.",
"selectProvider": "Selectați un furnizor",
"providerSettings": "Setări furnizor",
"addProvider": "Adăugați furnizor",
"editProvider": "Editați furnizorul",
"removeProvider": "Eliminați furnizorul",
"providerType": "Furnizor",
"providerLabel": "Nume",
"providerLabelPlaceholder": "De exemplu: Ollama locală",
"baseUrl": "Adresa serverului",
"apiKey": "Cheie API",
"defaultModel": "Model implicit",
"defaultModelPlaceholder": "De exemplu: llama3.1 sau claude-opus-5",
"labelRequired": "Dați un nume acestui furnizor",
"providerSaveFailed": "Nu s-a putut salva furnizorul",
"providerUpdated": "Furnizor actualizat",
"providerDeleteFailed": "Nu s-a putut elimina furnizorul",
"privateEndpointHint": "Adresele din rețeaua privată necesită mai întâi permisiunea unui administrator pentru gazdă.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropic",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemeni",
"providerOpenaiCompatible": "Compatibil cu OpenAI",
"toolArguments": "Intrare",
"toolResult": "Rezultat",
"statusApplied": "Aplicat",
"statusRejected": "Respins",
"proposalApplied": "Modificare aplicată",
"proposalApplyFailed": "Nu s-a putut aplica modificarea",
"proposalRejectFailed": "Nu s-a putut respinge modificarea",
"enableTitle": "Activează asistentul AI",
"enableDescription": "Permiteți asistentului să citească configurația și să sugereze modificări pentru aprobare.",
"readOnlyCommands": "Permite comenzi de diagnosticare doar în citire",
"readOnlyCommandsDescription": "Asistentul poate rula o listă scurtă de comenzi sigure, cum ar fi df și uptime, fără a întreba de fiecare dată. Orice modificare a unui server este întotdeauna propusă prima.",
"modelDetectFailed": "Nu s-a putut contacta furnizorul, așadar aceasta este o listă generală. Puteți introduce orice nume de model.",
"modelCustom": "Introduceți un nume de model",
"modelRefresh": "Reîmprospătați lista",
"modelPlaceholder": "Alegeți sau introduceți un model",
"attachHint": "Tastați @ pentru a face referire la o gazdă, un fragment de cod sau o automatizare. Sunt propuse modificări pentru aprobare.",
"runCommandTitle": "Comandă de rulare",
"assistant": "Asistent AI",
"terminalPanelEmpty": "Întreabă asistentul despre această gazdă. Orice modificare a stării necesită mai întâi aprobarea ta.",
"terminalPanelPlaceholder": "Întreabă despre această gazdă...",
"noProvidersConfigured": "Încă nu este configurat niciun furnizor de inteligență artificială. Adăugați unul în fila Asistent de inteligență artificială.",
"runsInOpenTerminal": "Rulează în sesiunea de terminal deschisă.",
"runInTerminal": "Aleargă"
},
"nav": {
"ai": "IA"
},
"onboarding": {
"aiTitle": "Asistent AI",
"aiIntro": "Termix poate include un asistent AI opțional. Puteți modifica acest lucru ulterior în setări.",
"aiSkipTitle": "Lasă-l deoparte",
"aiSkipDesc": "Termix funcționează ca acum. Asistentul rămâne ascuns.",
"aiEnableTitle": "Pornește-l",
"aiEnableDesc": "Adaugă un asistent care citește configurația și sugerează modificări pe care le aprobi. Necesită un furnizor furnizat de tine, cum ar fi un Ollama local.",
"aiNote": "Nimic nu este trimis nicăieri până nu adăugați un furnizor și nu trimiteți un mesaj."
},
"admin": {
"aiGloballyEnabled": "Asistent AI",
"aiGloballyEnabledDesc": "Permiteți utilizatorilor să activeze asistentul AI. Cât timp este dezactivat, asistentul este ascuns și blocat pentru toată lumea.",
"aiPrivateEndpoints": "Gazde private AI permise",
"aiPrivateEndpointsDesc": "Gazde din rețeaua privată către care utilizatorii pot direcționa un furnizor, cum ar fi un Ollama auto-găzduit. Separați-le prin virgule.",
"updateAiEnabledFailed": "Nu s-a putut actualiza setarea AI",
"updateAiEndpointsFailed": "Nu s-au putut actualiza gazdele permise"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Помощник",
"send": "Отправлять",
"stop": "Останавливаться",
"save": "Сохранять",
"cancel": "Отмена",
"running": "бег",
"approve": "Утвердить",
"reject": "Отклонять",
"inputPlaceholder": "Поинтересуйтесь состоянием ваших серверов или предложите внести изменения.",
"selectProvider": "Выберите поставщика",
"providerSettings": "Настройки поставщика",
"addProvider": "Добавить поставщика",
"editProvider": "Поставщик редактирования",
"removeProvider": "Удалить поставщика",
"providerType": "Поставщик",
"providerLabel": "Имя",
"providerLabelPlaceholder": "Например: Местная лама",
"baseUrl": "Адрес сервера",
"apiKey": "ключ API",
"defaultModel": "Модель по умолчанию",
"defaultModelPlaceholder": "Например: llama3.1 или claude-opus-5",
"labelRequired": "Укажите имя этого поставщика услуг.",
"providerSaveFailed": "Не удалось сохранить поставщика услуг.",
"providerUpdated": "Поставщик обновлен",
"providerDeleteFailed": "Не удалось удалить поставщика услуг.",
"privateEndpointHint": "Для доступа к адресам в вашей частной сети необходимо предварительное разрешение администратора.",
"providerOllama": "оллама",
"providerAnthropic": "Антропический",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Совместимость с OpenAI",
"toolArguments": "Вход",
"toolResult": "Результат",
"statusApplied": "Применяемый",
"statusRejected": "Отклоненный",
"proposalApplied": "Внесены изменения.",
"proposalApplyFailed": "Не удалось применить изменения.",
"proposalRejectFailed": "Не мог отклонить изменение.",
"enableTitle": "Включите ИИ-помощника",
"enableDescription": "Позвольте ассистенту ознакомиться с вашими настройками и предложить изменения для утверждения.",
"readOnlyCommands": "Разрешить диагностические команды только для чтения",
"readOnlyCommandsDescription": "Ассистент может выполнить короткий список безопасных команд, таких как df и uptime, не запрашивая подтверждения каждый раз. Любая команда, изменяющая состояние сервера, всегда предлагается в первую очередь.",
"modelDetectFailed": "Не удалось связаться с поставщиком, поэтому это общий список. Вы можете ввести любое название модели.",
"modelCustom": "Введите название модели",
"modelRefresh": "Обновить список",
"modelPlaceholder": "Выберите или введите модель",
"attachHint": "Введите @ для ссылки на хост, фрагмент кода или автоматизацию. Предлагаются изменения для вашего утверждения.",
"runCommandTitle": "Выполните команду",
"assistant": "ИИ-помощник",
"terminalPanelEmpty": "Спросите помощника об этом хосте. Любое изменение состояния требует вашего предварительного одобрения.",
"terminalPanelPlaceholder": "Спросите об этом хостинге...",
"noProvidersConfigured": "Поставщик ИИ пока не настроен. Добавьте его на вкладке «Помощник ИИ».",
"runsInOpenTerminal": "Запускается в открытой терминальной сессии.",
"runInTerminal": "Бегать"
},
"nav": {
"ai": "ИИ"
},
"onboarding": {
"aiTitle": "ИИ-помощник",
"aiIntro": "В Termix можно добавить опционального ИИ-помощника. Это можно изменить позже в настройках.",
"aiSkipTitle": "Оставьте это выключенным.",
"aiSkipDesc": "Termix работает так, как сейчас. Ассистент остается скрытым.",
"aiEnableTitle": "Включите его",
"aiEnableDesc": "Добавляет помощника, который считывает ваши настройки и предлагает изменения, которые вы одобряете. Требуется поставщик, которого вы предоставляете, например, локальный Ollama.",
"aiNote": "Ничего никуда не отправляется, пока вы не добавите поставщика услуг и не отправите сообщение."
},
"admin": {
"aiGloballyEnabled": "ИИ-помощник",
"aiGloballyEnabledDesc": "Разрешите пользователям включить голосового помощника. Если он выключен, помощник скрыт и заблокирован для всех.",
"aiPrivateEndpoints": "Разрешены частные хосты ИИ",
"aiPrivateEndpointsDesc": "Укажите хосты в вашей частной сети, на которые пользователи могут направлять запросы провайдера, например, самостоятельно размещенный Ollama. Разделяйте их запятыми.",
"updateAiEnabledFailed": "Не удалось обновить настройки ИИ.",
"updateAiEndpointsFailed": "Не удалось обновить список разрешенных хостов."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Асистент",
"send": "Пошаљи",
"stop": "Заустави",
"save": "Сачувај",
"cancel": "Откажи",
"running": "трчање",
"approve": "Одобри",
"reject": "Одбиј",
"inputPlaceholder": "Питајте о својим серверима или опишите промену коју желите да предложите",
"selectProvider": "Изаберите добављача",
"providerSettings": "Подешавања добављача",
"addProvider": "Додај добављача",
"editProvider": "Измени добављача",
"removeProvider": "Уклони добављача",
"providerType": "Провајдер",
"providerLabel": "Име",
"providerLabelPlaceholder": "На пример: Локална Олама",
"baseUrl": "Адреса сервера",
"apiKey": "API кључ",
"defaultModel": "Подразумевани модел",
"defaultModelPlaceholder": "На пример: llama3.1 или claude-opus-5",
"labelRequired": "Дајте име овом добављачу",
"providerSaveFailed": "Није могуће сачувати добављача",
"providerUpdated": "Провајдер је ажуриран",
"providerDeleteFailed": "Није могуће уклонити добављача",
"privateEndpointHint": "Адресе на вашој приватној мрежи прво треба да дозволе администратору хоста.",
"providerOllama": "Олама",
"providerAnthropic": "Антропски",
"providerOpenai": "OpenAI",
"providerGemini": "Гугл Џемини",
"providerOpenaiCompatible": "Компатибилно са OpenAI-ом",
"toolArguments": "Унос",
"toolResult": "Резултат",
"statusApplied": "Примењено",
"statusRejected": "Одбијено",
"proposalApplied": "Промена је примењена",
"proposalApplyFailed": "Није могуће применити промену",
"proposalRejectFailed": "Није могуће одбити промену",
"enableTitle": "Омогућите вештачку интелигенцију (AI) асистента",
"enableDescription": "Нека асистент прочита ваша подешавања и предложи измене које ћете одобрити.",
"readOnlyCommands": "Дозволи дијагностичке команде само за читање",
"readOnlyCommandsDescription": "Асистент може да покрене кратку листу безбедних команди као што су df и uptime без сваког питања. Све што мења сервер се увек прво предлаже.",
"modelDetectFailed": "Нисмо могли да контактирамо добављача, тако да је ово општа листа. Можете да унесете било који назив модела.",
"modelCustom": "Унесите назив модела",
"modelRefresh": "Освежи листу",
"modelPlaceholder": "Изаберите или унесите модел",
"attachHint": "Унесите @ да бисте референцирали хост, исечак кода или аутоматизацију. Промене се предлажу да бисте их одобрили.",
"runCommandTitle": "Покрени команду",
"assistant": "АИ асистент",
"terminalPanelEmpty": "Питајте асистента о овом домаћину. Све што мења стање прво захтева ваше одобрење.",
"terminalPanelPlaceholder": "Питајте о овом домаћину...",
"noProvidersConfigured": "Још увек није конфигурисан ниједан добављач вештачке интелигенције. Додајте га на картици помоћника за вештачку интелигенцију.",
"runsInOpenTerminal": "Покреће се у вашој отвореној терминалној сесији.",
"runInTerminal": "Трчи"
},
"nav": {
"ai": "Вештачка интелигенција"
},
"onboarding": {
"aiTitle": "Асистент за вештачку интелигенцију",
"aiIntro": "Termix може да укључи опционог AI асистента. Ово можете касније променити у подешавањима.",
"aiSkipTitle": "Остави то искључено",
"aiSkipDesc": "Термикс ради као и сада. Асистент остаје скривен.",
"aiEnableTitle": "Укључи га",
"aiEnableDesc": "Додаје асистента који чита ваша подешавања и предлаже измене које одобравате. Потребан је добављач кога ви обезбедите, као што је локални Олама.",
"aiNote": "Ништа се не шаље нигде док не додате провајдера и не пошаљете поруку."
},
"admin": {
"aiGloballyEnabled": "Асистент за вештачку интелигенцију",
"aiGloballyEnabledDesc": "Дозволите корисницима да укључе вештачку интелигенцију асистента. Док је ово искључено, асистент је скривен и блокиран за све.",
"aiPrivateEndpoints": "Дозвољени приватни AI хостови",
"aiPrivateEndpointsDesc": "Домаћини на вашој приватној мрежи на које корисници могу усмерити провајдера, као што је самостално хостована Ollama. Раздвојите их зарезима.",
"updateAiEnabledFailed": "Није могуће ажурирати подешавање вештачке интелигенције",
"updateAiEndpointsFailed": "Није могуће ажурирати дозвољене хостове"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Assistent",
"send": "Skicka",
"stop": "Stopp",
"save": "Spara",
"cancel": "Avboka",
"running": "spring",
"approve": "Godkänna",
"reject": "Avvisa",
"inputPlaceholder": "Fråga om era servrar, eller beskriv en förändring att föreslå",
"selectProvider": "Välj en leverantör",
"providerSettings": "Leverantörsinställningar",
"addProvider": "Lägg till leverantör",
"editProvider": "Redigera leverantör",
"removeProvider": "Ta bort leverantör",
"providerType": "Leverantör",
"providerLabel": "Namn",
"providerLabelPlaceholder": "Till exempel: Lokal Ollama",
"baseUrl": "Serveradress",
"apiKey": "API-nyckel",
"defaultModel": "Standardmodell",
"defaultModelPlaceholder": "Till exempel: llama3.1 eller claude-opus-5",
"labelRequired": "Ge den här leverantören ett namn",
"providerSaveFailed": "Kunde inte spara leverantören",
"providerUpdated": "Leverantören uppdaterad",
"providerDeleteFailed": "Kunde inte ta bort leverantören",
"privateEndpointHint": "Adresser i ditt privata nätverk behöver en administratör som först tillåter värden.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropisk",
"providerOpenai": "Öppen AI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI-kompatibel",
"toolArguments": "Input",
"toolResult": "Resultat",
"statusApplied": "Tillämpad",
"statusRejected": "Avvisad",
"proposalApplied": "Ändring tillämpad",
"proposalApplyFailed": "Kunde inte tillämpa ändringen",
"proposalRejectFailed": "Kunde inte avvisa ändringen",
"enableTitle": "Aktivera AI-assistenten",
"enableDescription": "Låt assistenten läsa dina inställningar och föreslå ändringar som du kan godkänna.",
"readOnlyCommands": "Tillåt skrivskyddade diagnostikkommandon",
"readOnlyCommandsDescription": "Assistenten kan köra en kort lista med säkra kommandon som df och uptime utan att fråga varje gång. Allt som ändrar en server föreslås alltid först.",
"modelDetectFailed": "Kunde inte nå leverantören, så detta är en allmän lista. Du kan skriva in vilket modellnamn som helst.",
"modelCustom": "Ange ett modellnamn",
"modelRefresh": "Uppdatera listan",
"modelPlaceholder": "Välj eller skriv en modell",
"attachHint": "Skriv @ för att referera till en värd, ett kodavsnitt eller en automatisering. Ändringar föreslås för dig att godkänna.",
"runCommandTitle": "Kör kommando",
"assistant": "AI-assistent",
"terminalPanelEmpty": "Fråga assistenten om den här värden. Allt som ändras kräver ditt godkännande först.",
"terminalPanelPlaceholder": "Fråga om den här värden...",
"noProvidersConfigured": "Ingen AI-leverantör är konfigurerad ännu. Lägg till en på fliken AI-assistent.",
"runsInOpenTerminal": "Körs i din öppna terminalsession.",
"runInTerminal": "Sikt"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AI-assistent",
"aiIntro": "Termix kan inkludera en valfri AI-assistent. Du kan ändra detta senare i dina inställningar.",
"aiSkipTitle": "Lämna den av",
"aiSkipDesc": "Termix fungerar som den gör nu. Assistenten förblir dold.",
"aiEnableTitle": "Slå på den",
"aiEnableDesc": "Lägger till en assistent som läser din installation och föreslår ändringar som du godkänner. Kräver en leverantör som du tillhandahåller, till exempel en lokal Ollama.",
"aiNote": "Ingenting skickas någonstans förrän du lägger till en leverantör och skickar ett meddelande."
},
"admin": {
"aiGloballyEnabled": "AI-assistent",
"aiGloballyEnabledDesc": "Låt användare aktivera AI-assistenten. Medan detta är avstängt är assistenten dold och blockerad för alla.",
"aiPrivateEndpoints": "Tillåtna privata AI-värdar",
"aiPrivateEndpointsDesc": "Värdar på ditt privata nätverk som användare kan hänvisa en leverantör till, till exempel en egenhostad Ollama. Separera dem med kommatecken.",
"updateAiEnabledFailed": "Kunde inte uppdatera AI-inställningen",
"updateAiEndpointsFailed": "Kunde inte uppdatera de tillåtna värdarna"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "ผู้ช่วย",
"send": "ส่ง",
"stop": "หยุด",
"save": "บันทึก",
"cancel": "ยกเลิก",
"running": "วิ่ง",
"approve": "อนุมัติ",
"reject": "ปฏิเสธ",
"inputPlaceholder": "สอบถามเกี่ยวกับเซิร์ฟเวอร์ของคุณ หรืออธิบายการเปลี่ยนแปลงที่คุณต้องการเสนอแนะ",
"selectProvider": "เลือกผู้ให้บริการ",
"providerSettings": "การตั้งค่าผู้ให้บริการ",
"addProvider": "เพิ่มผู้ให้บริการ",
"editProvider": "แก้ไขผู้ให้บริการ",
"removeProvider": "ลบผู้ให้บริการ",
"providerType": "ผู้ให้บริการ",
"providerLabel": "ชื่อ",
"providerLabelPlaceholder": "ตัวอย่างเช่น: Ollama ท้องถิ่น",
"baseUrl": "ที่อยู่เซิร์ฟเวอร์",
"apiKey": "คีย์ API",
"defaultModel": "โมเดลเริ่มต้น",
"defaultModelPlaceholder": "ตัวอย่างเช่น: llama3.1 หรือ claude-opus-5",
"labelRequired": "ตั้งชื่อให้ผู้ให้บริการรายนี้",
"providerSaveFailed": "ไม่สามารถบันทึกผู้ให้บริการได้",
"providerUpdated": "ผู้ให้บริการได้รับการอัปเดตแล้ว",
"providerDeleteFailed": "ไม่สามารถลบผู้ให้บริการได้",
"privateEndpointHint": "การเข้าถึงที่อยู่บนเครือข่ายส่วนตัวของคุณนั้น จำเป็นต้องให้ผู้ดูแลระบบอนุญาตก่อน",
"providerOllama": "โอลาม่า",
"providerAnthropic": "แอนโทรปิก",
"providerOpenai": "โอเพ่นไอ",
"providerGemini": "กูเกิล เจมินี",
"providerOpenaiCompatible": "เข้ากันได้กับ OpenAI",
"toolArguments": "ป้อนข้อมูล",
"toolResult": "ผลลัพธ์",
"statusApplied": "สมัครแล้ว",
"statusRejected": "ถูกปฏิเสธ",
"proposalApplied": "มีการเปลี่ยนแปลงเกิดขึ้น",
"proposalApplyFailed": "ไม่สามารถทำการเปลี่ยนแปลงได้",
"proposalRejectFailed": "ไม่สามารถปฏิเสธการเปลี่ยนแปลงได้",
"enableTitle": "เปิดใช้งานผู้ช่วย AI",
"enableDescription": "ให้ผู้ช่วยอ่านการตั้งค่าของคุณและแนะนำการเปลี่ยนแปลงเพื่อให้คุณอนุมัติ",
"readOnlyCommands": "อนุญาตคำสั่งวินิจฉัยแบบอ่านอย่างเดียว",
"readOnlyCommandsDescription": "ผู้ช่วยอาจเรียกใช้คำสั่งที่ปลอดภัยเพียงไม่กี่คำสั่ง เช่น df และ uptime โดยไม่ต้องถามทุกครั้ง คำสั่งใดๆ ที่เปลี่ยนแปลงสถานะของเซิร์ฟเวอร์จะถูกเสนอให้ดำเนินการก่อนเสมอ",
"modelDetectFailed": "ไม่สามารถติดต่อผู้ให้บริการได้ ดังนั้นนี่จึงเป็นรายการทั่วไป คุณสามารถพิมพ์ชื่อรุ่นใดก็ได้",
"modelCustom": "ป้อนชื่อรุ่น",
"modelRefresh": "รีเฟรชรายการ",
"modelPlaceholder": "เลือกหรือพิมพ์แบบจำลอง",
"attachHint": "พิมพ์ @ เพื่ออ้างอิงถึงโฮสต์ สนิปเป็ต หรือระบบอัตโนมัติ การเปลี่ยนแปลงที่เสนอจะถูกนำมาให้คุณอนุมัติ",
"runCommandTitle": "เรียกใช้คำสั่ง",
"assistant": "ผู้ช่วย AI",
"terminalPanelEmpty": "สอบถามผู้ช่วยเกี่ยวกับโฮสต์นี้ การเปลี่ยนแปลงสถานะใดๆ ต้องได้รับการอนุมัติจากคุณก่อน",
"terminalPanelPlaceholder": "สอบถามเกี่ยวกับโฮสต์รายนี้...",
"noProvidersConfigured": "ยังไม่ได้ตั้งค่าผู้ให้บริการ AI เพิ่มผู้ให้บริการในแท็บผู้ช่วย AI",
"runsInOpenTerminal": "ทำงานในเซสชันเทอร์มินัลที่เปิดอยู่ของคุณ",
"runInTerminal": "วิ่ง"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "ผู้ช่วย AI",
"aiIntro": "Termix อาจมีผู้ช่วย AI เสริม คุณสามารถเปลี่ยนการตั้งค่าได้ในภายหลัง",
"aiSkipTitle": "ปิดมันไว้",
"aiSkipDesc": "Termix ทำงานเหมือนเดิมทุกประการ ตัวช่วยยังคงซ่อนอยู่",
"aiEnableTitle": "เปิดใช้งาน",
"aiEnableDesc": "เพิ่มผู้ช่วยที่จะอ่านการตั้งค่าของคุณและแนะนำการเปลี่ยนแปลงที่คุณต้องอนุมัติ จำเป็นต้องมีผู้ให้บริการที่คุณจัดหาให้ เช่น Ollama ในพื้นที่ของคุณ",
"aiNote": "จะไม่มีการส่งข้อมูลใดๆ จนกว่าคุณจะเพิ่มผู้ให้บริการและส่งข้อความ"
},
"admin": {
"aiGloballyEnabled": "ผู้ช่วย AI",
"aiGloballyEnabledDesc": "อนุญาตให้ผู้ใช้เปิดใช้งานผู้ช่วย AI ได้ เมื่อปิดใช้งานอยู่ ผู้ช่วยจะถูกซ่อนและบล็อกไม่ให้ใครเห็น",
"aiPrivateEndpoints": "อนุญาตให้โฮสต์ AI ส่วนตัว",
"aiPrivateEndpointsDesc": "โฮสต์บนเครือข่ายส่วนตัวของคุณที่ผู้ใช้สามารถชี้ไปยังผู้ให้บริการได้ เช่น Ollama ที่ติดตั้งเอง คั่นด้วยเครื่องหมายจุลภาค",
"updateAiEnabledFailed": "ไม่สามารถอัปเดตการตั้งค่า AI ได้",
"updateAiEndpointsFailed": "ไม่สามารถอัปเดตโฮสต์ที่อนุญาตได้"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Asistan",
"send": "Göndermek",
"stop": "Durmak",
"save": "Kaydetmek",
"cancel": "İptal etmek",
"running": "koşma",
"approve": "Onaylamak",
"reject": "Reddetmek",
"inputPlaceholder": "Sunucularınız hakkında bilgi isteyin veya önereceğiniz bir değişikliği açıklayın.",
"selectProvider": "Bir sağlayıcı seçin",
"providerSettings": "Sağlayıcı ayarları",
"addProvider": "Sağlayıcı ekle",
"editProvider": "Sağlayıcıyı düzenle",
"removeProvider": "Sağlayıcıyı kaldır",
"providerType": "Sağlayıcı",
"providerLabel": "İsim",
"providerLabelPlaceholder": "Örneğin: Yerel Ollama",
"baseUrl": "Sunucu adresi",
"apiKey": "API anahtarı",
"defaultModel": "Varsayılan model",
"defaultModelPlaceholder": "Örneğin: llama3.1 veya claude-opus-5",
"labelRequired": "Bu sağlayıcıya bir isim verin.",
"providerSaveFailed": "Sağlayıcı kaydedilemedi.",
"providerUpdated": "Sağlayıcı güncellendi.",
"providerDeleteFailed": "Sağlayıcı kaldırılamadı.",
"privateEndpointHint": "Özel ağınızdaki adreslerin kullanılabilmesi için öncelikle yöneticinin sunucuya izin vermesi gerekmektedir.",
"providerOllama": "Ollama",
"providerAnthropic": "Antropik",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI uyumlu",
"toolArguments": "Giriş",
"toolResult": "Sonuç",
"statusApplied": "Uygulandı",
"statusRejected": "Reddedilmiş",
"proposalApplied": "Değişiklik uygulandı.",
"proposalApplyFailed": "Değişiklik uygulanamadı.",
"proposalRejectFailed": "Değişikliği reddedemedim.",
"enableTitle": "Yapay zekâ asistanını etkinleştirin",
"enableDescription": "Asistanın ayarlarınızı okumasını ve onaylamanız için değişiklik önerileri sunmasını sağlayın.",
"readOnlyCommands": "Yalnızca okuma amaçlı tanı komutlarına izin ver",
"readOnlyCommandsDescription": "Yardımcı program, her seferinde sormadan df ve uptime gibi kısa bir güvenli komut listesini çalıştırabilir. Sunucuda değişiklik yapan her şey her zaman önce önerilir.",
"modelDetectFailed": "Sağlayıcıya ulaşılamadı, bu nedenle bu genel bir listedir. İstediğiniz model adını yazabilirsiniz.",
"modelCustom": "Bir model adı girin",
"modelRefresh": "Listeyi yenile",
"modelPlaceholder": "Bir model seçin veya yazın.",
"attachHint": "Bir sunucuya, kod parçasına veya otomasyona referans vermek için @ işaretini kullanın. Onaylamanız için değişiklikler öneriliyor.",
"runCommandTitle": "Çalıştır komutu",
"assistant": "Yapay Zeka Asistanı",
"terminalPanelEmpty": "Bu ev sahibi hakkında asistana danışın. Durum değişikliği gerektiren her şey önce sizin onayınızı gerektirir.",
"terminalPanelPlaceholder": "Bu ev sahibi hakkında bilgi isteyin...",
"noProvidersConfigured": "Henüz hiçbir yapay zeka sağlayıcısı yapılandırılmadı. Yapay zeka asistanı sekmesinden bir tane ekleyin.",
"runsInOpenTerminal": "Açık terminal oturumunuzda çalışır.",
"runInTerminal": "Koşmak"
},
"nav": {
"ai": "Yapay zeka"
},
"onboarding": {
"aiTitle": "Yapay zeka asistanı",
"aiIntro": "Termix isteğe bağlı bir yapay zeka asistanı içerebilir. Bunu daha sonra ayarlarınızdan değiştirebilirsiniz.",
"aiSkipTitle": "Kapalı bırakın",
"aiSkipDesc": "Termix şu anki haliyle çalışıyor. Asistan gizli kalıyor.",
"aiEnableTitle": "Açın",
"aiEnableDesc": "Ayarlarınızı okuyan ve onaylamanız halinde değişiklikler öneren bir asistan ekler. Yerel bir Ollama gibi, sizin sağladığınız bir sağlayıcıya ihtiyaç duyar.",
"aiNote": "Bir sağlayıcı ekleyip mesaj gönderene kadar hiçbir şey hiçbir yere gönderilmez."
},
"admin": {
"aiGloballyEnabled": "Yapay zeka asistanı",
"aiGloballyEnabledDesc": "Kullanıcıların yapay zeka asistanını açmasına izin verin. Bu özellik kapalıyken, asistan gizlenir ve herkes için engellenir.",
"aiPrivateEndpoints": "Özel yapay zeka sunucularına izin verildi.",
"aiPrivateEndpointsDesc": "Kullanıcıların bir sağlayıcıya yönlendirebileceği, özel ağınızdaki sunucular (örneğin, kendi sunucunuzda barındırdığınız Ollama). Bunları virgülle ayırın.",
"updateAiEnabledFailed": "Yapay zeka ayarları güncellenemedi.",
"updateAiEndpointsFailed": "İzin verilen sunucular güncellenemedi."
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Помічник",
"send": "Надіслати",
"stop": "СТІЙ",
"save": "Зберегти",
"cancel": "Скасувати",
"running": "біг",
"approve": "Схвалити",
"reject": "Відхилити",
"inputPlaceholder": "Запитайте про ваші сервери або опишіть зміну, яку ви хочете запропонувати",
"selectProvider": "Виберіть постачальника",
"providerSettings": "Налаштування постачальника",
"addProvider": "Додати постачальника",
"editProvider": "Редагувати постачальника",
"removeProvider": "Видалити постачальника",
"providerType": "Постачальник",
"providerLabel": "Ім'я",
"providerLabelPlaceholder": "Наприклад: Місцевий Оллама",
"baseUrl": "Адреса сервера",
"apiKey": "Ключ API",
"defaultModel": "Модель за замовчуванням",
"defaultModelPlaceholder": "Наприклад: llama3.1 або claude-opus-5",
"labelRequired": "Введіть назву цього постачальника",
"providerSaveFailed": "Не вдалося зберегти постачальника",
"providerUpdated": "Постачальника оновлено",
"providerDeleteFailed": "Не вдалося видалити постачальника",
"privateEndpointHint": "Адреси у вашій приватній мережі спочатку потребують дозволу адміністратора хоста.",
"providerOllama": "Оллама",
"providerAnthropic": "Антропний",
"providerOpenai": "OpenAI",
"providerGemini": "Google Близнюки",
"providerOpenaiCompatible": "Сумісний з OpenAI",
"toolArguments": "Вхід",
"toolResult": "Результат",
"statusApplied": "Застосовано",
"statusRejected": "Відхилено",
"proposalApplied": "Зміну застосовано",
"proposalApplyFailed": "Не вдалося застосувати зміну",
"proposalRejectFailed": "Не вдалося відхилити зміну",
"enableTitle": "Увімкнути помічника зі штучним інтелектом",
"enableDescription": "Дозвольте асистенту прочитати ваші налаштування та запропонувати зміни для вашого схвалення.",
"readOnlyCommands": "Дозволити діагностичні команди лише для читання",
"readOnlyCommandsDescription": "Помічник може виконувати короткий список безпечних команд, таких як df та uptime, не запитуючи щоразу. Будь-які зміни на сервері завжди пропонуються першими.",
"modelDetectFailed": "Не вдалося зв’язатися з постачальником, тому це загальний список. Ви можете ввести будь-яку назву моделі.",
"modelCustom": "Введіть назву моделі",
"modelRefresh": "Оновити список",
"modelPlaceholder": "Виберіть або введіть модель",
"attachHint": "Введіть @, щоб посилатися на хост, фрагмент або автоматизацію. Зміни пропонуються для вашого схвалення.",
"runCommandTitle": "Виконати команду",
"assistant": "Помічник зі штучним інтелектом",
"terminalPanelEmpty": "Запитайте асистента про цього хоста. Будь-яка зміна стану потребує вашого попереднього схвалення.",
"terminalPanelPlaceholder": "Запитайте про цього хоста...",
"noProvidersConfigured": "Постачальника штучного інтелекту ще не налаштовано. Додайте його на вкладці помічника штучного інтелекту.",
"runsInOpenTerminal": "Працює у вашому відкритому термінальному сеансі.",
"runInTerminal": "Бігти"
},
"nav": {
"ai": "Штучний інтелект"
},
"onboarding": {
"aiTitle": "Асистент зі штучним інтелектом",
"aiIntro": "Termix може включати додатковий помічник зі штучним інтелектом. Ви можете змінити це пізніше в налаштуваннях.",
"aiSkipTitle": "Залиште це вимкненим",
"aiSkipDesc": "Termix працює як і зараз. Помічник залишається прихованим.",
"aiEnableTitle": "Увімкніть це",
"aiEnableDesc": "Додає помічника, який читає ваші налаштування та пропонує зміни, які ви схвалюєте. Потрібен постачальник, якого ви надаєте, наприклад, місцевий Ollama.",
"aiNote": "Нічого нікуди не надсилається, доки ви не додасте постачальника та не надішлете повідомлення."
},
"admin": {
"aiGloballyEnabled": "Асистент зі штучним інтелектом",
"aiGloballyEnabledDesc": "Дозвольте користувачам увімкнути помічника зі штучним інтелектом. Коли це вимкнено, помічник прихований і заблокований для всіх.",
"aiPrivateEndpoints": "Дозволені приватні хости ШІ",
"aiPrivateEndpointsDesc": "Хости у вашій приватній мережі, на які користувачі можуть вказувати провайдера, наприклад, Ollama з власним хостингом. Розділяйте їх комами.",
"updateAiEnabledFailed": "Не вдалося оновити налаштування штучного інтелекту",
"updateAiEndpointsFailed": "Не вдалося оновити дозволені хости"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "Trợ lý",
"send": "Gửi",
"stop": "Dừng lại",
"save": "Cứu",
"cancel": "Hủy bỏ",
"running": "đang chạy",
"approve": "Chấp thuận",
"reject": "Từ chối",
"inputPlaceholder": "Hãy hỏi về máy chủ của bạn, hoặc mô tả một thay đổi mà bạn muốn đề xuất.",
"selectProvider": "Chọn nhà cung cấp",
"providerSettings": "Cài đặt nhà cung cấp",
"addProvider": "Thêm nhà cung cấp",
"editProvider": "Chỉnh sửa nhà cung cấp",
"removeProvider": "Xóa nhà cung cấp",
"providerType": "Nhà cung cấp",
"providerLabel": "Tên",
"providerLabelPlaceholder": "Ví dụ: Ollama địa phương",
"baseUrl": "Địa chỉ máy chủ",
"apiKey": "Khóa API",
"defaultModel": "Mô hình mặc định",
"defaultModelPlaceholder": "Ví dụ: llama3.1 hoặc claude-opus-5",
"labelRequired": "Đặt tên cho nhà cung cấp này",
"providerSaveFailed": "Không thể lưu nhà cung cấp",
"providerUpdated": "Nhà cung cấp đã cập nhật",
"providerDeleteFailed": "Không thể xóa nhà cung cấp",
"privateEndpointHint": "Các địa chỉ trên mạng riêng của bạn cần được quản trị viên cho phép truy cập trước.",
"providerOllama": "Ollama",
"providerAnthropic": "Nhân loại",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "Tương thích với OpenAI",
"toolArguments": "Đầu vào",
"toolResult": "Kết quả",
"statusApplied": "Đã áp dụng",
"statusRejected": "Vật bị loại bỏ",
"proposalApplied": "Thay đổi đã được áp dụng",
"proposalApplyFailed": "Không thể áp dụng thay đổi",
"proposalRejectFailed": "Không thể từ chối thay đổi",
"enableTitle": "Kích hoạt trợ lý AI",
"enableDescription": "Hãy để trợ lý đọc cấu hình của bạn và đề xuất các thay đổi để bạn phê duyệt.",
"readOnlyCommands": "Cho phép các lệnh chẩn đoán chỉ đọc",
"readOnlyCommandsDescription": "Trợ lý có thể chạy một danh sách ngắn các lệnh an toàn như df và uptime mà không cần hỏi mỗi lần. Bất kỳ lệnh nào thay đổi máy chủ đều được đề xuất trước tiên.",
"modelDetectFailed": "Không thể liên lạc được với nhà cung cấp, vì vậy đây là danh sách chung. Bạn có thể nhập bất kỳ tên mẫu nào.",
"modelCustom": "Nhập tên mẫu",
"modelRefresh": "Làm mới danh sách",
"modelPlaceholder": "Chọn hoặc nhập mô hình",
"attachHint": "Nhập @ để tham chiếu đến máy chủ, đoạn mã hoặc quy trình tự động hóa. Các thay đổi được đề xuất để bạn phê duyệt.",
"runCommandTitle": "Chạy lệnh",
"assistant": "Trợ lý AI",
"terminalPanelEmpty": "Hãy hỏi trợ lý về máy chủ này. Bất cứ thay đổi nào về trạng thái đều cần sự chấp thuận của bạn trước.",
"terminalPanelPlaceholder": "Hãy hỏi về chủ nhà này...",
"noProvidersConfigured": "Chưa có nhà cung cấp AI nào được cấu hình. Hãy thêm một nhà cung cấp trong tab trợ lý AI.",
"runsInOpenTerminal": "Chương trình này chạy trong cửa sổ dòng lệnh đang mở của bạn.",
"runInTerminal": "Chạy"
},
"nav": {
"ai": "Trí tuệ nhân tạo"
},
"onboarding": {
"aiTitle": "Trợ lý AI",
"aiIntro": "Termix có thể bao gồm trợ lý AI tùy chọn. Bạn có thể thay đổi điều này sau trong phần cài đặt.",
"aiSkipTitle": "Đừng làm vậy",
"aiSkipDesc": "Termix vẫn hoạt động như hiện tại. Trợ lý ảo vẫn ẩn mình.",
"aiEnableTitle": "Bật nó lên",
"aiEnableDesc": "Thêm một trợ lý đọc cấu hình của bạn và đề xuất các thay đổi mà bạn chấp thuận. Cần có nhà cung cấp do bạn chỉ định, chẳng hạn như Ollama địa phương.",
"aiNote": "Không có dữ liệu nào được gửi đi cho đến khi bạn thêm nhà cung cấp và gửi tin nhắn."
},
"admin": {
"aiGloballyEnabled": "Trợ lý AI",
"aiGloballyEnabledDesc": "Cho phép người dùng bật trợ lý AI. Khi trợ lý này tắt, nó sẽ bị ẩn và chặn đối với tất cả mọi người.",
"aiPrivateEndpoints": "Cho phép máy chủ AI riêng",
"aiPrivateEndpointsDesc": "Các máy chủ trên mạng riêng của bạn mà người dùng có thể trỏ đến nhà cung cấp, chẳng hạn như Ollama tự lưu trữ. Phân tách chúng bằng dấu phẩy.",
"updateAiEnabledFailed": "Không thể cập nhật cài đặt AI.",
"updateAiEndpointsFailed": "Không thể cập nhật các máy chủ được cho phép"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "助手",
"send": "发送",
"stop": "停止",
"save": "保存",
"cancel": "取消",
"running": "运行中",
"approve": "批准",
"reject": "拒绝",
"inputPlaceholder": "询问服务器相关情况,或描述要提出的变更建议",
"selectProvider": "选择服务提供商",
"providerSettings": "提供商设置",
"addProvider": "添加提供商",
"editProvider": "编辑提供商",
"removeProvider": "移除提供者",
"providerType": "提供商",
"providerLabel": "名称",
"providerLabelPlaceholder": "例如:本地 Ollama",
"baseUrl": "服务器地址",
"apiKey": "API 密钥",
"defaultModel": "默认模型",
"defaultModelPlaceholder": "例如:llama3.1 或 claude-opus-5",
"labelRequired": "给此提供商起个名字",
"providerSaveFailed": "无法保存提供商",
"providerUpdated": "提供商已更新",
"providerDeleteFailed": "无法移除提供商",
"privateEndpointHint": "您私有网络上的地址需要管理员先允许主机访问。",
"providerOllama": "Ollama",
"providerAnthropic": "Anthropic",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "兼容 OpenAI",
"toolArguments": "输入",
"toolResult": "结果",
"statusApplied": "已应用",
"statusRejected": "已拒绝",
"proposalApplied": "变更已生效",
"proposalApplyFailed": "无法应用更改",
"proposalRejectFailed": "无法拒绝更改",
"enableTitle": "启用 AI 助手",
"enableDescription": "让助手读取您的设置并提出更改建议,供您批准。",
"readOnlyCommands": "允许只读诊断命令",
"readOnlyCommandsDescription": "助手可能会在不每次都询问的情况下运行一些安全的命令,例如 `df` 和 `uptime`。任何会更改服务器的操作都会优先被提出。",
"modelDetectFailed": "无法联系到提供商,所以这里提供的是通用列表。您可以输入任何模型名称。",
"modelCustom": "请输入模型名称",
"modelRefresh": "刷新列表",
"modelPlaceholder": "选择或输入模型",
"attachHint": "输入 @ 符号可引用主机、代码片段或自动化流程。系统会提出更改建议,供您批准。",
"runCommandTitle": "运行命令",
"assistant": "人工智能助手",
"terminalPanelEmpty": "向助理询问有关此房东的信息。任何状态变更都需要您先批准。",
"terminalPanelPlaceholder": "询问一下这位房东的情况……",
"noProvidersConfigured": "尚未配置任何 AI 提供商。请在 AI 助手选项卡中添加。",
"runsInOpenTerminal": "在打开的终端会话中运行。",
"runInTerminal": "跑步"
},
"nav": {
"ai": "AI"
},
"onboarding": {
"aiTitle": "AI 助手",
"aiIntro": "Termix 可以包含一个可选的 AI 助手。您可以稍后在设置中更改此选项。",
"aiSkipTitle": "禁用",
"aiSkipDesc": "Termix 正常工作,AI 助手将保持隐藏状态。",
"aiEnableTitle": "启用",
"aiEnableDesc": "启用 AI 助手,它将分析您的配置并提供优化建议。需自行配置服务商,例如本地运行的 Ollama。",
"aiNote": "在您配置服务商并主动发送消息之前,绝不会向外发送任何数据。"
},
"admin": {
"aiGloballyEnabled": "AI 助手",
"aiGloballyEnabledDesc": "允许用户开启 AI 助手。关闭此功能后,助手将被隐藏,所有用户均无法使用。",
"aiPrivateEndpoints": "允许的私有 AI 主机",
"aiPrivateEndpointsDesc": "用户可能将服务提供商指向的私有网络上的主机,例如自托管的 Ollama。用逗号分隔。",
"updateAiEnabledFailed": "无法更新 AI 设置",
"updateAiEndpointsFailed": "无法更新允许的主机列表。"
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"ai": {
"title": "助理",
"send": "傳送",
"stop": "停止",
"save": "節省",
"cancel": "取消",
"running": "跑步",
"approve": "核准",
"reject": "拒絕",
"inputPlaceholder": "詢問伺服器相關情況,或描述要提出的變更建議",
"selectProvider": "選擇服務提供者",
"providerSettings": "提供者設定",
"addProvider": "新增提供者",
"editProvider": "編輯提供者",
"removeProvider": "移除提供者",
"providerType": "提供者",
"providerLabel": "姓名",
"providerLabelPlaceholder": "例如:本地奧拉馬",
"baseUrl": "伺服器位址",
"apiKey": "API金鑰",
"defaultModel": "預設模型",
"defaultModelPlaceholder": "例如:llama3.1 或 claude-opus-5",
"labelRequired": "給這家服務提供者取個名字",
"providerSaveFailed": "無法儲存提供者",
"providerUpdated": "提供者已更新",
"providerDeleteFailed": "無法移除提供者",
"privateEndpointHint": "您私有網路上的位址需要管理員先允許主機存取。",
"providerOllama": "奧拉瑪",
"providerAnthropic": "人類學",
"providerOpenai": "OpenAI",
"providerGemini": "Google Gemini",
"providerOpenaiCompatible": "相容 OpenAI",
"toolArguments": "輸入",
"toolResult": "結果",
"statusApplied": "應用",
"statusRejected": "已拒絕",
"proposalApplied": "變更已生效",
"proposalApplyFailed": "無法套用變更",
"proposalRejectFailed": "無法拒絕更改",
"enableTitle": "啟用人工智慧助手",
"enableDescription": "請助手讀取您的設定並提出更改建議,供您批准。",
"readOnlyCommands": "允許只讀診斷命令",
"readOnlyCommandsDescription": "助手可能會在不每次都詢問的情況下執行一些安全的命令,例如 `df` 和 `uptime`。任何會更改伺服器的操作都會優先被提出。",
"modelDetectFailed": "無法聯繫到供應商,所以這裡提供的是通用清單。您可以輸入任何型號名稱。",
"modelCustom": "請輸入型號名稱",
"modelRefresh": "重新整理清單",
"modelPlaceholder": "選擇或輸入型號",
"attachHint": "輸入 @ 符號可引用主機、程式碼片段或自動化流程。系統會提出更改建議,供您批准。",
"runCommandTitle": "運行命令",
"assistant": "人工智慧助手",
"terminalPanelEmpty": "向助理詢問有關此房東的資訊。任何狀態變更都需要您先批准。",
"terminalPanelPlaceholder": "詢問一下這位房東的狀況…",
"noProvidersConfigured": "尚未配置任何 AI 提供者。請在 AI 助理標籤中新增。",
"runsInOpenTerminal": "在開啟的終端機會話中運行。",
"runInTerminal": "跑步"
},
"nav": {
"ai": "人工智慧"
},
"onboarding": {
"aiTitle": "人工智慧助手",
"aiIntro": "Termix 可以包含一個可選的 AI 助手。您可以稍後在設定中變更此選項。",
"aiSkipTitle": "丟開",
"aiSkipDesc": "Termix 的運作方式與現在相同。助手保持隱藏狀態。",
"aiEnableTitle": "打開它",
"aiEnableDesc": "添加一個助手,它可以讀取您的設定並提出您批准的更改建議。需要您提供服務提供者,例如當地的 Ollama 服務提供者。",
"aiNote": "在您新增服務提供者並發送訊息之前,任何內容都不會發送到任何地方。"
},
"admin": {
"aiGloballyEnabled": "人工智慧助手",
"aiGloballyEnabledDesc": "允許用戶開啟AI助理。關閉此功能後,助手將被隱藏,所有使用者均無法使用。",
"aiPrivateEndpoints": "允許的私有人工智慧主機",
"aiPrivateEndpointsDesc": "使用者可能將服務提供者指向的私有網路上的主機,例如自架的 Ollama。用逗號分隔。",
"updateAiEnabledFailed": "無法更新AI設定",
"updateAiEndpointsFailed": "無法更新允許的主機清單。"
}
}
+17 -1
View File
@@ -87,7 +87,23 @@
"descriptionKey": "permissions.secrets.share.description",
"defaultRoles": ["user"]
}
]
],
"settings": {
"admin": [
{
"key": "access",
"type": "custom",
"component": "access"
}
],
"user": [
{
"key": "assistant",
"type": "custom",
"component": "assistant"
}
]
}
},
"locales": "locales",
"platforms": ["linux", "win32", "darwin"]
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Check } from "lucide-react";
import { saveUserPreferences } from "@/main-axios";
import { readHiddenRailTabs } from "@/sidebar/hidden-rail-tabs";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { setAssistantEnabledForUser } from "./settings/AiUserSettings";
/**
* Asks once, plainly.
@@ -18,21 +17,7 @@ export function AiAssistantStep() {
function apply(next: boolean) {
setEnabled(next);
const hidden = readHiddenRailTabs();
if (next) hidden.delete("ai");
else hidden.add("ai");
const serialized = JSON.stringify([...hidden]);
localStorage.setItem("hiddenRailTabs", serialized);
window.dispatchEvent(new Event("hiddenRailTabsChanged"));
saveUserPreferences({
aiAssistantEnabled: next,
hiddenRailTabs: serialized,
}).catch(() => {
// The local choice still applies if the write fails.
});
setAssistantEnabledForUser(next);
}
// Skipping the step entirely still has to mean off, not unset.
File renamed without changes.
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { Loader2, Send, Settings2, Sparkles, Square } from "lucide-react";
import { Button } from "@/components/button";
import { Textarea } from "@/components/textarea";
@@ -17,7 +17,7 @@ import {
getAiStatus,
type AiProposal,
type AiProvider,
} from "@/api/ai-api";
} from "./ai-api";
import { AiMessage } from "./AiMessage";
import { AiProviderSettings } from "./AiProviderSettings";
import { AiToolCall } from "./AiToolCall";
@@ -1,6 +1,6 @@
import { getErrorMessage } from "../../lib/error-message.js";
import { getErrorMessage } from "@/lib/error-message";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { toast } from "sonner";
import { Loader2, Pencil, Plus, RefreshCw, Trash2 } from "lucide-react";
import { Button } from "@/components/button";
@@ -21,7 +21,7 @@ import {
updateAiProvider,
type AiProvider,
type AiProviderType,
} from "@/api/ai-api";
} from "./ai-api";
const PROVIDER_TYPES: Array<{
value: AiProviderType;
@@ -1,5 +1,5 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { ChevronDown, ChevronRight, Loader2, Wrench } from "lucide-react";
import type { ToolActivity } from "./use-ai-stream";
import { toolLabel } from "./labels";
@@ -1,14 +1,10 @@
import { getErrorMessage } from "../../lib/error-message.js";
import { getErrorMessage } from "@/lib/error-message";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { toast } from "sonner";
import { Check, Loader2, TriangleAlert, X } from "lucide-react";
import { Button } from "@/components/button";
import {
applyAiProposal,
rejectAiProposal,
type AiProposal,
} from "@/api/ai-api";
import { applyAiProposal, rejectAiProposal, type AiProposal } from "./ai-api";
import { fieldLabel, toolLabel } from "./labels";
interface ProposalCardProps {
@@ -245,64 +245,3 @@ export async function setAiPrivateEndpoints(
throw handleApiError(error, "update AI endpoint allowlist");
}
}
export async function getNotificationPrivateEndpoints(): Promise<string[]> {
try {
return (await authApi.get("/users/notification-private-endpoints")).data
.hosts;
} catch (error) {
throw handleApiError(error, "get notification endpoint allowlist");
}
}
export async function getStepCaPrivateEndpoints(): Promise<string[]> {
try {
return (await authApi.get("/users/step-ca-private-endpoints")).data.hosts;
} catch (error) {
throw handleApiError(error, "get Step CA endpoint allowlist");
}
}
export async function setStepCaPrivateEndpoints(
hosts: string[],
): Promise<string[]> {
try {
return (await authApi.patch("/users/step-ca-private-endpoints", { hosts }))
.data.hosts;
} catch (error) {
throw handleApiError(error, "update Step CA endpoint allowlist");
}
}
export async function getSecretSourcePrivateEndpoints(): Promise<string[]> {
try {
return (await authApi.get("/users/secret-source-private-endpoints")).data
.hosts;
} catch (error) {
throw handleApiError(error, "get secret source endpoint allowlist");
}
}
export async function setSecretSourcePrivateEndpoints(
hosts: string[],
): Promise<string[]> {
try {
return (
await authApi.patch("/users/secret-source-private-endpoints", { hosts })
).data.hosts;
} catch (error) {
throw handleApiError(error, "update secret source endpoint allowlist");
}
}
export async function setNotificationPrivateEndpoints(
hosts: string[],
): Promise<string[]> {
try {
return (
await authApi.patch("/users/notification-private-endpoints", { hosts })
).data.hosts;
} catch (error) {
throw handleApiError(error, "update notification endpoint allowlist");
}
}
-68
View File
@@ -1,68 +0,0 @@
/**
* Frontend half of the ai plugin.
*
* AiPanel and the rest of src/ui/features/ai/ stay in core rather than
* moving here: TerminalAiPanel.tsx and TerminalProposalCard.tsx in
* src/ui/features/terminal/ (also core) import AiMessage, AiToolCall,
* useAiStream and ProposalCard directly, for the terminal's own docked AI
* panel. That is a real external dependency, the same reason session-manager
* stays in core for ssh-terminal rather than being copied into that plugin.
*
* The "ai" surface itself is not registered through registerTabComponent:
* it is a rail-view/right-dock panel baked into AppShell.tsx and
* tabUtils.tsx (railView === "ai", case "ai"), the same way the terminal's
* tab is baked into Terminal.tsx rather than going through the generic tab
* registry. What gates it is isTabTypeAvailable("ai") in
* src/ui/shell/pluginLoader.ts's BUILT_IN_TABS_BY_PLUGIN map.
*
* What this file DOES contribute is the terminal toolbar's AI button, through
* the action registry: ai.openWithContext, declared in manifest.json and
* gated on ai.services.use. The button used to be hardwired into
* TerminalToolbar.tsx behind a showAiAssistant prop.
*
* There is still no frontend plugin loader, so nothing imports this file yet
* (see src/ui/tests/sidebar/plugin-extension-seam.test.tsx). Until one exists,
* pluginLoader.ts performs the same registration itself when the ai plugin is
* enabled. This file is the version that runs once a loader lands, and the two
* must be kept in step.
*/
export const id = "ai";
export const tabId = "ai";
export const actionId = "ai.openWithContext";
/** Asks the focused terminal to open its AI panel seeded with `context`. */
export function openWithContext(context) {
window.dispatchEvent(
new CustomEvent("termix:ai:openWithContext", {
detail: { context: typeof context === "string" ? context : "" },
}),
);
}
export async function register({
registerAction,
registerSlotContribution,
icons,
}) {
registerAction(actionId, openWithContext, {
permission: "ai.services.use",
pluginId: id,
});
registerSlotContribution("terminal.toolbar", {
actionId,
titleKey: "ai.assistant",
icon: icons.Bot,
kind: "button",
pluginId: id,
});
}
export async function unregister({
unregisterAction,
unregisterSlotContribution,
}) {
unregisterSlotContribution("terminal.toolbar", actionId);
unregisterAction(actionId);
}
+183
View File
@@ -0,0 +1,183 @@
import type { ComponentType } from "react";
import { Bot, Sparkles } from "lucide-react";
import type {
PanelProps,
TabProps,
TermixApp,
} from "@termix/plugin-sdk/frontend";
import type {
TerminalDockProps,
TerminalSlotApi,
} from "@/features/terminal/terminal-slots";
import { AiPanel } from "./AiPanel";
import { TerminalAiPanel } from "./terminal/TerminalAiPanel";
import { AiAssistantStep } from "./AiAssistantStep";
import { AiAccessSettings } from "./settings/AiAccessSettings";
import { AI_RAIL_ID, AiUserSettings } from "./settings/AiUserSettings";
import { getAiStatus } from "./ai-api";
import { AI_STATUS_CHANGED_EVENT } from "./use-ai-availability";
const ASSISTANT_ACTION = "ai.openWithContext";
const DOCK_ID = "ai.assistant";
function AssistantPanel({ activeTabType }: PanelProps) {
return (
<div className="flex flex-col flex-1 min-h-0">
<AiPanel activeTab={activeTabType ?? null} />
</div>
);
}
function AssistantTab() {
return <AiPanel />;
}
/** The assistant docked beside a terminal. */
function TerminalDock({
host,
hostId,
hostLabel,
dockProps,
onClose,
onRunInTerminal,
}: TerminalDockProps) {
if (!hostId) return null;
return (
<TerminalAiPanel
hostLabel={hostLabel}
hostId={hostId}
activeTab={`terminal:${host?.name || host?.ip || ""}`}
initialContext={
typeof dockProps.context === "string" ? dockProps.context : ""
}
onClose={onClose}
onRunInTerminal={onRunInTerminal}
/>
);
}
export function activate(app: TermixApp): void {
let globallyEnabled = false;
let userEnabled = false;
const surface: (() => void)[] = [];
const clearSurface = () => {
while (surface.length > 0) surface.pop()!();
};
// What the assistant shows depends on the admin switch and the user's own
// choice, so it is re-registered whenever either changes.
const applyStatus = () => {
clearSurface();
surface.push(
app.registerRailItem({
id: AI_RAIL_ID,
icon: Sparkles,
titleKey: "nav.ai",
promotable: true,
rightDockable: true,
after: "macros",
order: 40,
// Off for everyone: not even a Navigation toggle.
hidden: !globallyEnabled,
}),
);
if (!globallyEnabled) {
app.tabs.closeTab(AI_RAIL_ID);
return;
}
surface.push(
app.registerTab(AI_RAIL_ID, AssistantTab as ComponentType<TabProps>, {
icon: Sparkles,
titleKey: "nav.ai",
singleton: true,
hostless: true,
panelFrame: true,
}),
);
const offeredOnHost = (context: Record<string, unknown>) =>
userEnabled &&
(context.host as { enableAiAssistant?: boolean } | undefined)
?.enableAiAssistant === true;
surface.push(
app.registerSlotContribution("terminal.toolbar", {
actionId: ASSISTANT_ACTION,
titleKey: "ai.assistant",
icon: Bot,
kind: "button",
when: offeredOnHost,
}),
);
surface.push(
app.registerSlotContribution("terminal.dock", {
actionId: DOCK_ID,
titleKey: "ai.assistant",
icon: Bot,
kind: "component",
component: TerminalDock as unknown as ComponentType<
Record<string, unknown>
>,
when: offeredOnHost,
}),
);
surface.push(
app.registerSlotContribution("onboarding.steps", {
actionId: "ai.onboarding",
titleKey: "onboarding.aiTitle",
kind: "component",
component: AiAssistantStep as unknown as ComponentType<
Record<string, unknown>
>,
}),
);
};
const refresh = () => {
getAiStatus()
.then((status) => {
globallyEnabled = status.globallyEnabled;
userEnabled = status.globallyEnabled && status.enabled;
})
.catch(() => {
globallyEnabled = false;
userEnabled = false;
})
.finally(() => {
if (!disposed) applyStatus();
});
};
let disposed = false;
app.onDispose(() => {
disposed = true;
clearSurface();
});
app.registerPanel(AI_RAIL_ID, AssistantPanel);
// Opens the docked assistant on the terminal it was clicked from, seeded
// with what is on screen.
app.registerAction(
ASSISTANT_ACTION,
(terminal: TerminalSlotApi) =>
terminal?.openDock(DOCK_ID, { context: terminal.getBufferText() }),
{ permission: "services.use" },
);
app.registerSettingsComponent("access", AiAccessSettings);
app.registerSettingsComponent("assistant", AiUserSettings);
applyStatus();
refresh();
window.addEventListener(AI_STATUS_CHANGED_EVENT, refresh);
window.addEventListener("hiddenRailTabsChanged", refresh);
app.onDispose(() => {
window.removeEventListener(AI_STATUS_CHANGED_EVENT, refresh);
window.removeEventListener("hiddenRailTabsChanged", refresh);
});
}
File renamed without changes.
@@ -0,0 +1,95 @@
import { useEffect, useState } from "react";
import { useToast, useTranslation } from "@termix/plugin-sdk/frontend";
import { FakeSwitch, Input, SettingRow } from "@termix/plugin-sdk/ui";
import {
getAiGloballyEnabled,
getAiPrivateEndpoints,
setAiGloballyEnabled,
setAiPrivateEndpoints,
} from "../ai-api";
import { notifyAiStatusChanged } from "../use-ai-availability";
/**
* The instance-wide switch and the private address allowlist. Stored in the
* assistant's own backend rather than plugin settings, so this saves on
* change instead of through the page's Save.
*/
export function AiAccessSettings() {
const { t } = useTranslation();
const toast = useToast();
const [enabled, setEnabled] = useState(false);
const [endpoints, setEndpoints] = useState<string[] | null>(null);
useEffect(() => {
let cancelled = false;
void Promise.allSettled([
getAiGloballyEnabled(),
getAiPrivateEndpoints(),
]).then(([enabledResult, endpointsResult]) => {
if (cancelled) return;
if (enabledResult.status === "fulfilled") setEnabled(enabledResult.value);
setEndpoints(
endpointsResult.status === "fulfilled" ? endpointsResult.value : [],
);
});
return () => {
cancelled = true;
};
}, []);
const toggle = async (next: boolean) => {
setEnabled(next);
try {
await setAiGloballyEnabled(next);
notifyAiStatusChanged();
} catch {
setEnabled(!next);
toast.error(t("admin.updateAiEnabledFailed"));
}
};
const saveEndpoints = async (hosts: string[]) => {
const previous = endpoints ?? [];
setEndpoints(hosts);
try {
setEndpoints(await setAiPrivateEndpoints(hosts));
} catch {
setEndpoints(previous);
toast.error(t("admin.updateAiEndpointsFailed"));
}
};
return (
<div className="flex flex-col gap-1">
<SettingRow
label={t("admin.aiGloballyEnabled")}
description={t("admin.aiGloballyEnabledDesc")}
>
<FakeSwitch checked={enabled} onChange={(next) => void toggle(next)} />
</SettingRow>
{enabled && endpoints !== null && (
<div className="flex flex-col gap-1.5 py-2">
<span className="text-xs font-medium">
{t("admin.aiPrivateEndpoints")}
</span>
<span className="text-[11px] leading-snug text-muted-foreground">
{t("admin.aiPrivateEndpointsDesc")}
</span>
<Input
className="rounded-none"
defaultValue={endpoints.join(", ")}
placeholder="localhost, 127.0.0.1"
onBlur={(event) =>
void saveEndpoints(
event.target.value
.split(",")
.map((entry) => entry.trim())
.filter(Boolean),
)
}
/>
</div>
)}
</div>
);
}
@@ -0,0 +1,112 @@
import { useEffect, useState } from "react";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { FakeSwitch, SettingRow } from "@termix/plugin-sdk/ui";
import { saveUserPreferences } from "@/main-axios";
import { readHiddenRailTabs } from "@/sidebar/hidden-rail-tabs";
import { getAiStatus } from "../ai-api";
import { notifyAiStatusChanged } from "../use-ai-availability";
/** The rail id the assistant registers under. */
export const AI_RAIL_ID = "ai";
/**
* Turns the assistant on or off for one person. Off also hides its rail
* entry, so declining removes the feature from view rather than leaving an
* inert button behind. Stored in core user preferences, where it has always
* lived, so it roams with the rest of a user's settings.
*/
export function setAssistantEnabledForUser(enabled: boolean): void {
const hidden = readHiddenRailTabs();
if (enabled) hidden.delete(AI_RAIL_ID);
else hidden.add(AI_RAIL_ID);
const serialized = JSON.stringify([...hidden]);
localStorage.setItem("hiddenRailTabs", serialized);
window.dispatchEvent(new Event("hiddenRailTabsChanged"));
saveUserPreferences({
aiAssistantEnabled: enabled,
hiddenRailTabs: serialized,
})
.catch(() => {
// The local choice still applies if the write fails.
})
.finally(() => notifyAiStatusChanged());
}
export function AiUserSettings() {
const { t } = useTranslation();
const [status, setStatus] = useState<{
globallyEnabled: boolean;
enabled: boolean;
readOnly: boolean;
} | null>(null);
useEffect(() => {
let cancelled = false;
getAiStatus()
.then((next) => {
if (!cancelled) {
setStatus({
globallyEnabled: next.globallyEnabled,
enabled: next.enabled,
readOnly: next.allowReadOnlyCommands,
});
}
})
.catch(() => {
if (!cancelled) {
setStatus({
globallyEnabled: false,
enabled: false,
readOnly: false,
});
}
});
return () => {
cancelled = true;
};
}, []);
// A user who cannot have the feature is not told about it.
if (!status?.globallyEnabled) {
return (
<p className="text-xs text-muted-foreground">
{t("settings.assistantUnavailable")}
</p>
);
}
return (
<div className="flex flex-col gap-1">
<SettingRow
label={t("ai.enableTitle")}
description={t("ai.enableDescription")}
>
<FakeSwitch
checked={status.enabled}
onChange={(enabled) => {
setStatus({ ...status, enabled });
setAssistantEnabledForUser(enabled);
}}
/>
</SettingRow>
{status.enabled && (
<SettingRow
label={t("ai.readOnlyCommands")}
description={t("ai.readOnlyCommandsDescription")}
>
<FakeSwitch
checked={status.readOnly}
onChange={(readOnly) => {
setStatus({ ...status, readOnly });
saveUserPreferences({ aiReadOnlyCommands: readOnly }).catch(
() => {},
);
}}
/>
</SettingRow>
)}
</div>
);
}
@@ -5,20 +5,20 @@ import {
useRef,
useState,
} from "react";
import { useTranslation } from "react-i18next";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { Bot, GripVertical, Send, Square, X } from "lucide-react";
import { Button } from "@/components/button";
import { getAiProviders, type AiProposal, type AiProvider } from "@/api/ai-api";
import { AiMessage } from "@/features/ai/AiMessage";
import { AiToolCall } from "@/features/ai/AiToolCall";
import { useAiStream } from "@/features/ai/use-ai-stream";
import { getAiProviders, type AiProposal, type AiProvider } from "../ai-api";
import { AiMessage } from "../AiMessage";
import { AiToolCall } from "../AiToolCall";
import { useAiStream } from "../use-ai-stream";
import { TerminalProposalCard } from "./TerminalProposalCard";
import {
clampToolbarPosition,
persistAiPanelPosition,
readStoredAiPanelPosition,
type ToolbarPosition,
} from "./toolbar-geometry";
} from "@/features/terminal/toolbar-geometry";
interface HistoryEntry {
role: "user" | "assistant";
@@ -35,7 +35,7 @@ type TimelineItem =
| {
kind: "tool";
key: string;
tool: import("@/features/ai/use-ai-stream").ToolActivity;
tool: import("../use-ai-stream").ToolActivity;
}
| { kind: "proposal"; key: string; proposal: AiProposal };
@@ -1,6 +1,6 @@
import { getErrorMessage } from "../../lib/error-message.js";
import { getErrorMessage } from "@/lib/error-message";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { toast } from "sonner";
import { Check, Loader2, TriangleAlert, X } from "lucide-react";
import { Button } from "@/components/button";
@@ -8,8 +8,8 @@ import {
markAiProposalRunInTerminal,
rejectAiProposal,
type AiProposal,
} from "@/api/ai-api";
import { ProposalCard } from "@/features/ai/ProposalCard";
} from "../ai-api";
import { ProposalCard } from "../ProposalCard";
interface TerminalProposalCardProps {
proposal: AiProposal;
@@ -34,7 +34,7 @@ export function useAiAvailability(): AiAvailability {
useEffect(() => {
let cancelled = false;
const refresh = () => {
import("@/api/ai-api")
import("./ai-api")
.then(({ getAiStatus }) => getAiStatus())
.then((status) => {
if (cancelled) return;
@@ -1,7 +1,7 @@
import { getErrorMessage } from "../../lib/error-message.js";
import { getErrorMessage } from "@/lib/error-message";
import { useCallback, useRef, useState } from "react";
import { authApi } from "@/main-axios";
import type { AiProposal } from "@/api/ai-api";
import type { AiProposal } from "./ai-api";
/**
* Reuses whatever base authApi resolved to, so the stream follows the same
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { getSSHHosts } from "@/api/ssh-host-management-api";
import { getSnippets } from "@/api/snippets-api";
import { listAutomations } from "../../../../plugins/automations/src/frontend/automations-api";
import { invokeAction } from "@termix/plugin-sdk/frontend";
/**
* Backs the @-mention picker in the composer.
@@ -39,6 +39,16 @@ export function activeMentionQuery(
return { query, start: at };
}
/** The automations plugin's list, or nothing when it is not running. */
async function listAutomationsForMentions(): Promise<
{ id: number; name: string }[]
> {
const result = await invokeAction("automations.list");
return Array.isArray(result)
? (result as { id: number; name: string }[])
: [];
}
export function useMentions(enabled: boolean) {
const [items, setItems] = useState<MentionItem[]>([]);
@@ -50,7 +60,9 @@ export function useMentions(enabled: boolean) {
const [hosts, snippets, automations] = await Promise.allSettled([
getSSHHosts(),
getSnippets(),
listAutomations(),
// Through the automations plugin's action, so the assistant works
// without it and never imports its code.
listAutomationsForMentions(),
]);
if (cancelled) return;
@@ -6,7 +6,7 @@ import {
screen,
waitFor,
} from "@testing-library/react";
import type { AiProvider } from "@/api/ai-api";
import type { AiProvider } from "../../src/frontend/ai-api";
const api = vi.hoisted(() => ({
createAiProvider: vi.fn(),
@@ -16,7 +16,7 @@ const api = vi.hoisted(() => ({
updateAiProvider: vi.fn(),
}));
vi.mock("@/api/ai-api", () => api);
vi.mock("../../src/frontend/ai-api", () => api);
vi.mock("react-i18next", () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));
@@ -24,7 +24,7 @@ vi.mock("sonner", () => ({
toast: { success: vi.fn(), error: vi.fn() },
}));
import { AiProviderSettings } from "@/features/ai/AiProviderSettings";
import { AiProviderSettings } from "../../src/frontend/AiProviderSettings";
const provider: AiProvider = {
id: 7,
@@ -0,0 +1,53 @@
import { afterEach, describe, expect, it } from "vitest";
import {
renderWithApp,
type RenderedPluginApp,
} from "@termix/plugin-sdk/testing";
import type { PluginManifest } from "@termix/plugin-sdk/manifest";
import * as plugin from "../../src/frontend/index";
import manifestJson from "../../manifest.json";
import locales from "../../locales/en.json";
const manifest = manifestJson as unknown as PluginManifest;
const declared = (views?: Array<{ id: string }>) =>
(views ?? []).map((view) => view.id).sort();
let rendered: RenderedPluginApp | null = null;
afterEach(async () => {
await rendered?.deactivate();
rendered = null;
});
describe(`${manifest.id} activate`, () => {
it("activates and registers only views its manifest declares", async () => {
rendered = await renderWithApp(plugin, { manifest, locales });
const contributes = manifest.contributes as unknown as Record<
string,
Array<{ id: string }> | undefined
>;
const tabs = declared(contributes.tabs);
// A rail panel may reuse one of the plugin's tab ids.
const panels = [...declared(contributes.panels), ...tabs];
const cards = declared(contributes.dashboardCards);
for (const id of rendered.registered.tabs()) expect(tabs).toContain(id);
for (const id of rendered.registered.panels()) expect(panels).toContain(id);
for (const id of rendered.registered.dashboardCards()) {
expect(cards).toContain(id);
}
});
it("removes everything it registered on deactivate", async () => {
const app = await renderWithApp(plugin, { manifest, locales });
await app.deactivate();
expect(app.registered.tabs()).toEqual([]);
expect(app.registered.panels()).toEqual([]);
expect(app.registered.railItems()).toEqual([]);
expect(app.registered.hostActions()).toEqual([]);
expect(app.registered.hostEditorSections()).toEqual([]);
expect(app.registered.dashboardCards()).toEqual([]);
expect(app.registered.settingsComponents()).toEqual([]);
expect(app.registered.actions()).toEqual([]);
});
});
+1 -1
View File
@@ -4,7 +4,7 @@ import {
humanize,
mentionLabel,
toolLabel,
} from "@/features/ai/labels";
} from "../../src/frontend/labels";
import { AI_TOOLS } from "../../src/backend/tools/catalog.js";
describe("humanize", () => {
@@ -2,10 +2,10 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { renderHook, waitFor, act } from "@testing-library/react";
const getAiStatus = vi.fn();
vi.mock("@/api/ai-api", () => ({ getAiStatus }));
vi.mock("../../src/frontend/ai-api", () => ({ getAiStatus }));
const { useAiAvailability, notifyAiStatusChanged } =
await import("../../hooks/use-ai-availability.js");
await import("../../src/frontend/use-ai-availability");
beforeEach(() => {
getAiStatus.mockReset();
+171 -1
View File
@@ -1 +1,171 @@
{}
{
"permissions": {
"view": {
"title": "View automations",
"description": "See the automations and their history."
},
"create": {
"title": "Create automations",
"description": "Add new automations."
},
"edit": {
"title": "Edit automations",
"description": "Change existing automations."
},
"delete": {
"title": "Delete automations",
"description": "Remove automations."
},
"run": {
"title": "Run automations",
"description": "Trigger an automation by hand."
}
},
"newUi": {
"sidebar": {
"automations": {
"title": "Automations",
"tabAutomations": "Automations",
"tabRuns": "Runs",
"tabChannels": "Channels",
"createAutomation": "New Automation",
"backToAutomations": "Back to Automations",
"createTitle": "New Automation",
"editTitle": "Edit Automation",
"empty": "No automations yet. Create one to react to a metric, a schedule, or an event.",
"emptyRuns": "No runs yet.",
"emptyChannels": "No channels yet. Add one to send notifications somewhere.",
"noChannelsHint": "No channels set up yet. Add one in the Channels tab.",
"addChannel": "Add Channel",
"testChannel": "Test",
"channelTestSent": "Test notification sent",
"channelTestFailed": "Test notification failed",
"channelDeleted": "Channel deleted",
"deleteChannelConfirm": "Delete this channel? Automations using it will stop notifying.",
"nameLabel": "Name",
"namePlaceholder": "e.g., Clean up /data when it fills",
"nameRequired": "Name is required",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Optional description",
"enabledLabel": "Enabled",
"concurrencyLabel": "If it is already running",
"concurrencyHint": "What to do when this is triggered again before the last run has finished.",
"concurrency": {
"skip": "Skip the new run",
"queue": "Wait and run after",
"allow": "Run both at once"
},
"save": "Save",
"created": "Automation created",
"updated": "Automation saved",
"deleted": "Automation deleted",
"deleteConfirm": "Delete this automation? Its run history is deleted too.",
"runNow": "Run now",
"testRun": "Test run",
"testRunHint": "Runs every step but does not touch anything outside Termix.",
"runStarted": "Run started",
"runFinished": "Run finished: {{status}}",
"trigger": "Trigger",
"steps": "Steps",
"addStep": "Add step",
"removeStep": "Remove step",
"moveUp": "Move up",
"moveDown": "Move down",
"noSteps": "No steps yet. Add one to say what should happen.",
"webhookTokenTitle": "Webhook URL",
"webhookTokenDescription": "This is shown once. Save it now, it cannot be retrieved later.",
"copy": "Copy",
"copied": "Copied",
"viewJson": "JSON",
"viewBuilder": "Builder",
"viewGraph": "Graph",
"lastRun": "Last run",
"never": "Never",
"duration": "{{seconds}}s",
"stepOutput": "Output",
"stepError": "Error",
"dryRunBadge": "Test run",
"hostSelector": {
"trigger": "Triggering host",
"host": "Specific host",
"fleet": "Fleet",
"all": "All hosts"
},
"triggerKinds": {
"metric_threshold": "Metric threshold",
"host_status": "Host goes up or down",
"health_check": "Health check changes",
"schedule": "On a schedule",
"docker_event": "Container event",
"internal_event": "Termix event",
"webhook": "Incoming webhook"
},
"stepTypes": {
"notify": "Send a notification",
"http": "Call a URL",
"run_snippet": "Run a snippet",
"run_command": "Run a command",
"docker": "Control a container",
"tunnel": "Control a tunnel",
"wol": "Wake a host",
"wait": "Wait",
"set_var": "Set a variable",
"if": "If / otherwise",
"run_automation": "Run another automation",
"stop": "Stop"
},
"statuses": {
"running": "Running",
"success": "Success",
"failed": "Failed",
"timeout": "Timed out",
"skipped": "Skipped",
"cancelled": "Cancelled"
},
"fields": {
"host": "Host",
"metric": "Metric",
"mount": "Mount point",
"operator": "Is",
"value": "Value",
"forSeconds": "Sustained for (seconds)",
"cooldown": "Cooldown (minutes)",
"cron": "Cron expression",
"intervalSeconds": "Every (seconds)",
"timezone": "Time zone",
"timezoneServer": "Server time",
"timezoneHint": "Cron times are read in this zone. Does not apply to interval schedules.",
"container": "Container",
"command": "Command",
"url": "URL",
"method": "Method",
"body": "Body",
"channels": "Channels",
"title": "Title",
"message": "Message",
"snippet": "Snippet",
"seconds": "Seconds",
"conditionLeftPlaceholder": "Value to check",
"conditionRightPlaceholder": "Compare against",
"variableName": "Variable name",
"variableValue": "Value",
"elevated": "Run with sudo",
"onError": "If this fails",
"allowPrivateNetwork": "Allow local network addresses",
"allowPrivateNetworkHint": "Needed for a self-hosted service on your LAN. Off by default to prevent requests to internal addresses."
},
"onError": {
"stop": "Stop the run",
"continue": "Carry on"
}
}
}
},
"nav": {
"automations": "Automations"
},
"onboarding": {
"feature_automations": "Automations",
"feature_automations_desc": "Run steps automatically when something happens, and get notified."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Outomatisasies",
"tabAutomations": "Outomatisasies",
"tabRuns": "Loopbane",
"tabChannels": "Kanale",
"createAutomation": "Nuwe Outomatisering",
"backToAutomations": "Terug na Outomatisasies",
"createTitle": "Nuwe Outomatisering",
"editTitle": "Wysig Outomatisering",
"empty": "Geen outomatisasies nog nie. Skep een om op 'n maatstaf, 'n skedule of 'n gebeurtenis te reageer.",
"emptyRuns": "Geen lopies nog nie.",
"emptyChannels": "Geen kanale nog nie. Voeg een by om kennisgewings êrens heen te stuur.",
"noChannelsHint": "Geen kanale nog opgestel nie. Voeg een by in die Kanale-oortjie.",
"addChannel": "Voeg kanaal by",
"testChannel": "Toets",
"channelTestSent": "Toetskennisgewing gestuur",
"channelTestFailed": "Toetskennisgewing het misluk",
"channelDeleted": "Kanaal uitgevee",
"deleteChannelConfirm": "Vee hierdie kanaal uit? Outomatiserings wat dit gebruik, sal ophou om kennisgewings te stuur.",
"nameLabel": "Naam",
"namePlaceholder": "bv. Maak /data skoon wanneer dit vol is",
"nameRequired": "Naam word vereis",
"descriptionLabel": "Beskrywing",
"descriptionPlaceholder": "Opsionele beskrywing",
"enabledLabel": "Geaktiveer",
"concurrencyLabel": "As dit reeds aan die gang is",
"concurrencyHint": "Wat om te doen wanneer dit weer geaktiveer word voordat die laaste lopie klaar is.",
"concurrency": {
"skip": "Slaan die nuwe lopie oor",
"queue": "Wag en hardloop agterna",
"allow": "Begin albei gelyktydig"
},
"save": "Stoor",
"created": "Outomatisering geskep",
"updated": "Outomatisering gestoor",
"deleted": "Outomatisering uitgevee",
"deleteConfirm": "Vee hierdie outomatisering uit? Die loopgeskiedenis daarvan word ook uitgevee.",
"runNow": "Hardloop nou",
"testRun": "Toetslopie",
"testRunHint": "Loop elke stap, maar raak niks buite Termix aan nie.",
"runStarted": "Loop het begin",
"runFinished": "Loop voltooi: {{status}}",
"trigger": "Sneller",
"steps": "Stappe",
"addStep": "Voeg stap by",
"removeStep": "Verwyder stap",
"moveUp": "Beweeg op",
"moveDown": "Beweeg af",
"noSteps": "Geen stappe nog nie. Voeg een by om te sê wat moet gebeur.",
"webhookTokenTitle": "Webhook-URL",
"webhookTokenDescription": "Dit word een keer gewys. Stoor dit nou, dit kan nie later herwin word nie.",
"copy": "Kopieer",
"copied": "Gekopieer",
"viewJson": "JSON",
"viewBuilder": "Bouer",
"viewGraph": "Grafiek",
"lastRun": "Laaste lopie",
"never": "Nooit",
"duration": "{{seconds}}s",
"stepOutput": "Uitset",
"stepError": "Fout",
"dryRunBadge": "Toetslopie",
"hostSelector": {
"trigger": "Aanvallende gasheer",
"host": "Spesifieke gasheer",
"fleet": "Vloot",
"all": "Alle gashere"
},
"triggerKinds": {
"metric_threshold": "Metrieke drempel",
"host_status": "Gasheer gaan op of af",
"health_check": "Veranderinge in gesondheidstoetse",
"schedule": "Op 'n skedule",
"docker_event": "Houergebeurtenis",
"internal_event": "Termix-geleentheid",
"webhook": "Inkomende webhook"
},
"stepTypes": {
"notify": "Stuur 'n kennisgewing",
"http": "Roep 'n URL op",
"run_snippet": "Voer 'n brokkie uit",
"run_command": "Voer 'n opdrag uit",
"docker": "Beheer 'n houer",
"tunnel": "Beheer 'n tonnel",
"wol": "Maak 'n gasheer wakker",
"wait": "Wag",
"set_var": "Stel 'n veranderlike",
"if": "Indien / andersins",
"run_automation": "Begin nog 'n outomatisering",
"stop": "Stop"
},
"statuses": {
"running": "Hardloop",
"success": "Sukses",
"failed": "Misluk",
"timeout": "Tyd verstryk",
"skipped": "Oorgeslaan",
"cancelled": "Gekanselleer"
},
"fields": {
"host": "Gasheer",
"metric": "Metrieke",
"mount": "Monteerpunt",
"operator": "Is",
"value": "Waarde",
"forSeconds": "Aangehou vir (sekondes)",
"cooldown": "Afkoeltyd (minute)",
"cron": "Cron-uitdrukking",
"intervalSeconds": "Elke (sekondes)",
"timezone": "Tydsone",
"timezoneServer": "Bedienertyd",
"timezoneHint": "Cron-tye word in hierdie sone gelees. Geld nie vir intervalskedules nie.",
"container": "Houer",
"command": "Bevel",
"url": "URL",
"method": "Metode",
"body": "Liggaam",
"channels": "Kanale",
"title": "Titel",
"message": "Boodskap",
"snippet": "Brokkie",
"seconds": "Sekondes",
"conditionLeftPlaceholder": "Waarde om na te gaan",
"conditionRightPlaceholder": "Vergelyk teen",
"variableName": "Veranderlike naam",
"variableValue": "Waarde",
"elevated": "Begin met sudo",
"onError": "As dit misluk",
"allowPrivateNetwork": "Laat plaaslike netwerkadresse toe",
"allowPrivateNetworkHint": "Nodig vir 'n selfgehoste diens op jou LAN. Standaard afgeskakel om versoeke na interne adresse te voorkom."
},
"onError": {
"stop": "Stop die hardloop",
"continue": "Gaan voort"
}
}
}
},
"nav": {
"automations": "Outomatisasies"
},
"onboarding": {
"feature_automations": "Outomatisasies",
"feature_automations_desc": "Voer stappe outomaties uit wanneer iets gebeur, en word in kennis gestel."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "الأتمتة",
"tabAutomations": "الأتمتة",
"tabRuns": "يركض",
"tabChannels": "القنوات",
"createAutomation": "الأتمتة الجديدة",
"backToAutomations": "العودة إلى الأتمتة",
"createTitle": "الأتمتة الجديدة",
"editTitle": "أتمتة التحرير",
"empty": "لا توجد عمليات أتمتة حتى الآن. أنشئ واحدة للتفاعل مع مقياس أو جدول زمني أو حدث.",
"emptyRuns": "لم يتم تسجيل أي نقاط حتى الآن.",
"emptyChannels": "لا توجد قنوات حتى الآن. أضف قناة لإرسال الإشعارات إلى مكان ما.",
"noChannelsHint": "لم يتم إعداد أي قنوات بعد. أضف قناة في تبويب القنوات.",
"addChannel": "إضافة قناة",
"testChannel": "امتحان",
"channelTestSent": "تم إرسال إشعار الاختبار",
"channelTestFailed": "فشل إشعار الاختبار",
"channelDeleted": "تم حذف القناة",
"deleteChannelConfirm": "هل تريد حذف هذه القناة؟ ستتوقف عمليات التشغيل الآلي التي تستخدمها عن إرسال الإشعارات.",
"nameLabel": "اسم",
"namePlaceholder": "مثال: تنظيف /data عند امتلائها",
"nameRequired": "الاسم مطلوب",
"descriptionLabel": "وصف",
"descriptionPlaceholder": "وصف اختياري",
"enabledLabel": "مُفعّل",
"concurrencyLabel": "إذا كان قيد التشغيل بالفعل",
"concurrencyHint": "ماذا نفعل عندما يتم تشغيل هذا مرة أخرى قبل انتهاء التشغيل الأخير؟",
"concurrency": {
"skip": "تخطَّ الجولة الجديدة",
"queue": "انتظر واركض خلفه",
"allow": "قم بتشغيل كليهما في وقت واحد"
},
"save": "يحفظ",
"created": "تم إنشاء الأتمتة",
"updated": "تم حفظ الأتمتة",
"deleted": "تم حذف الأتمتة",
"deleteConfirm": "هل تريد حذف هذه العملية الآلية؟ سيتم حذف سجل تشغيلها أيضاً.",
"runNow": "انطلق الآن",
"testRun": "تشغيل تجريبي",
"testRunHint": "يقوم بتنفيذ كل خطوة ولكنه لا يلمس أي شيء خارج Termix.",
"runStarted": "بدأ الجري",
"runFinished": "انتهت العملية: {{status}}",
"trigger": "مشغل",
"steps": "خطوات",
"addStep": "خطوات إضافية",
"removeStep": "خطوة الإزالة",
"moveUp": "انتقل إلى مستوى أعلى",
"moveDown": "انزل لأسفل",
"noSteps": "لم تُتخذ أي خطوات بعد. أضف خطوة واحدة لتوضيح ما ينبغي أن يحدث.",
"webhookTokenTitle": "رابط الويب هوك",
"webhookTokenDescription": "هذا يظهر مرة واحدة فقط. احفظه الآن، فلن تتمكن من استعادته لاحقاً.",
"copy": "ينسخ",
"copied": "تم النسخ",
"viewJson": "JSON",
"viewBuilder": "باني",
"viewGraph": "الرسم البياني",
"lastRun": "الجولة الأخيرة",
"never": "أبداً",
"duration": "{{seconds}}s",
"stepOutput": "الناتج",
"stepError": "خطأ",
"dryRunBadge": "تشغيل تجريبي",
"hostSelector": {
"trigger": "تشغيل المضيف",
"host": "مضيف محدد",
"fleet": "أسطول",
"all": "جميع المضيفين"
},
"triggerKinds": {
"metric_threshold": "العتبة المترية",
"host_status": "يرتفع أو ينخفض مستوى المضيف",
"health_check": "تغييرات في الفحص الصحي",
"schedule": "وفقًا لجدول زمني",
"docker_event": "حدث الحاوية",
"internal_event": "حدث تيرميكس",
"webhook": "رابط الويب الوارد"
},
"stepTypes": {
"notify": "إرسال إشعار",
"http": "استدعاء عنوان URL",
"run_snippet": "قم بتشغيل جزء من التعليمات البرمجية",
"run_command": "نفّذ أمرًا",
"docker": "التحكم في حاوية",
"tunnel": "التحكم في نفق",
"wol": "أيقظ مضيفًا",
"wait": "انتظر",
"set_var": "قم بتعيين متغير",
"if": "إذا / خلاف ذلك",
"run_automation": "قم بتشغيل عملية أتمتة أخرى",
"stop": "قف"
},
"statuses": {
"running": "جري",
"success": "نجاح",
"failed": "فشل",
"timeout": "انتهت مهلة الاتصال",
"skipped": "تم التخطي",
"cancelled": "تم الإلغاء"
},
"fields": {
"host": "يستضيف",
"metric": "متري",
"mount": "نقطة التثبيت",
"operator": "يكون",
"value": "قيمة",
"forSeconds": "استمر لمدة (ثوانٍ)",
"cooldown": "فترة التهدئة (بالدقائق)",
"cron": "تعبير كرون",
"intervalSeconds": "كل (ثانية)",
"timezone": "المنطقة الزمنية",
"timezoneServer": "وقت الخادم",
"timezoneHint": "تُقرأ أوقات Cron في هذه المنطقة. لا ينطبق ذلك على الجداول الزمنية الفاصلة.",
"container": "حاوية",
"command": "يأمر",
"url": "عنوان URL",
"method": "طريقة",
"body": "جسم",
"channels": "القنوات",
"title": "عنوان",
"message": "رسالة",
"snippet": "مقتطف",
"seconds": "ثوانٍ",
"conditionLeftPlaceholder": "قيمة يجب التحقق منها",
"conditionRightPlaceholder": "قارن بـ",
"variableName": "اسم المتغير",
"variableValue": "قيمة",
"elevated": "قم بتشغيل الأمر sudo",
"onError": "إذا فشل هذا",
"allowPrivateNetwork": "السماح بعناوين الشبكة المحلية",
"allowPrivateNetworkHint": "هذا الخيار ضروري لخدمة مُستضافة ذاتيًا على شبكتك المحلية. وهو مُعطّل افتراضيًا لمنع الطلبات إلى العناوين الداخلية."
},
"onError": {
"stop": "أوقف الجري",
"continue": "حمل"
}
}
}
},
"nav": {
"automations": "الأتمتة"
},
"onboarding": {
"feature_automations": "الأتمتة",
"feature_automations_desc": "قم بتشغيل الخطوات تلقائيًا عند حدوث شيء ما، واحصل على إشعار بذلك."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Автоматизации",
"tabAutomations": "Автоматизации",
"tabRuns": "Бягания",
"tabChannels": "Канали",
"createAutomation": "Нова автоматизация",
"backToAutomations": "Обратно към Автоматизации",
"createTitle": "Нова автоматизация",
"editTitle": "Редактиране на автоматизация",
"empty": "Все още няма автоматизации. Създайте такава, която да реагира на показател, график или събитие.",
"emptyRuns": "Все още няма бягания.",
"emptyChannels": "Все още няма канали. Добавете такъв, за да изпращате известия някъде.",
"noChannelsHint": "Все още няма настроени канали. Добавете такъв в раздела „Канали“.",
"addChannel": "Добавяне на канал",
"testChannel": "Тест",
"channelTestSent": "Тестово известие е изпратено",
"channelTestFailed": "Тестовото известие не беше успешно",
"channelDeleted": "Каналът е изтрит",
"deleteChannelConfirm": "Да се изтрие ли този канал? Автоматизациите, които го използват, ще спрат да получават известия.",
"nameLabel": "Име",
"namePlaceholder": "напр. Почистване на /данни, когато се запълни",
"nameRequired": "Името е задължително",
"descriptionLabel": "Описание",
"descriptionPlaceholder": "Незадължително описание",
"enabledLabel": "Активирано",
"concurrencyLabel": "Ако вече работи",
"concurrencyHint": "Какво да направите, когато това се задейства отново, преди да е завършило последното изпълнение.",
"concurrency": {
"skip": "Пропуснете новото бягане",
"queue": "Изчакай и бягай след това",
"allow": "Изпълнете и двете едновременно"
},
"save": "Запазване",
"created": "Автоматизация, създадена",
"updated": "Автоматизацията е запазена",
"deleted": "Автоматизацията е изтрита",
"deleteConfirm": "Да се изтрие ли тази автоматизация? Историята на нейните изпълнения също ще бъде изтрита.",
"runNow": "Бягай сега",
"testRun": "Тестов пробег",
"testRunHint": "Изпълнява всяка стъпка, но не докосва нищо извън Termix.",
"runStarted": "Бягането започна",
"runFinished": "Изпълнението е завършено: {{status}}",
"trigger": "Спусък",
"steps": "Стъпки",
"addStep": "Добавяне на стъпка",
"removeStep": "Премахване на стъпка",
"moveUp": "Премести нагоре",
"moveDown": "Премести надолу",
"noSteps": "Все още няма стъпки. Добавете една, за да кажете какво трябва да се случи.",
"webhookTokenTitle": "URL адрес на уебкукичката",
"webhookTokenDescription": "Това се показва веднъж. Запазете го сега, не може да бъде извлечено по-късно.",
"copy": "Копиране",
"copied": "Копирано",
"viewJson": "JSON",
"viewBuilder": "Строител",
"viewGraph": "Графика",
"lastRun": "Последно бягане",
"never": "Никога",
"duration": "{{seconds}}с",
"stepOutput": "Изход",
"stepError": "Грешка",
"dryRunBadge": "Тестов пробег",
"hostSelector": {
"trigger": "Задействащ хост",
"host": "Специфичен хост",
"fleet": "Флот",
"all": "Всички хостове"
},
"triggerKinds": {
"metric_threshold": "Праг на показателя",
"host_status": "Домакинът се изкачва или слиза",
"health_check": "Промени в проверката на състоянието",
"schedule": "По график",
"docker_event": "Събитие в контейнера",
"internal_event": "Събитие Termix",
"webhook": "Входящ уеб кукичка"
},
"stepTypes": {
"notify": "Изпращане на известие",
"http": "Извикване на URL адрес",
"run_snippet": "Изпълнете фрагмент",
"run_command": "Изпълнете команда",
"docker": "Контрол на контейнер",
"tunnel": "Контрол на тунел",
"wol": "Събудете домакин",
"wait": "Чакай",
"set_var": "Задаване на променлива",
"if": "Ако / в противен случай",
"run_automation": "Изпълнете друга автоматизация",
"stop": "Спри"
},
"statuses": {
"running": "Бягане",
"success": "Успех",
"failed": "Неуспешно",
"timeout": "Времето за изчакване изтече",
"skipped": "Пропуснато",
"cancelled": "Отменено"
},
"fields": {
"host": "Домакин",
"metric": "Метричен",
"mount": "Точка на монтиране",
"operator": "Е",
"value": "Стойност",
"forSeconds": "Задържа се за (секунди)",
"cooldown": "Охлаждане (минути)",
"cron": "Cron израз",
"intervalSeconds": "Всеки (секунди)",
"timezone": "Часова зона",
"timezoneServer": "Време на сървъра",
"timezoneHint": "Cron времената се отчитат в тази зона. Не се отнася за интервални графици.",
"container": "Контейнер",
"command": "Команда",
"url": "URL адрес",
"method": "Метод",
"body": "Тяло",
"channels": "Канали",
"title": "Заглавие",
"message": "Съобщение",
"snippet": "Фрагмент",
"seconds": "Секунди",
"conditionLeftPlaceholder": "Стойност за проверка",
"conditionRightPlaceholder": "Сравнете с",
"variableName": "Име на променлива",
"variableValue": "Стойност",
"elevated": "Стартирай със sudo",
"onError": "Ако това не успее",
"allowPrivateNetwork": "Разрешаване на локални мрежови адреси",
"allowPrivateNetworkHint": "Необходимо за самостоятелно хоствана услуга във вашата локална мрежа. Изключено по подразбиране, за да се предотвратят заявки към вътрешни адреси."
},
"onError": {
"stop": "Спрете бягането",
"continue": "Продължавай"
}
}
}
},
"nav": {
"automations": "Автоматизации"
},
"onboarding": {
"feature_automations": "Автоматизации",
"feature_automations_desc": "Изпълнявайте стъпките автоматично, когато нещо се случи, и получавайте известия."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "অটোমেশন",
"tabAutomations": "অটোমেশন",
"tabRuns": "রান",
"tabChannels": "চ্যানেল",
"createAutomation": "নতুন অটোমেশন",
"backToAutomations": "অটোমেশনে ফিরে যান",
"createTitle": "নতুন অটোমেশন",
"editTitle": "সম্পাদনা অটোমেশন",
"empty": "এখনো কোনো অটোমেশন নেই। কোনো মেট্রিক, শিডিউল বা ইভেন্টের প্রতিক্রিয়ায় একটি তৈরি করুন।",
"emptyRuns": "এখনো কোনো রান হয়নি।",
"emptyChannels": "এখনো কোনো চ্যানেল নেই। নোটিফিকেশন পাঠানোর জন্য একটি চ্যানেল যোগ করুন।",
"noChannelsHint": "এখনো কোনো চ্যানেল সেট আপ করা হয়নি। চ্যানেল ট্যাবে একটি চ্যানেল যোগ করুন।",
"addChannel": "চ্যানেল যোগ করুন",
"testChannel": "পরীক্ষা",
"channelTestSent": "পরীক্ষার বিজ্ঞপ্তি পাঠানো হয়েছে",
"channelTestFailed": "পরীক্ষার বিজ্ঞপ্তি ব্যর্থ হয়েছে",
"channelDeleted": "চ্যানেল মুছে ফেলা হয়েছে",
"deleteChannelConfirm": "এই চ্যানেলটি মুছে ফেললে? এটি ব্যবহার করা অটোমেশনগুলো নোটিফিকেশন পাঠানো বন্ধ করে দেবে।",
"nameLabel": "নাম",
"namePlaceholder": "যেমন, /data পূর্ণ হয়ে গেলে তা পরিষ্কার করুন।",
"nameRequired": "নাম আবশ্যক",
"descriptionLabel": "বর্ণনা",
"descriptionPlaceholder": "ঐচ্ছিক বিবরণ",
"enabledLabel": "সক্ষম করা হয়েছে",
"concurrencyLabel": "যদি এটি ইতিমধ্যে চালু থাকে",
"concurrencyHint": "শেষ রান শেষ হওয়ার আগেই এটি আবার চালু হলে কী করতে হবে।",
"concurrency": {
"skip": "নতুন রানটি এড়িয়ে যান।",
"queue": "অপেক্ষা করো এবং পিছনে দৌড়াও",
"allow": "একসাথে দুটোই চালান"
},
"save": "সংরক্ষণ করুন",
"created": "অটোমেশন তৈরি করা হয়েছে",
"updated": "অটোমেশন সংরক্ষিত",
"deleted": "অটোমেশন মুছে ফেলা হয়েছে",
"deleteConfirm": "এই অটোমেশনটি ডিলিট করলে, এর রান হিস্ট্রিও ডিলিট হয়ে যাবে।",
"runNow": "এখনই দৌড়ান",
"testRun": "পরীক্ষামূলক চালনা",
"testRunHint": "প্রতিটি ধাপ চলে কিন্তু টার্মিক্সের বাইরের কোনো কিছু স্পর্শ করে না।",
"runStarted": "দৌড় শুরু হলো",
"runFinished": "দৌড় শেষ: {{status}}",
"trigger": "ট্রিগার",
"steps": "পদক্ষেপ",
"addStep": "ধাপ যোগ করুন",
"removeStep": "ধাপটি সরান",
"moveUp": "উপরে উঠুন",
"moveDown": "নিচে নামুন",
"noSteps": "এখনো কোনো ধাপ যোগ করা হয়নি। কী হওয়া উচিত তা জানাতে একটি ধাপ যোগ করুন।",
"webhookTokenTitle": "ওয়েবহুক ইউআরএল",
"webhookTokenDescription": "এটি একবারই দেখানো হবে। এখনই সংরক্ষণ করুন, পরে আর পুনরুদ্ধার করা যাবে না।",
"copy": "অনুলিপি",
"copied": "অনুলিপি করা হয়েছে",
"viewJson": "JSON",
"viewBuilder": "নির্মাতা",
"viewGraph": "গ্রাফ",
"lastRun": "শেষ দৌড়",
"never": "কখনো না",
"duration": "{{seconds}}s",
"stepOutput": "আউটপুট",
"stepError": "ত্রুটি",
"dryRunBadge": "পরীক্ষামূলক চালনা",
"hostSelector": {
"trigger": "ট্রিগারিং হোস্ট",
"host": "নির্দিষ্ট হোস্ট",
"fleet": "ফ্লিট",
"all": "সকল হোস্ট"
},
"triggerKinds": {
"metric_threshold": "মেট্রিক থ্রেশহোল্ড",
"host_status": "হোস্ট উপরে বা নিচে যায়",
"health_check": "স্বাস্থ্য পরীক্ষার পরিবর্তন",
"schedule": "একটি সময়সূচী অনুযায়ী",
"docker_event": "কন্টেইনার ইভেন্ট",
"internal_event": "টারমিক্স ইভেন্ট",
"webhook": "ইনকামিং ওয়েবহুক"
},
"stepTypes": {
"notify": "একটি বিজ্ঞপ্তি পাঠান",
"http": "একটি URL কল করুন",
"run_snippet": "একটি স্নিপেট চালান",
"run_command": "একটি কমান্ড চালান",
"docker": "একটি কন্টেইনার নিয়ন্ত্রণ করুন",
"tunnel": "একটি টানেল নিয়ন্ত্রণ করুন",
"wol": "একজন হোস্টকে জাগান",
"wait": "অপেক্ষা করুন",
"set_var": "একটি ভেরিয়েবল সেট করুন",
"if": "যদি / অন্যথায়",
"run_automation": "আরেকটি অটোমেশন চালান",
"stop": "থামুন"
},
"statuses": {
"running": "দৌড়ানো",
"success": "সাফল্য",
"failed": "ব্যর্থ",
"timeout": "সময়সীমা শেষ",
"skipped": "বাদ দেওয়া হয়েছে",
"cancelled": "বাতিল করা হয়েছে"
},
"fields": {
"host": "হোস্ট",
"metric": "মেট্রিক",
"mount": "মাউন্ট পয়েন্ট",
"operator": "হয়",
"value": "মূল্য",
"forSeconds": "(সেকেন্ড) ধরে স্থায়ী",
"cooldown": "কুলডাউন (মিনিট)",
"cron": "ক্রোন এক্সপ্রেশন",
"intervalSeconds": "প্রতি (সেকেন্ডে)",
"timezone": "সময় অঞ্চল",
"timezoneServer": "সার্ভারের সময়",
"timezoneHint": "এই জোনে ক্রন টাইম পড়া হয়। এটি ইন্টারভাল শিডিউলের ক্ষেত্রে প্রযোজ্য নয়।",
"container": "কন্টেইনার",
"command": "আদেশ",
"url": "ইউআরএল",
"method": "পদ্ধতি",
"body": "শরীর",
"channels": "চ্যানেল",
"title": "শিরোনাম",
"message": "বার্তা",
"snippet": "স্নিপেট",
"seconds": "সেকেন্ড",
"conditionLeftPlaceholder": "যাচাই করার মান",
"conditionRightPlaceholder": "তুলনা করুন",
"variableName": "ভেরিয়েবলের নাম",
"variableValue": "মূল্য",
"elevated": "sudo দিয়ে চালান",
"onError": "যদি এটি ব্যর্থ হয়",
"allowPrivateNetwork": "স্থানীয় নেটওয়ার্ক ঠিকানাগুলির অনুমতি দিন",
"allowPrivateNetworkHint": "আপনার ল্যানে সেলফ-হোস্টেড সার্ভিসের জন্য প্রয়োজন। অভ্যন্তরীণ অ্যাড্রেসে অনুরোধ পাঠানো প্রতিরোধ করতে এটি ডিফল্টরূপে বন্ধ থাকে।"
},
"onError": {
"stop": "দৌড় থামাও",
"continue": "চালিয়ে যান"
}
}
}
},
"nav": {
"automations": "অটোমেশন"
},
"onboarding": {
"feature_automations": "অটোমেশন",
"feature_automations_desc": "কোনো ঘটনা ঘটলে স্বয়ংক্রিয়ভাবে ধাপগুলো সম্পন্ন করুন এবং বিজ্ঞপ্তি পান।"
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatitzacions",
"tabAutomations": "Automatitzacions",
"tabRuns": "Carreres",
"tabChannels": "Canals",
"createAutomation": "Nova automatització",
"backToAutomations": "Torna a Automatitzacions",
"createTitle": "Nova automatització",
"editTitle": "Edita l'automatització",
"empty": "Encara no hi ha automatitzacions. Crea'n una per reaccionar a una mètrica, una programació o un esdeveniment.",
"emptyRuns": "Encara no hi ha curses.",
"emptyChannels": "Encara no hi ha cap canal. Afegeix-ne un per enviar notificacions a algun lloc.",
"noChannelsHint": "Encara no s'ha configurat cap canal. Afegeix-ne un a la pestanya Canals.",
"addChannel": "Afegeix un canal",
"testChannel": "Prova",
"channelTestSent": "Notificació de prova enviada",
"channelTestFailed": "La notificació de prova ha fallat",
"channelDeleted": "Canal suprimit",
"deleteChannelConfirm": "Voleu suprimir aquest canal? Les automatitzacions que l'utilitzen deixaran de notificar.",
"nameLabel": "Nom",
"namePlaceholder": "p. ex., Neteja /dades quan s'ompli",
"nameRequired": "El nom és obligatori",
"descriptionLabel": "Descripció",
"descriptionPlaceholder": "Descripció opcional",
"enabledLabel": "Activat",
"concurrencyLabel": "Si ja està en funcionament",
"concurrencyHint": "Què cal fer quan això es torna a activar abans que s'hagi acabat l'última execució.",
"concurrency": {
"skip": "Omet la nova cursa",
"queue": "Espera i corre darrere",
"allow": "Executa tots dos alhora"
},
"save": "Desa",
"created": "Automatització creada",
"updated": "Automatització desada",
"deleted": "Automatització suprimida",
"deleteConfirm": "Voleu suprimir aquesta automatització? També se n'esborra l'historial d'execucions.",
"runNow": "Executa ara",
"testRun": "Prova de funcionament",
"testRunHint": "Corre cada pas però no toca res fora de Termix.",
"runStarted": "Execució iniciada",
"runFinished": "Execució acabada: {{status}}",
"trigger": "Disparador",
"steps": "Passos",
"addStep": "Afegeix un pas",
"removeStep": "Elimina el pas",
"moveUp": "Puja",
"moveDown": "Moure cap avall",
"noSteps": "Encara no hi ha cap pas. Afegeix-ne un per indicar què hauria de passar.",
"webhookTokenTitle": "URL del webhook",
"webhookTokenDescription": "Això es mostra una vegada. Desa-ho ara, no es podrà recuperar més tard.",
"copy": "Còpia",
"copied": "Copiat",
"viewJson": "JSON",
"viewBuilder": "Constructor",
"viewGraph": "Gràfic",
"lastRun": "Última cursa",
"never": "Mai",
"duration": "{{seconds}}s",
"stepOutput": "Sortida",
"stepError": "Error",
"dryRunBadge": "Prova de funcionament",
"hostSelector": {
"trigger": "Host activador",
"host": "Amfitrió específic",
"fleet": "Flota",
"all": "Tots els amfitrions"
},
"triggerKinds": {
"metric_threshold": "Llindar mètric",
"host_status": "L'amfitrió puja o baixa",
"health_check": "Canvis en la revisió de salut",
"schedule": "En un horari",
"docker_event": "Esdeveniment de contenidor",
"internal_event": "Esdeveniment de Termix",
"webhook": "Webhook entrant"
},
"stepTypes": {
"notify": "Enviar una notificació",
"http": "Crida a una URL",
"run_snippet": "Executa un fragment",
"run_command": "Executa una ordre",
"docker": "Controlar un contenidor",
"tunnel": "Controlar un túnel",
"wol": "Despertar un amfitrió",
"wait": "Espera",
"set_var": "Defineix una variable",
"if": "Si / altrament",
"run_automation": "Executa una altra automatització",
"stop": "Atura"
},
"statuses": {
"running": "Córrer",
"success": "Èxit",
"failed": "Fallat",
"timeout": "Temps d'espera esgotat",
"skipped": "Omès",
"cancelled": "Cancel·lat"
},
"fields": {
"host": "Amfitrió",
"metric": "Mètrica",
"mount": "Punt de muntatge",
"operator": "És",
"value": "Valor",
"forSeconds": "Sostingut durant (segons)",
"cooldown": "Refredament (minuts)",
"cron": "Expressió cron",
"intervalSeconds": "Cada (segons)",
"timezone": "Zona horària",
"timezoneServer": "Hora del servidor",
"timezoneHint": "Els temps cron es llegeixen en aquesta zona. No s'aplica a les programacions per intervals.",
"container": "Contenidor",
"command": "Comandament",
"url": "URL",
"method": "Mètode",
"body": "Cos",
"channels": "Canals",
"title": "Títol",
"message": "Missatge",
"snippet": "Fragment",
"seconds": "Segons",
"conditionLeftPlaceholder": "Valor a comprovar",
"conditionRightPlaceholder": "Comparar amb",
"variableName": "Nom de la variable",
"variableValue": "Valor",
"elevated": "Executar amb sudo",
"onError": "Si això falla",
"allowPrivateNetwork": "Permetre adreces de xarxa locals",
"allowPrivateNetworkHint": "Necessari per a un servei autoallotjat a la vostra LAN. Desactivat per defecte per evitar sol·licituds a adreces internes."
},
"onError": {
"stop": "Atura la cursa",
"continue": "Continua"
}
}
}
},
"nav": {
"automations": "Automatitzacions"
},
"onboarding": {
"feature_automations": "Automatitzacions",
"feature_automations_desc": "Executa els passos automàticament quan passi alguna cosa i rep notificacions."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatizace",
"tabAutomations": "Automatizace",
"tabRuns": "Běhy",
"tabChannels": "Kanály",
"createAutomation": "Nová automatizace",
"backToAutomations": "Zpět k automatizaci",
"createTitle": "Nová automatizace",
"editTitle": "Upravit automatizaci",
"empty": "Zatím žádné automatizace. Vytvořte si automatizaci, která bude reagovat na metriku, plán nebo událost.",
"emptyRuns": "Zatím žádné běhy.",
"emptyChannels": "Zatím žádné kanály. Přidejte jeden, abyste někam mohli odesílat oznámení.",
"noChannelsHint": "Zatím nejsou nastaveny žádné kanály. Přidejte jeden na kartě Kanály.",
"addChannel": "Přidat kanál",
"testChannel": "Test",
"channelTestSent": "Testovací oznámení odesláno",
"channelTestFailed": "Testovací oznámení selhalo",
"channelDeleted": "Kanál smazán",
"deleteChannelConfirm": "Smazat tento kanál? Automatizace, které ho používají, přestanou zasílat upozornění.",
"nameLabel": "Jméno",
"namePlaceholder": "např. Vyčistit /data, když se zaplní",
"nameRequired": "Jméno je povinné",
"descriptionLabel": "Popis",
"descriptionPlaceholder": "Volitelný popis",
"enabledLabel": "Povoleno",
"concurrencyLabel": "Pokud již běží",
"concurrencyHint": "Co dělat, když se to spustí znovu před dokončením posledního běhu.",
"concurrency": {
"skip": "Přeskočit nový běh",
"queue": "Čekej a běž za ním",
"allow": "Spustit obojí najednou"
},
"save": "Uložit",
"created": "Automatizace vytvořena",
"updated": "Automatizace uložena",
"deleted": "Automatizace smazána",
"deleteConfirm": "Smazat tuto automatizaci? Její historie spuštění bude také smazána.",
"runNow": "Běhej hned",
"testRun": "Zkušební provoz",
"testRunHint": "Provádí každý krok, ale nedotýká se ničeho mimo Termix.",
"runStarted": "Běh byl zahájen",
"runFinished": "Běh dokončen: {{status}}",
"trigger": "Spoušť",
"steps": "Kroky",
"addStep": "Přidat krok",
"removeStep": "Odebrat krok",
"moveUp": "Posunout nahoru",
"moveDown": "Přesunout dolů",
"noSteps": "Zatím žádné kroky. Přidejte jeden, který popisuje, co by se mělo stát.",
"webhookTokenTitle": "URL webhooku",
"webhookTokenDescription": "Toto se zobrazí jednou. Uložte si to nyní, později to nelze načíst.",
"copy": "Kopie",
"copied": "Zkopírováno",
"viewJson": "JSON",
"viewBuilder": "Stavitel",
"viewGraph": "Graf",
"lastRun": "Poslední běh",
"never": "Nikdy",
"duration": "{{seconds}}s",
"stepOutput": "Výstup",
"stepError": "Chyba",
"dryRunBadge": "Zkušební provoz",
"hostSelector": {
"trigger": "Spouštěcí hostitel",
"host": "Specifický hostitel",
"fleet": "Loďstvo",
"all": "Všichni hostitelé"
},
"triggerKinds": {
"metric_threshold": "Prahová hodnota metriky",
"host_status": "Hostitel jde nahoru nebo dolů",
"health_check": "Změny v kontrolách stavu",
"schedule": "Podle plánu",
"docker_event": "Událost kontejneru",
"internal_event": "Událost Termix",
"webhook": "Příchozí webhook"
},
"stepTypes": {
"notify": "Odeslat oznámení",
"http": "Zavolat URL",
"run_snippet": "Spustit úryvek",
"run_command": "Spusťte příkaz",
"docker": "Ovládání kontejneru",
"tunnel": "Ovládejte tunel",
"wol": "Probuďte hostitele",
"wait": "Počkejte",
"set_var": "Nastavení proměnné",
"if": "Pokud / jinak",
"run_automation": "Spusťte další automatizaci",
"stop": "Zastávka"
},
"statuses": {
"running": "Běh",
"success": "Úspěch",
"failed": "Selhalo",
"timeout": "Časový limit vypršel",
"skipped": "Přeskočeno",
"cancelled": "Zrušeno"
},
"fields": {
"host": "Hostitel",
"metric": "Metrický",
"mount": "Bod připojení",
"operator": "Je",
"value": "Hodnota",
"forSeconds": "Trvalé po dobu (sekund)",
"cooldown": "Doba ochlazování (minuty)",
"cron": "Cron výraz",
"intervalSeconds": "Každých (sekund)",
"timezone": "Časové pásmo",
"timezoneServer": "Čas serveru",
"timezoneHint": "V této zóně se odečítají časy Cron. Nevztahuje se na intervalové plány.",
"container": "Kontejner",
"command": "Příkaz",
"url": "URL",
"method": "Metoda",
"body": "Tělo",
"channels": "Kanály",
"title": "Titul",
"message": "Zpráva",
"snippet": "Úryvek",
"seconds": "Sekundy",
"conditionLeftPlaceholder": "Hodnota ke kontrole",
"conditionRightPlaceholder": "Porovnat s",
"variableName": "Název proměnné",
"variableValue": "Hodnota",
"elevated": "Spusťte pomocí sudo",
"onError": "Pokud se to nepodaří",
"allowPrivateNetwork": "Povolit lokální síťové adresy",
"allowPrivateNetworkHint": "Vyžadováno pro službu hostovanou na vašem lokálním serveru (LAN). Ve výchozím nastavení vypnuto, aby se zabránilo požadavkům na interní adresy."
},
"onError": {
"stop": "Zastav běh",
"continue": "Pokračovat"
}
}
}
},
"nav": {
"automations": "Automatizace"
},
"onboarding": {
"feature_automations": "Automatizace",
"feature_automations_desc": "Automaticky spouštět kroky, když se něco stane, a dostávat upozornění."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatiseringer",
"tabAutomations": "Automatiseringer",
"tabRuns": "Løber",
"tabChannels": "Kanaler",
"createAutomation": "Ny automatisering",
"backToAutomations": "Tilbage til Automatiseringer",
"createTitle": "Ny automatisering",
"editTitle": "Rediger automatisering",
"empty": "Ingen automatiseringer endnu. Opret en for at reagere på en metric, en tidsplan eller en begivenhed.",
"emptyRuns": "Ingen løb endnu.",
"emptyChannels": "Ingen kanaler endnu. Tilføj en for at sende notifikationer et sted hen.",
"noChannelsHint": "Ingen kanaler er konfigureret endnu. Tilføj en i fanen Kanaler.",
"addChannel": "Tilføj kanal",
"testChannel": "Prøve",
"channelTestSent": "Testnotifikation sendt",
"channelTestFailed": "Testnotifikation mislykkedes",
"channelDeleted": "Kanal slettet",
"deleteChannelConfirm": "Vil du slette denne kanal? Automatiseringer, der bruger den, vil stoppe med at sende dig notifikationer.",
"nameLabel": "Navn",
"namePlaceholder": "f.eks. Ryd op i /data når den fyldes op",
"nameRequired": "Navn er påkrævet",
"descriptionLabel": "Beskrivelse",
"descriptionPlaceholder": "Valgfri beskrivelse",
"enabledLabel": "Aktiveret",
"concurrencyLabel": "Hvis den allerede kører",
"concurrencyHint": "Hvad skal man gøre, når dette udløses igen, før den sidste kørsel er færdig.",
"concurrency": {
"skip": "Spring den nye kørsel over",
"queue": "Vent og løb efter",
"allow": "Kør begge på én gang"
},
"save": "Spare",
"created": "Automatisering skabt",
"updated": "Automatisering gemt",
"deleted": "Automatisering slettet",
"deleteConfirm": "Vil du slette denne automatisering? Dens kørselshistorik slettes også.",
"runNow": "Løb nu",
"testRun": "Testkørsel",
"testRunHint": "Kører hvert trin, men rører ikke noget uden for Termix.",
"runStarted": "Løbet startede",
"runFinished": "Kørsel færdig: {{status}}",
"trigger": "Udløser",
"steps": "Trin",
"addStep": "Tilføj trin",
"removeStep": "Fjern trin",
"moveUp": "Flyt op",
"moveDown": "Flyt ned",
"noSteps": "Ingen trin endnu. Tilføj et trin for at angive, hvad der skal ske.",
"webhookTokenTitle": "Webhook-URL",
"webhookTokenDescription": "Dette vises én gang. Gem det nu, det kan ikke hentes senere.",
"copy": "Kopi",
"copied": "Kopieret",
"viewJson": "JSON",
"viewBuilder": "Byggefirma",
"viewGraph": "Kurve",
"lastRun": "Sidste løb",
"never": "Aldrig",
"duration": "{{seconds}}s",
"stepOutput": "Produktion",
"stepError": "Fejl",
"dryRunBadge": "Testkørsel",
"hostSelector": {
"trigger": "Udløsende vært",
"host": "Specifik vært",
"fleet": "Flåde",
"all": "Alle værter"
},
"triggerKinds": {
"metric_threshold": "Metrisk tærskel",
"host_status": "Værten går op eller ned",
"health_check": "Ændringer i sundhedstjek",
"schedule": "På en tidsplan",
"docker_event": "Containerhændelse",
"internal_event": "Termix-begivenhed",
"webhook": "Indgående webhook"
},
"stepTypes": {
"notify": "Send en notifikation",
"http": "Kald en URL",
"run_snippet": "Kør et uddrag",
"run_command": "Kør en kommando",
"docker": "Styr en container",
"tunnel": "Kontroller en tunnel",
"wol": "Væk en vært",
"wait": "Vente",
"set_var": "Angiv en variabel",
"if": "Hvis / ellers",
"run_automation": "Kør en anden automatisering",
"stop": "Stop"
},
"statuses": {
"running": "Løb",
"success": "Succes",
"failed": "Mislykkedes",
"timeout": "Timeout",
"skipped": "Springet over",
"cancelled": "Annulleret"
},
"fields": {
"host": "Vært",
"metric": "Metrisk",
"mount": "Monteringspunkt",
"operator": "Er",
"value": "Værdi",
"forSeconds": "Vedvarende i (sekunder)",
"cooldown": "Nedkølingstid (minutter)",
"cron": "Cron-udtryk",
"intervalSeconds": "Hvert (sekund)",
"timezone": "Tidszone",
"timezoneServer": "Servertid",
"timezoneHint": "Cron-tider aflæses i denne zone. Gælder ikke for intervalplaner.",
"container": "Beholder",
"command": "Kommando",
"url": "URL",
"method": "Metode",
"body": "Legeme",
"channels": "Kanaler",
"title": "Titel",
"message": "Besked",
"snippet": "Uddrag",
"seconds": "Sekunder",
"conditionLeftPlaceholder": "Værdi at kontrollere",
"conditionRightPlaceholder": "Sammenlign med",
"variableName": "Variabelnavn",
"variableValue": "Værdi",
"elevated": "Kør med sudo",
"onError": "Hvis dette mislykkes",
"allowPrivateNetwork": "Tillad lokale netværksadresser",
"allowPrivateNetworkHint": "Nødvendig for en selvhostet tjeneste på dit LAN. Deaktiveret som standard for at forhindre anmodninger til interne adresser."
},
"onError": {
"stop": "Stop løbet",
"continue": "Fortsæt"
}
}
}
},
"nav": {
"automations": "Automatiseringer"
},
"onboarding": {
"feature_automations": "Automatiseringer",
"feature_automations_desc": "Kør trin automatisk, når der sker noget, og få besked."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatisierungen",
"tabAutomations": "Automatisierungen",
"tabRuns": "Läufe",
"tabChannels": "Kanäle",
"createAutomation": "Neue Automatisierung",
"backToAutomations": "Zurück zu Automatisierungen",
"createTitle": "Neue Automatisierung",
"editTitle": "Automatisierung bearbeiten",
"empty": "Noch keine Automatisierungen. Erstellen Sie eine, um auf eine Kennzahl, einen Zeitplan oder ein Ereignis zu reagieren.",
"emptyRuns": "Bisher keine Läufe.",
"emptyChannels": "Noch keine Kanäle. Fügen Sie einen hinzu, um Benachrichtigungen an einen bestimmten Ort zu senden.",
"noChannelsHint": "Es sind noch keine Kanäle eingerichtet. Fügen Sie einen im Tab „Kanäle“ hinzu.",
"addChannel": "Kanal hinzufügen",
"testChannel": "Prüfen",
"channelTestSent": "Testbenachrichtigung gesendet",
"channelTestFailed": "Testbenachrichtigung fehlgeschlagen",
"channelDeleted": "Kanal gelöscht",
"deleteChannelConfirm": "Diesen Kanal löschen? Automatisierungen, die diesen Kanal verwenden, werden keine Benachrichtigungen mehr senden.",
"nameLabel": "Name",
"namePlaceholder": "z.B. das Verzeichnis /data leeren, wenn es voll ist",
"nameRequired": "Name erforderlich",
"descriptionLabel": "Beschreibung",
"descriptionPlaceholder": "Optionale Beschreibung",
"enabledLabel": "Ermöglicht",
"concurrencyLabel": "Wenn es bereits läuft",
"concurrencyHint": "Was ist zu tun, wenn dies erneut ausgelöst wird, bevor der letzte Durchlauf abgeschlossen ist?",
"concurrency": {
"skip": "Den neuen Lauf überspringen.",
"queue": "Warten und dann hinterherlaufen",
"allow": "Führe beide gleichzeitig aus"
},
"save": "Speichern",
"created": "Automatisierung erstellt",
"updated": "Automatisierung gerettet",
"deleted": "Automatisierung gelöscht",
"deleteConfirm": "Wenn Sie diese Automatisierung löschen, wird auch ihr Ausführungsverlauf gelöscht.",
"runNow": "Jetzt laufen",
"testRun": "Probelauf",
"testRunHint": "Führt jeden Schritt aus, greift aber auf nichts außerhalb von Termix ein.",
"runStarted": "Lauf gestartet",
"runFinished": "Lauf beendet: {{status}}",
"trigger": "Auslösen",
"steps": "Schritte",
"addStep": "Schritt hinzufügen",
"removeStep": "Schritt entfernen",
"moveUp": "Aufsteigen",
"moveDown": "Nach unten schieben",
"noSteps": "Noch keine Schritte. Füge einen hinzu, um zu beschreiben, was passieren soll.",
"webhookTokenTitle": "Webhook-URL",
"webhookTokenDescription": "Dies wird nur einmal angezeigt. Speichern Sie es jetzt, es kann später nicht mehr abgerufen werden.",
"copy": "Kopie",
"copied": "Kopiert",
"viewJson": "JSON",
"viewBuilder": "Bauherr",
"viewGraph": "Graph",
"lastRun": "Letzter Lauf",
"never": "Niemals",
"duration": "{{seconds}}s",
"stepOutput": "Ausgabe",
"stepError": "Fehler",
"dryRunBadge": "Probelauf",
"hostSelector": {
"trigger": "Auslösender Host",
"host": "Spezifischer Wirt",
"fleet": "Flotte",
"all": "Alle Gastgeber"
},
"triggerKinds": {
"metric_threshold": "Metrischer Schwellenwert",
"host_status": "Der Host steigt oder fällt ab",
"health_check": "Änderungen bei Gesundheitschecks",
"schedule": "Nach einem Zeitplan",
"docker_event": "Container-Ereignis",
"internal_event": "Termix-Ereignis",
"webhook": "Eingehender Webhook"
},
"stepTypes": {
"notify": "Sende eine Benachrichtigung",
"http": "URL aufrufen",
"run_snippet": "Führe einen Codeausschnitt aus",
"run_command": "Führe einen Befehl aus",
"docker": "Einen Container steuern",
"tunnel": "Einen Tunnel steuern",
"wol": "Wecken Sie einen Gastgeber",
"wait": "Warten",
"set_var": "Eine Variable setzen",
"if": "Falls / andernfalls",
"run_automation": "Führe eine weitere Automatisierung aus",
"stop": "Stoppen"
},
"statuses": {
"running": "Läuft",
"success": "Erfolg",
"failed": "Fehlgeschlagen",
"timeout": "Zeitüberschreitung",
"skipped": "Übersprungen",
"cancelled": "Abgesagt"
},
"fields": {
"host": "Gastgeber",
"metric": "Metrisch",
"mount": "Montagepunkt",
"operator": "Ist",
"value": "Wert",
"forSeconds": "Anhaltend für (Sekunden)",
"cooldown": "Abkühlzeit (Minuten)",
"cron": "Cron-Ausdruck",
"intervalSeconds": "Alle (Sekunden)",
"timezone": "Zeitzone",
"timezoneServer": "Serverzeit",
"timezoneHint": "In dieser Zone werden Cron-Zeiten gelesen. Gilt nicht für Intervallpläne.",
"container": "Container",
"command": "Befehl",
"url": "URL",
"method": "Verfahren",
"body": "Körper",
"channels": "Kanäle",
"title": "Titel",
"message": "Nachricht",
"snippet": "Ausschnitt",
"seconds": "Sekunden",
"conditionLeftPlaceholder": "Zu prüfender Wert",
"conditionRightPlaceholder": "Vergleiche mit",
"variableName": "Variablenname",
"variableValue": "Wert",
"elevated": "Mit sudo ausführen",
"onError": "Wenn das fehlschlägt",
"allowPrivateNetwork": "Lokale Netzwerkadressen zulassen",
"allowPrivateNetworkHint": "Wird für einen selbstgehosteten Dienst in Ihrem LAN benötigt. Standardmäßig deaktiviert, um Anfragen an interne Adressen zu verhindern."
},
"onError": {
"stop": "Stoppt die Flucht!",
"continue": "Weiter geht's"
}
}
}
},
"nav": {
"automations": "Automatisierungen"
},
"onboarding": {
"feature_automations": "Automatisierungen",
"feature_automations_desc": "Schritte automatisch ausführen, wenn etwas passiert, und Benachrichtigungen erhalten."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Αυτοματισμοί",
"tabAutomations": "Αυτοματισμοί",
"tabRuns": "Τρεξίματα",
"tabChannels": "Κανάλια",
"createAutomation": "Νέος Αυτοματισμός",
"backToAutomations": "Επιστροφή στους Αυτοματισμούς",
"createTitle": "Νέος Αυτοματισμός",
"editTitle": "Επεξεργασία αυτοματισμού",
"empty": "Δεν υπάρχουν ακόμη αυτοματοποιήσεις. Δημιουργήστε μία για να αντιδράτε σε μια μέτρηση, ένα πρόγραμμα ή ένα συμβάν.",
"emptyRuns": "Δεν υπάρχουν τρεξίματα ακόμα.",
"emptyChannels": "Δεν υπάρχουν ακόμη κανάλια. Προσθέστε ένα για να στέλνετε ειδοποιήσεις κάπου.",
"noChannelsHint": "Δεν έχουν ρυθμιστεί ακόμη κανάλια. Προσθέστε ένα στην καρτέλα Κανάλια.",
"addChannel": "Προσθήκη καναλιού",
"testChannel": "Δοκιμή",
"channelTestSent": "Αποστολή ειδοποίησης δοκιμής",
"channelTestFailed": "Η ειδοποίηση δοκιμής απέτυχε.",
"channelDeleted": "Το κανάλι διαγράφηκε",
"deleteChannelConfirm": "Διαγραφή αυτού του καναλιού; Οι αυτοματοποιήσεις που το χρησιμοποιούν θα σταματήσουν να ειδοποιούν.",
"nameLabel": "Ονομα",
"namePlaceholder": "π.χ., Καθαρισμός /data όταν γεμίσει",
"nameRequired": "Απαιτείται όνομα",
"descriptionLabel": "Περιγραφή",
"descriptionPlaceholder": "Προαιρετική περιγραφή",
"enabledLabel": "Ενεργοποιημένο",
"concurrencyLabel": "Εάν ήδη εκτελείται",
"concurrencyHint": "Τι πρέπει να κάνετε όταν αυτό ενεργοποιηθεί ξανά πριν ολοκληρωθεί η τελευταία εκτέλεση.",
"concurrency": {
"skip": "Παράλειψη της νέας διαδρομής",
"queue": "Περίμενε και τρέχα πίσω",
"allow": "Εκτελέστε και τα δύο ταυτόχρονα"
},
"save": "Εκτός",
"created": "Αυτοματοποίηση που δημιουργήθηκε",
"updated": "Ο αυτοματισμός αποθηκεύτηκε",
"deleted": "Ο αυτοματισμός διαγράφηκε.",
"deleteConfirm": "Διαγραφή αυτού του αυτοματισμού; Το ιστορικό εκτέλεσής του διαγράφεται επίσης.",
"runNow": "Εκτέλεση τώρα",
"testRun": "Δοκιμαστική εκτέλεση",
"testRunHint": "Εκτελεί κάθε βήμα αλλά δεν αγγίζει τίποτα εκτός του Termix.",
"runStarted": "Η εκτέλεση ξεκίνησε",
"runFinished": "Η εκτέλεση ολοκληρώθηκε: {{status}}",
"trigger": "Σκανδάλη",
"steps": "Βήματα",
"addStep": "Προσθήκη βήματος",
"removeStep": "Αφαίρεση βήματος",
"moveUp": "Μετακίνηση προς τα πάνω",
"moveDown": "Μετακίνηση προς τα κάτω",
"noSteps": "Δεν υπάρχουν ακόμη βήματα. Προσθέστε ένα για να πείτε τι πρέπει να συμβεί.",
"webhookTokenTitle": "URL ιστού",
"webhookTokenDescription": "Αυτό εμφανίζεται μία φορά. Αποθηκεύστε το τώρα, δεν μπορεί να ανακτηθεί αργότερα.",
"copy": "Αντίγραφο",
"copied": "Αντιγράφηκε",
"viewJson": "JSON",
"viewBuilder": "Οικοδόμος",
"viewGraph": "Γραφική παράσταση",
"lastRun": "Τελευταία εκτέλεση",
"never": "Ποτέ",
"duration": "{{seconds}}s",
"stepOutput": "Παραγωγή",
"stepError": "Σφάλμα",
"dryRunBadge": "Δοκιμαστική εκτέλεση",
"hostSelector": {
"trigger": "Ενεργοποίηση κεντρικού υπολογιστή",
"host": "Συγκεκριμένος κεντρικός υπολογιστής",
"fleet": "Στόλος",
"all": "Όλοι οι οικοδεσπότες"
},
"triggerKinds": {
"metric_threshold": "Μετρικό όριο",
"host_status": "Ο κεντρικός υπολογιστής ανεβαίνει ή κατεβαίνει",
"health_check": "Αλλαγές στον έλεγχο υγείας",
"schedule": "Σε πρόγραμμα",
"docker_event": "Συμβάν κοντέινερ",
"internal_event": "Συμβάν Termix",
"webhook": "Εισερχόμενο webhook"
},
"stepTypes": {
"notify": "Αποστολή ειδοποίησης",
"http": "Κλήση μιας διεύθυνσης URL",
"run_snippet": "Εκτέλεση ενός αποσπάσματος",
"run_command": "Εκτέλεση εντολής",
"docker": "Έλεγχος ενός κοντέινερ",
"tunnel": "Έλεγχος μιας σήραγγας",
"wol": "Ξυπνήστε έναν οικοδεσπότη",
"wait": "Περιμένετε",
"set_var": "Ορισμός μεταβλητής",
"if": "Εάν / διαφορετικά",
"run_automation": "Εκτελέστε έναν άλλο αυτοματισμό",
"stop": "Στάση"
},
"statuses": {
"running": "Τρέξιμο",
"success": "Επιτυχία",
"failed": "Αποτυχημένος",
"timeout": "Λήξη χρονικού ορίου",
"skipped": "Παραλήφθηκε",
"cancelled": "Ακυρώθηκε"
},
"fields": {
"host": "Πλήθος",
"metric": "Μετρικός",
"mount": "Σημείο προσάρτησης",
"operator": "Είναι",
"value": "Αξία",
"forSeconds": "Διαρκεί για (δευτερόλεπτα)",
"cooldown": "Χρόνος αναμονής (λεπτά)",
"cron": "Έκφραση Cron",
"intervalSeconds": "Κάθε (δευτερόλεπτα)",
"timezone": "Ζώνη ώρας",
"timezoneServer": "Ώρα διακομιστή",
"timezoneHint": "Οι χρόνοι Cron διαβάζονται σε αυτήν τη ζώνη. Δεν ισχύει για τα χρονοδιαγράμματα διαστημάτων.",
"container": "Δοχείο",
"command": "Εντολή",
"url": "URL",
"method": "Μέθοδος",
"body": "Σώμα",
"channels": "Κανάλια",
"title": "Τίτλος",
"message": "Μήνυμα",
"snippet": "Απόσπασμα",
"seconds": "Δευτερόλεπτα",
"conditionLeftPlaceholder": "Τιμή προς έλεγχο",
"conditionRightPlaceholder": "Σύγκριση με",
"variableName": "Όνομα μεταβλητής",
"variableValue": "Αξία",
"elevated": "Εκτέλεση με sudo",
"onError": "Εάν αυτό αποτύχει",
"allowPrivateNetwork": "Επιτρέψτε τις τοπικές διευθύνσεις δικτύου",
"allowPrivateNetworkHint": "Απαιτείται για μια αυτο-φιλοξενούμενη υπηρεσία στο LAN σας. Απενεργοποιημένο από προεπιλογή για την αποτροπή αιτημάτων σε εσωτερικές διευθύνσεις."
},
"onError": {
"stop": "Σταμάτα το τρέξιμο",
"continue": "Συνεχίζω"
}
}
}
},
"nav": {
"automations": "Αυτοματισμοί"
},
"onboarding": {
"feature_automations": "Αυτοματισμοί",
"feature_automations_desc": "Εκτελέστε αυτόματα βήματα όταν συμβεί κάτι και λάβετε ειδοποίηση."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatizaciones",
"tabAutomations": "Automatizaciones",
"tabRuns": "Corre",
"tabChannels": "Canales",
"createAutomation": "Nueva automatización",
"backToAutomations": "Volver a Automatizaciones",
"createTitle": "Nueva automatización",
"editTitle": "Automatización de edición",
"empty": "Aún no hay automatizaciones. Crea una para que reaccione a una métrica, una programación o un evento.",
"emptyRuns": "Aún no hay carreras.",
"emptyChannels": "Aún no hay canales. Añade uno para enviar notificaciones a algún lugar.",
"noChannelsHint": "Aún no hay canales configurados. Añade uno en la pestaña Canales.",
"addChannel": "Agregar canal",
"testChannel": "Prueba",
"channelTestSent": "Notificación de prueba enviada",
"channelTestFailed": "Falló la notificación de prueba",
"channelDeleted": "Canal eliminado",
"deleteChannelConfirm": "¿Eliminar este canal? Las automatizaciones que lo utilicen dejarán de enviar notificaciones.",
"nameLabel": "Nombre",
"namePlaceholder": "Por ejemplo, limpiar /data cuando se llena",
"nameRequired": "Se requiere el nombre",
"descriptionLabel": "Descripción",
"descriptionPlaceholder": "Descripción opcional",
"enabledLabel": "Activado",
"concurrencyLabel": "Si ya está en funcionamiento",
"concurrencyHint": "Qué hacer si esto se activa de nuevo antes de que finalice la última ejecución.",
"concurrency": {
"skip": "Saltarse la nueva carrera",
"queue": "Espera y corre tras",
"allow": "Ejecuta ambos a la vez"
},
"save": "Ahorrar",
"created": "Automatización creada",
"updated": "Automatización guardada",
"deleted": "Automatización eliminada",
"deleteConfirm": "¿Eliminar esta automatización? Su historial de ejecución también se eliminará.",
"runNow": "Corre ahora",
"testRun": "Ejecución de prueba",
"testRunHint": "Ejecuta todos los pasos, pero no toca nada fuera de Termix.",
"runStarted": "La carrera ha comenzado.",
"runFinished": "Ejecución finalizada: {{status}}",
"trigger": "Desencadenar",
"steps": "Pasos",
"addStep": "Agregar paso",
"removeStep": "Eliminar paso",
"moveUp": "Subir",
"moveDown": "Baja",
"noSteps": "Aún no hay pasos a seguir. Añade uno para indicar qué debería suceder.",
"webhookTokenTitle": "URL del webhook",
"webhookTokenDescription": "Esto se muestra una sola vez. Guárdalo ahora, no se podrá recuperar más tarde.",
"copy": "Copiar",
"copied": "Copiado",
"viewJson": "JSON",
"viewBuilder": "Constructor",
"viewGraph": "Gráfico",
"lastRun": "Última carrera",
"never": "Nunca",
"duration": "{{seconds}}s",
"stepOutput": "Producción",
"stepError": "Error",
"dryRunBadge": "Ejecución de prueba",
"hostSelector": {
"trigger": "Host desencadenante",
"host": "Anfitrión específico",
"fleet": "Flota",
"all": "Todos los anfitriones"
},
"triggerKinds": {
"metric_threshold": "Umbral métrico",
"host_status": "El host sube o baja",
"health_check": "Cambios en los chequeos médicos",
"schedule": "Según un horario",
"docker_event": "Evento de contenedor",
"internal_event": "Evento Termix",
"webhook": "Webhook entrante"
},
"stepTypes": {
"notify": "Enviar una notificación",
"http": "Llamar a una URL",
"run_snippet": "Ejecutar un fragmento",
"run_command": "Ejecutar un comando",
"docker": "Controlar un contenedor",
"tunnel": "Controlar un túnel",
"wol": "Despierta a un anfitrión",
"wait": "Esperar",
"set_var": "Establecer una variable",
"if": "Si / de lo contrario",
"run_automation": "Ejecuta otra automatización",
"stop": "Detener"
},
"statuses": {
"running": "Correr",
"success": "Éxito",
"failed": "Fallido",
"timeout": "Tiempo de espera agotado",
"skipped": "Saltado",
"cancelled": "Cancelado"
},
"fields": {
"host": "Anfitrión",
"metric": "Métrico",
"mount": "Punto de montaje",
"operator": "Es",
"value": "Valor",
"forSeconds": "Mantenido durante (segundos)",
"cooldown": "Tiempo de enfriamiento (minutos)",
"cron": "Expresión cron",
"intervalSeconds": "Cada (segundos)",
"timezone": "Huso horario",
"timezoneServer": "Hora del servidor",
"timezoneHint": "En esta zona se leen las horas cron. No se aplica a los horarios de intervalo.",
"container": "Recipiente",
"command": "Dominio",
"url": "URL",
"method": "Método",
"body": "Cuerpo",
"channels": "Canales",
"title": "Título",
"message": "Mensaje",
"snippet": "Retazo",
"seconds": "Artículos de segunda clase",
"conditionLeftPlaceholder": "Valor a comprobar",
"conditionRightPlaceholder": "Comparar con",
"variableName": "nombre de la variable",
"variableValue": "Valor",
"elevated": "Ejecutar con sudo",
"onError": "Si esto falla",
"allowPrivateNetwork": "Permitir direcciones de red local",
"allowPrivateNetworkHint": "Necesario para un servicio autohospedado en su LAN. Desactivado por defecto para evitar solicitudes a direcciones internas."
},
"onError": {
"stop": "Detengan la carrera",
"continue": "Continuar"
}
}
}
},
"nav": {
"automations": "Automatizaciones"
},
"onboarding": {
"feature_automations": "Automatizaciones",
"feature_automations_desc": "Ejecuta pasos automáticamente cuando ocurra algo y recibe notificaciones."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automaatiot",
"tabAutomations": "Automaatiot",
"tabRuns": "Juoksut",
"tabChannels": "Kanavat",
"createAutomation": "Uusi automaatio",
"backToAutomations": "Takaisin automaatioihin",
"createTitle": "Uusi automaatio",
"editTitle": "Muokkaa automatisointia",
"empty": "Ei vielä automaatioita. Luo sellainen reagoimaan mittariin, aikatauluun tai tapahtumaan.",
"emptyRuns": "Ei vielä juoksuja.",
"emptyChannels": "Ei kanavia vielä. Lisää yksi lähettääksesi ilmoituksia jonnekin.",
"noChannelsHint": "Kanavia ei ole vielä määritetty. Lisää yksi Kanavat-välilehdellä.",
"addChannel": "Lisää kanava",
"testChannel": "Testata",
"channelTestSent": "Testi-ilmoitus lähetetty",
"channelTestFailed": "Testi-ilmoitus epäonnistui",
"channelDeleted": "Kanava poistettu",
"deleteChannelConfirm": "Poistetaanko tämä kanava? Sitä käyttävät automaatiot lakkaavat lähettämästä ilmoituksia.",
"nameLabel": "Nimi",
"namePlaceholder": "esim. Siivoa /data, kun se täyttyy",
"nameRequired": "Nimi on pakollinen",
"descriptionLabel": "Kuvaus",
"descriptionPlaceholder": "Valinnainen kuvaus",
"enabledLabel": "Käytössä",
"concurrencyLabel": "Jos se on jo käynnissä",
"concurrencyHint": "Mitä tehdä, kun tämä laukeaa uudelleen ennen viimeisen ajon päättymistä.",
"concurrency": {
"skip": "Ohita uusi juoksu",
"queue": "Odota ja juokse perässä",
"allow": "Suorita molemmat yhtä aikaa"
},
"save": "Tallentaa",
"created": "Automaatio luotu",
"updated": "Automaatio tallennettu",
"deleted": "Automaatio poistettu",
"deleteConfirm": "Poistetaanko tämä automaatio? Myös sen suoritushistoria poistetaan.",
"runNow": "Juokse nyt",
"testRun": "Koeajo",
"testRunHint": "Juoksee jokaisen askeleen, mutta ei koske mihinkään Termixin ulkopuolella.",
"runStarted": "Juoksu aloitettu",
"runFinished": "Suoritus päättynyt: {{status}}",
"trigger": "Laukaista",
"steps": "Askeleet",
"addStep": "Lisää vaihe",
"removeStep": "Poista vaihe",
"moveUp": "Siirry ylös",
"moveDown": "Siirrä alas",
"noSteps": "Ei vielä vaiheita. Lisää yksi, joka kertoo, mitä pitäisi tapahtua.",
"webhookTokenTitle": "Webhookin URL-osoite",
"webhookTokenDescription": "Tämä näytetään kerran. Tallenna se nyt, sitä ei voi palauttaa myöhemmin.",
"copy": "Kopioida",
"copied": "Kopioitu",
"viewJson": "JSON",
"viewBuilder": "Rakentaja",
"viewGraph": "Kaavio",
"lastRun": "Viimeisin juoksu",
"never": "Ei koskaan",
"duration": "{{seconds}}s",
"stepOutput": "Lähtö",
"stepError": "Virhe",
"dryRunBadge": "Koeajo",
"hostSelector": {
"trigger": "Käynnistävä isäntä",
"host": "Tietty isäntä",
"fleet": "Laivasto",
"all": "Kaikki isännät"
},
"triggerKinds": {
"metric_threshold": "Metrinen kynnysarvo",
"host_status": "Isäntä nousee tai laskee",
"health_check": "Terveystarkastusten muutokset",
"schedule": "Aikataulussa",
"docker_event": "Säiliötapahtuma",
"internal_event": "Termix-tapahtuma",
"webhook": "Saapuva webhook"
},
"stepTypes": {
"notify": "Lähetä ilmoitus",
"http": "Soita URL-osoitteeseen",
"run_snippet": "Suorita koodinpätkä",
"run_command": "Suorita komento",
"docker": "Säiliön hallinta",
"tunnel": "Hallitse tunnelia",
"wol": "Herätä isäntä",
"wait": "Odota",
"set_var": "Aseta muuttuja",
"if": "Jos / muuten",
"run_automation": "Suorita toinen automaatio",
"stop": "Stop"
},
"statuses": {
"running": "Juoksu",
"success": "Menestys",
"failed": "Epäonnistui",
"timeout": "Aikakatkaisu",
"skipped": "Ohitettu",
"cancelled": "Peruutettu"
},
"fields": {
"host": "Isäntä",
"metric": "Metrinen",
"mount": "Kiinnityskohta",
"operator": "On",
"value": "Arvo",
"forSeconds": "Jatkuu (sekuntia)",
"cooldown": "Jäähdyttelyaika (minuuttia)",
"cron": "Cron-lauseke",
"intervalSeconds": "Joka (sekunti)",
"timezone": "Aikavyöhyke",
"timezoneServer": "Palvelimen aika",
"timezoneHint": "Cron-ajat luetaan tällä alueella. Ei koske intervalliaikatauluja.",
"container": "Kontti",
"command": "Komento",
"url": "URL-osoite",
"method": "Menetelmä",
"body": "Keho",
"channels": "Kanavat",
"title": "Otsikko",
"message": "Viesti",
"snippet": "Katkelma",
"seconds": "Sekuntia",
"conditionLeftPlaceholder": "Tarkistettava arvo",
"conditionRightPlaceholder": "Vertaa",
"variableName": "Muuttujan nimi",
"variableValue": "Arvo",
"elevated": "Suorita sudon avulla",
"onError": "Jos tämä epäonnistuu",
"allowPrivateNetwork": "Salli paikallisverkko-osoitteet",
"allowPrivateNetworkHint": "Tarvitaan lähiverkossasi itse isännöidylle palvelulle. Oletusarvoisesti pois päältä estääkseen pyynnöt sisäisiin osoitteisiin."
},
"onError": {
"stop": "Pysäytä juoksu",
"continue": "Jatkaa"
}
}
}
},
"nav": {
"automations": "Automaatiot"
},
"onboarding": {
"feature_automations": "Automaatiot",
"feature_automations_desc": "Suorita vaiheet automaattisesti, kun jotain tapahtuu, ja saat ilmoituksen."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatisations",
"tabAutomations": "Automatisations",
"tabRuns": "Courses",
"tabChannels": "Chaînes",
"createAutomation": "Nouvelle automatisation",
"backToAutomations": "Retour aux automatisations",
"createTitle": "Nouvelle automatisation",
"editTitle": "Automatisation de la modification",
"empty": "Aucune automatisation pour le moment. Créez-en une pour qu'elle réagisse à une mesure, une planification ou un événement.",
"emptyRuns": "Pas encore de runs.",
"emptyChannels": "Aucun canal pour le moment. Ajoutez-en un pour envoyer des notifications.",
"noChannelsHint": "Aucune chaîne n'est encore configurée. Ajoutez-en une dans l'onglet Chaînes.",
"addChannel": "Ajouter une chaîne",
"testChannel": "Test",
"channelTestSent": "Notification de test envoyée",
"channelTestFailed": "La notification de test a échoué",
"channelDeleted": "Chaîne supprimée",
"deleteChannelConfirm": "Supprimer ce canal ? Les automatisations qui l'utilisent cesseront d'envoyer des notifications.",
"nameLabel": "Nom",
"namePlaceholder": "Par exemple, nettoyer le répertoire /data lorsqu'il est plein.",
"nameRequired": "Le nom est requis.",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Description facultative",
"enabledLabel": "Activé",
"concurrencyLabel": "S'il est déjà en cours d'exécution",
"concurrencyHint": "Que faire si ce problème se reproduit avant la fin de la dernière exécution ?",
"concurrency": {
"skip": "Évitez la nouvelle course",
"queue": "Attendez et courez après",
"allow": "Exécutez les deux simultanément."
},
"save": "Sauvegarder",
"created": "L'automatisation a créé",
"updated": "L'automatisation a permis de sauver",
"deleted": "Automatisation supprimée",
"deleteConfirm": "Supprimer cette automatisation ? Son historique d'exécution est également supprimé.",
"runNow": "Courez maintenant",
"testRun": "Essai",
"testRunHint": "Exécute chaque étape mais ne touche à rien en dehors de Termix.",
"runStarted": "La course a commencé",
"runFinished": "Course terminée : {{status}}",
"trigger": "Déclenchement",
"steps": "Mesures",
"addStep": "Ajouter une étape",
"removeStep": "Supprimer l'étape",
"moveUp": "Monter en gamme",
"moveDown": "Descendre",
"noSteps": "Aucune étape pour l'instant. Ajoutez-en une pour décrire ce qui doit se passer.",
"webhookTokenTitle": "URL du webhook",
"webhookTokenDescription": "Ceci ne s'affiche qu'une seule fois. Enregistrez-le maintenant, il ne pourra pas être récupéré ultérieurement.",
"copy": "Copie",
"copied": "Copié",
"viewJson": "JSON",
"viewBuilder": "Constructeur",
"viewGraph": "Graphique",
"lastRun": "Dernière course",
"never": "Jamais",
"duration": "{{seconds}}s",
"stepOutput": "Sortir",
"stepError": "Erreur",
"dryRunBadge": "Essai",
"hostSelector": {
"trigger": "Hôte déclencheur",
"host": "hôte spécifique",
"fleet": "Flotte",
"all": "Tous les hôtes"
},
"triggerKinds": {
"metric_threshold": "Seuil métrique",
"host_status": "L'hôte monte ou descend",
"health_check": "changements au bilan de santé",
"schedule": "Selon un horaire",
"docker_event": "événement conteneur",
"internal_event": "Événement Termix",
"webhook": "Webhook entrant"
},
"stepTypes": {
"notify": "Envoyer une notification",
"http": "Appeler une URL",
"run_snippet": "Exécuter un extrait de code",
"run_command": "Exécuter une commande",
"docker": "Contrôler un conteneur",
"tunnel": "Contrôler un tunnel",
"wol": "Réveillez un hôte",
"wait": "Attendez",
"set_var": "Définir une variable",
"if": "Si / sinon",
"run_automation": "Exécutez une autre automatisation",
"stop": "Arrêt"
},
"statuses": {
"running": "En cours d'exécution",
"success": "Succès",
"failed": "Échoué",
"timeout": "Délai d'attente dépassé",
"skipped": "Omission",
"cancelled": "Annulé"
},
"fields": {
"host": "Hôte",
"metric": "Métrique",
"mount": "Point de montage",
"operator": "Est",
"value": "Valeur",
"forSeconds": "Maintenu pendant (secondes)",
"cooldown": "Temps de récupération (minutes)",
"cron": "Expression de Cron",
"intervalSeconds": "Toutes les (secondes)",
"timezone": "Fuseau horaire",
"timezoneServer": "Heure du serveur",
"timezoneHint": "Les heures des tâches cron sont lues dans cette zone. Ceci ne s'applique pas aux planifications par intervalles.",
"container": "Récipient",
"command": "Commande",
"url": "URL",
"method": "Méthode",
"body": "Corps",
"channels": "Chaînes",
"title": "Titre",
"message": "Message",
"snippet": "Fragment",
"seconds": "Secondes",
"conditionLeftPlaceholder": "Valeur à vérifier",
"conditionRightPlaceholder": "Comparer à",
"variableName": "Nom de la variable",
"variableValue": "Valeur",
"elevated": "Exécuter avec sudo",
"onError": "Si cela échoue",
"allowPrivateNetwork": "Autoriser les adresses réseau locales",
"allowPrivateNetworkHint": "Nécessaire pour un service auto-hébergé sur votre réseau local. Désactivé par défaut pour empêcher les requêtes vers des adresses internes."
},
"onError": {
"stop": "Arrêtez la course",
"continue": "Continuer"
}
}
}
},
"nav": {
"automations": "Automatisations"
},
"onboarding": {
"feature_automations": "Automatisations",
"feature_automations_desc": "Exécutez des actions automatiquement lorsqu'un événement se produit et recevez une notification."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "אוטומציות",
"tabAutomations": "אוטומציות",
"tabRuns": "ריצות",
"tabChannels": "ערוצים",
"createAutomation": "אוטומציה חדשה",
"backToAutomations": "חזרה לאוטומציות",
"createTitle": "אוטומציה חדשה",
"editTitle": "עריכה אוטומציה",
"empty": "אין עדיין אוטומציות. צור אחת שתגיב למדד, לוח זמנים או אירוע.",
"emptyRuns": "עדיין אין ריצות.",
"emptyChannels": "אין עדיין ערוצים. הוסף ערוצים כדי לשלוח התראות לאנשהו.",
"noChannelsHint": "עדיין לא הוגדרו ערוצים. הוסף אחד בכרטיסייה ערוצים.",
"addChannel": "הוסף ערוץ",
"testChannel": "מִבְחָן",
"channelTestSent": "הודעת בדיקה נשלחה",
"channelTestFailed": "הודעת הבדיקה נכשלה",
"channelDeleted": "הערוץ נמחק",
"deleteChannelConfirm": "למחוק את הערוץ הזה? אוטומציות המשתמשות בו יפסיקו לשלוח התראות.",
"nameLabel": "שֵׁם",
"namePlaceholder": "לדוגמה, ניקוי /נתונים כאשר הוא מתמלא",
"nameRequired": "שם נדרש",
"descriptionLabel": "תֵאוּר",
"descriptionPlaceholder": "תיאור אופציונלי",
"enabledLabel": "מופעל",
"concurrencyLabel": "אם זה כבר פועל",
"concurrencyHint": "מה לעשות כאשר זה מופעל שוב לפני שהריצה האחרונה הסתיימה.",
"concurrency": {
"skip": "דלג על הריצה החדשה",
"queue": "חכו ורצנו אחרי",
"allow": "הפעל את שניהם בו זמנית"
},
"save": "לְהַצִיל",
"created": "אוטומציה נוצרה",
"updated": "אוטומציה נשמרה",
"deleted": "אוטומציה נמחקה",
"deleteConfirm": "למחוק את האוטומציה הזו? גם היסטוריית הריצות שלה נמחקת.",
"runNow": "רוץ עכשיו",
"testRun": "ריצת מבחן",
"testRunHint": "רץ כל שלב אבל לא נוגע בשום דבר מחוץ לטרמיקס.",
"runStarted": "הריצה התחילה",
"runFinished": "ריצה הסתיימה: {{status}}",
"trigger": "לְהַפְעִיל",
"steps": "צעדים",
"addStep": "הוסף שלב",
"removeStep": "הסר שלב",
"moveUp": "הזז למעלה",
"moveDown": "הזז למטה",
"noSteps": "אין עדיין שלבים. הוסף שלבים כדי לומר מה אמור לקרות.",
"webhookTokenTitle": "כתובת אתר של Webhook",
"webhookTokenDescription": "זה מוצג פעם אחת. שמור את זה עכשיו, לא ניתן לאחזר את זה מאוחר יותר.",
"copy": "לְהַעְתִיק",
"copied": "מוּעֲתָק",
"viewJson": "JSON",
"viewBuilder": "בּוֹנֶה",
"viewGraph": "גרָף",
"lastRun": "ריצה אחרונה",
"never": "לְעוֹלָם לֹא",
"duration": "{{seconds}}ים",
"stepOutput": "תְפוּקָה",
"stepError": "שְׁגִיאָה",
"dryRunBadge": "ריצת מבחן",
"hostSelector": {
"trigger": "מארח מפעיל",
"host": "מארח ספציפי",
"fleet": "צִי",
"all": "כל המארחים"
},
"triggerKinds": {
"metric_threshold": "סף מטרי",
"host_status": "המארח עולה או יורד",
"health_check": "שינויים בבדיקת הבריאות",
"schedule": "על פי לוח זמנים",
"docker_event": "אירוע מיכל",
"internal_event": "אירוע טרמיקס",
"webhook": "וובהוק נכנס"
},
"stepTypes": {
"notify": "שלח הודעה",
"http": "קריאה לכתובת URL",
"run_snippet": "הפעלת קטע קוד",
"run_command": "הפעל פקודה",
"docker": "שליטה על מכולה",
"tunnel": "שליטה במנהרה",
"wol": "להעיר מארח",
"wait": "לַחֲכוֹת",
"set_var": "הגדר משתנה",
"if": "אם / אחרת",
"run_automation": "הפעל אוטומציה נוספת",
"stop": "לְהַפְסִיק"
},
"statuses": {
"running": "רִיצָה",
"success": "הַצלָחָה",
"failed": "נִכשָׁל",
"timeout": "תם הזמן",
"skipped": "דילג",
"cancelled": "בּוּטלָה"
},
"fields": {
"host": "מְאָרֵחַ",
"metric": "מֶטרִי",
"mount": "נקודת הרכבה",
"operator": "האם",
"value": "עֵרֶך",
"forSeconds": "נמשך (שניות)",
"cooldown": "זמן קירור (דקות)",
"cron": "ביטוי קרון",
"intervalSeconds": "כל (שניות)",
"timezone": "אזור זמן",
"timezoneServer": "זמן שרת",
"timezoneHint": "זמני Cron נקראים באזור זה. לא חל על לוחות זמנים של מרווחים.",
"container": "מְכוֹלָה",
"command": "פְּקוּדָה",
"url": "כתובת אתר",
"method": "שִׁיטָה",
"body": "גוּף",
"channels": "ערוצים",
"title": "כּוֹתֶרֶת",
"message": "הוֹדָעָה",
"snippet": "קטע",
"seconds": "שניות",
"conditionLeftPlaceholder": "ערך לבדיקה",
"conditionRightPlaceholder": "השווה מול",
"variableName": "שם משתנה",
"variableValue": "עֵרֶך",
"elevated": "רוץ עם סודו",
"onError": "אם זה ייכשל",
"allowPrivateNetwork": "אפשר כתובות רשת מקומיות",
"allowPrivateNetworkHint": "נדרש עבור שירות אירוח עצמי ברשת הרשת המקומית שלך. כבוי כברירת מחדל כדי למנוע בקשות לכתובות פנימיות."
},
"onError": {
"stop": "עצור את הריצה",
"continue": "לְהַמשִׁיך"
}
}
}
},
"nav": {
"automations": "אוטומציות"
},
"onboarding": {
"feature_automations": "אוטומציות",
"feature_automations_desc": "הפעל שלבים באופן אוטומטי כאשר משהו קורה, וקבל הודעה."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "स्वचालन",
"tabAutomations": "स्वचालन",
"tabRuns": "दौड़ें",
"tabChannels": "चैनल",
"createAutomation": "नया स्वचालन",
"backToAutomations": "स्वचालन पर वापस जाएँ",
"createTitle": "नया स्वचालन",
"editTitle": "संपादन स्वचालन",
"empty": "अभी तक कोई ऑटोमेशन नहीं है। किसी मेट्रिक, शेड्यूल या इवेंट के आधार पर प्रतिक्रिया देने के लिए एक ऑटोमेशन बनाएं।",
"emptyRuns": "अभी तक कोई रन नहीं बना है।",
"emptyChannels": "अभी तक कोई चैनल नहीं है। सूचनाएं भेजने के लिए एक चैनल जोड़ें।",
"noChannelsHint": "अभी तक कोई चैनल सेट अप नहीं किया गया है। चैनल टैब में एक चैनल जोड़ें।",
"addChannel": "चैनल जोड़ें",
"testChannel": "परीक्षा",
"channelTestSent": "परीक्षण अधिसूचना भेजी गई",
"channelTestFailed": "टेस्ट नोटिफिकेशन विफल रहा",
"channelDeleted": "चैनल हटा दिया गया",
"deleteChannelConfirm": "क्या इस चैनल को हटा दें? इसका उपयोग करने वाले ऑटोमेशन नोटिफिकेशन भेजना बंद कर देंगे।",
"nameLabel": "नाम",
"namePlaceholder": "उदाहरण के लिए, जब /data भर जाए तो उसे साफ़ कर दें।",
"nameRequired": "नाम आवश्यक है",
"descriptionLabel": "विवरण",
"descriptionPlaceholder": "वैकल्पिक विवरण",
"enabledLabel": "सक्रिय",
"concurrencyLabel": "यदि यह पहले से चल रहा है",
"concurrencyHint": "पिछली बार रन पूरा होने से पहले ही अगर यह समस्या दोबारा उत्पन्न हो जाए तो क्या करें।",
"concurrency": {
"skip": "नए रन को छोड़ दें",
"queue": "प्रतीक्षा करो और उसके पीछे दौड़ो",
"allow": "दोनों को एक साथ चलाएं"
},
"save": "बचाना",
"created": "स्वचालन द्वारा निर्मित",
"updated": "स्वचालन ने बचत की",
"deleted": "स्वचालित रूप से हटा दिया गया",
"deleteConfirm": "इस ऑटोमेशन को डिलीट करने पर, इसका रन हिस्ट्री भी डिलीट हो जाएगा।",
"runNow": "अब दौड़े",
"testRun": "परीक्षण के लिए चलाना",
"testRunHint": "यह हर चरण में दौड़ता है लेकिन टर्मिक्स के बाहर किसी भी चीज को नहीं छूता है।",
"runStarted": "दौड़ शुरू हो गई",
"runFinished": "रन समाप्त: {{status}}",
"trigger": "चालू कर देना",
"steps": "चरण",
"addStep": "चरण जोड़ें",
"removeStep": "चरण हटाएँ",
"moveUp": "बढ़ाना",
"moveDown": "नीचे की ओर",
"noSteps": "अभी तक कोई चरण निर्धारित नहीं हैं। कृपया एक चरण जोड़कर बताएं कि आगे क्या होना चाहिए।",
"webhookTokenTitle": "वेबहुक यूआरएल",
"webhookTokenDescription": "यह केवल एक बार दिखाया जाएगा। इसे अभी सहेज लें, बाद में इसे पुनः प्राप्त नहीं किया जा सकेगा।",
"copy": "प्रतिलिपि",
"copied": "कॉपी किया गया",
"viewJson": "जेएन",
"viewBuilder": "निर्माता",
"viewGraph": "ग्राफ़",
"lastRun": "आखरी बार",
"never": "कभी नहीं",
"duration": "{{seconds}}s",
"stepOutput": "उत्पादन",
"stepError": "गलती",
"dryRunBadge": "परीक्षण के लिए चलाना",
"hostSelector": {
"trigger": "ट्रिगरिंग होस्ट",
"host": "विशिष्ट मेज़बान",
"fleet": "बेड़ा",
"all": "सभी मेजबान"
},
"triggerKinds": {
"metric_threshold": "मीट्रिक सीमा",
"host_status": "होस्ट ऊपर या नीचे जाता है",
"health_check": "स्वास्थ्य जांच में बदलाव",
"schedule": "एक निर्धारित कार्यक्रम के अनुसार",
"docker_event": "कंटेनर घटना",
"internal_event": "टर्मिक्स घटना",
"webhook": "इनकमिंग वेबहुक"
},
"stepTypes": {
"notify": "सूचना भेजें",
"http": "किसी URL को कॉल करें",
"run_snippet": "एक स्निपेट चलाएँ",
"run_command": "एक कमांड चलाएँ",
"docker": "एक कंटेनर को नियंत्रित करें",
"tunnel": "एक सुरंग को नियंत्रित करें",
"wol": "किसी मेजबान को जगाओ",
"wait": "इंतज़ार",
"set_var": "एक चर सेट करें",
"if": "यदि / अन्यथा",
"run_automation": "एक और ऑटोमेशन चलाएँ",
"stop": "रुकना"
},
"statuses": {
"running": "दौड़ना",
"success": "सफलता",
"failed": "असफल",
"timeout": "समय समााप्त",
"skipped": "छोड़ा गया",
"cancelled": "रद्द कर दिया गया"
},
"fields": {
"host": "मेज़बान",
"metric": "मीट्रिक",
"mount": "माउंट पॉइंट",
"operator": "है",
"value": "कीमत",
"forSeconds": "(सेकंड) तक कायम रहा",
"cooldown": "कूलडाउन (मिनटों में)",
"cron": "क्रोन अभिव्यक्ति",
"intervalSeconds": "हर (सेकंड)",
"timezone": "समय क्षेत्र",
"timezoneServer": "सर्वर समय",
"timezoneHint": "इस ज़ोन में क्रॉन टाइम पढ़े जाते हैं। यह इंटरवल शेड्यूल पर लागू नहीं होता है।",
"container": "पात्र",
"command": "आज्ञा",
"url": "यूआरएल",
"method": "तरीका",
"body": "शरीर",
"channels": "चैनल",
"title": "शीर्षक",
"message": "संदेश",
"snippet": "टुकड़ा",
"seconds": "सेकंड",
"conditionLeftPlaceholder": "जाँच करने के लिए मान",
"conditionRightPlaceholder": "तुलना करें",
"variableName": "चर का नाम",
"variableValue": "कीमत",
"elevated": "sudo के साथ चलाएँ",
"onError": "यदि यह विफल हो जाता है",
"allowPrivateNetwork": "स्थानीय नेटवर्क पतों की अनुमति दें",
"allowPrivateNetworkHint": "आपके लोकल एरिया नेटवर्क (LAN) पर सेल्फ-होस्टेड सेवा के लिए आवश्यक। आंतरिक पतों पर अनुरोधों को रोकने के लिए डिफ़ॉल्ट रूप से बंद।"
},
"onError": {
"stop": "दौड़ रोको",
"continue": "जारी रखो"
}
}
}
},
"nav": {
"automations": "स्वचालन"
},
"onboarding": {
"feature_automations": "स्वचालन",
"feature_automations_desc": "कुछ घटित होने पर चरणों को स्वचालित रूप से चलाएं और सूचना प्राप्त करें।"
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatizálások",
"tabAutomations": "Automatizálások",
"tabRuns": "Futások",
"tabChannels": "Csatornák",
"createAutomation": "Új automatizálás",
"backToAutomations": "Vissza az automatizálásokhoz",
"createTitle": "Új automatizálás",
"editTitle": "Szerkesztési automatizálás",
"empty": "Még nincsenek automatizálások. Hozz létre egyet, amely egy mérőszámra, ütemezésre vagy eseményre reagál.",
"emptyRuns": "Még nincsenek futások.",
"emptyChannels": "Még nincsenek csatornák. Adj hozzá egyet, hogy értesítéseket küldhess valahova.",
"noChannelsHint": "Még nincsenek beállítva csatornák. Adjon hozzá egyet a Csatornák fülön.",
"addChannel": "Csatorna hozzáadása",
"testChannel": "Teszt",
"channelTestSent": "Tesztértesítés elküldve",
"channelTestFailed": "Tesztértesítés sikertelen",
"channelDeleted": "Csatorna törölve",
"deleteChannelConfirm": "Törlöd ezt a csatornát? Az azt használó automatizálások nem fognak értesítéseket küldeni.",
"nameLabel": "Név",
"namePlaceholder": "pl. /data törlése, amikor megtelik",
"nameRequired": "Név megadása kötelező",
"descriptionLabel": "Leírás",
"descriptionPlaceholder": "Opcionális leírás",
"enabledLabel": "Engedélyezve",
"concurrencyLabel": "Ha már fut",
"concurrencyHint": "Mi a teendő, ha ez ismét aktiválódik, mielőtt az utolsó futtatás befejeződött?",
"concurrency": {
"skip": "Kihagyja az új futást",
"queue": "Várj és fuss utána",
"allow": "Futtassa mindkettőt egyszerre"
},
"save": "Megtakarítás",
"created": "Automatizálás létrehozva",
"updated": "Automatizálás mentve",
"deleted": "Automatizálás törölve",
"deleteConfirm": "Törli ezt az automatizálást? A futási előzményei is törlődnek.",
"runNow": "Futtatás most",
"testRun": "Próbaüzem",
"testRunHint": "Minden lépést lefuttat, de a Termixen kívül semmihez sem ér hozzá.",
"runStarted": "Futtatás elkezdve",
"runFinished": "Futtatás befejezve: {{status}}",
"trigger": "Trigger",
"steps": "Lépések",
"addStep": "Lépés hozzáadása",
"removeStep": "Lépés eltávolítása",
"moveUp": "Feljebb mozgatás",
"moveDown": "Lejjebb mozgatás",
"noSteps": "Még nincsenek lépések. Írj be egyet, ami leírja, hogy minek kellene történnie.",
"webhookTokenTitle": "Webhook URL-címe",
"webhookTokenDescription": "Ez egyszer jelenik meg. Mentsd el most, később nem lesz elérhető.",
"copy": "Másolat",
"copied": "Másolva",
"viewJson": "JSON",
"viewBuilder": "Építész",
"viewGraph": "Grafikon",
"lastRun": "Utolsó futás",
"never": "Soha",
"duration": "{{seconds}}s",
"stepOutput": "Kimenet",
"stepError": "Hiba",
"dryRunBadge": "Próbaüzem",
"hostSelector": {
"trigger": "Kiváltó gazdagép",
"host": "Adott gazdagép",
"fleet": "Flotta",
"all": "Minden házigazda"
},
"triggerKinds": {
"metric_threshold": "Metrikus küszöbérték",
"host_status": "A host fel- vagy lekapcsolódik",
"health_check": "Egészségügyi ellenőrzés változásai",
"schedule": "Ütemterv szerint",
"docker_event": "Konténeresemény",
"internal_event": "Termix esemény",
"webhook": "Bejövő webhook"
},
"stepTypes": {
"notify": "Értesítés küldése",
"http": "URL hívása",
"run_snippet": "Kódrészlet futtatása",
"run_command": "Parancs futtatása",
"docker": "Egy konténer vezérlése",
"tunnel": "Irányíts egy alagutat",
"wol": "Ébresszen fel egy házigazdát",
"wait": "Várjon",
"set_var": "Változó beállítása",
"if": "Ha / egyébként",
"run_automation": "Futtasson egy másik automatizálást",
"stop": "Stop"
},
"statuses": {
"running": "Futás",
"success": "Siker",
"failed": "Sikertelen",
"timeout": "Időtúllépés történt",
"skipped": "Kihagyva",
"cancelled": "Lemondva"
},
"fields": {
"host": "Házigazda",
"metric": "Metrika",
"mount": "Csatlakozási pont",
"operator": "Van",
"value": "Érték",
"forSeconds": "Fenntartott (másodperc)",
"cooldown": "Lehűlési idő (perc)",
"cron": "Cron kifejezés",
"intervalSeconds": "Minden (másodperc)",
"timezone": "Időzóna",
"timezoneServer": "Szerveridő",
"timezoneHint": "A Cron idők leolvasása ebben a zónában történik. Nem vonatkozik az intervallumos ütemtervekre.",
"container": "Tartály",
"command": "Parancs",
"url": "URL-cím",
"method": "Módszer",
"body": "Test",
"channels": "Csatornák",
"title": "Cím",
"message": "Üzenet",
"snippet": "Töredék",
"seconds": "Másodperc",
"conditionLeftPlaceholder": "Ellenőrizendő érték",
"conditionRightPlaceholder": "Összevet",
"variableName": "Változó neve",
"variableValue": "Érték",
"elevated": "Futtassa a sudo paranccsal",
"onError": "Ha ez nem sikerül",
"allowPrivateNetwork": "Helyi hálózati címek engedélyezése",
"allowPrivateNetworkHint": "Szükséges egy saját tárhelyen futó szolgáltatáshoz a helyi hálózaton. Alapértelmezés szerint ki van kapcsolva, hogy megakadályozza a belső címekre irányuló kéréseket."
},
"onError": {
"stop": "Állítsd meg a futást",
"continue": "Folytat"
}
}
}
},
"nav": {
"automations": "Automatizálások"
},
"onboarding": {
"feature_automations": "Automatizálások",
"feature_automations_desc": "Automatikusan futtassa a lépéseket, amikor valami történik, és kapjon értesítést."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Otomatisasi",
"tabAutomations": "Otomatisasi",
"tabRuns": "Lari",
"tabChannels": "Saluran",
"createAutomation": "Otomatisasi Baru",
"backToAutomations": "Kembali ke Otomatisasi",
"createTitle": "Otomatisasi Baru",
"editTitle": "Edit Otomatisasi",
"empty": "Belum ada otomatisasi. Buat satu untuk bereaksi terhadap metrik, jadwal, atau peristiwa.",
"emptyRuns": "Belum ada percobaan lari.",
"emptyChannels": "Belum ada saluran. Tambahkan satu untuk mengirim notifikasi ke suatu tempat.",
"noChannelsHint": "Belum ada saluran yang dibuat. Tambahkan satu di tab Saluran.",
"addChannel": "Tambahkan Saluran",
"testChannel": "Tes",
"channelTestSent": "Notifikasi pengujian telah dikirim.",
"channelTestFailed": "Notifikasi pengujian gagal",
"channelDeleted": "Saluran dihapus",
"deleteChannelConfirm": "Hapus saluran ini? Otomatisasi yang menggunakannya akan berhenti memberi notifikasi.",
"nameLabel": "Nama",
"namePlaceholder": "misalnya, Bersihkan /data saat sudah penuh",
"nameRequired": "Nama wajib diisi.",
"descriptionLabel": "Keterangan",
"descriptionPlaceholder": "Deskripsi opsional",
"enabledLabel": "Diaktifkan",
"concurrencyLabel": "Jika sudah berjalan",
"concurrencyHint": "Apa yang harus dilakukan jika ini terpicu lagi sebelum proses terakhir selesai.",
"concurrency": {
"skip": "Lewati lari baru",
"queue": "Tunggu dan lari setelahnya",
"allow": "Jalankan keduanya sekaligus"
},
"save": "Menyimpan",
"created": "Otomatisasi dibuat",
"updated": "Otomatisasi tersimpan",
"deleted": "Otomatisasi dihapus",
"deleteConfirm": "Hapus otomatisasi ini? Riwayat eksekusinya juga akan terhapus.",
"runNow": "Lari sekarang",
"testRun": "Uji coba",
"testRunHint": "Berjalan di setiap langkah tetapi tidak menyentuh apa pun di luar Termix.",
"runStarted": "Lari dimulai",
"runFinished": "Eksekusi selesai: {{status}}",
"trigger": "Pemicu",
"steps": "Tangga",
"addStep": "Tambahkan langkah",
"removeStep": "Hapus langkah",
"moveUp": "Naikkan",
"moveDown": "Turun ke bawah",
"noSteps": "Belum ada langkah. Tambahkan satu langkah untuk menjelaskan apa yang seharusnya terjadi.",
"webhookTokenTitle": "URL Webhook",
"webhookTokenDescription": "Ini hanya ditampilkan sekali. Simpan sekarang, karena tidak dapat diambil kembali nanti.",
"copy": "Menyalin",
"copied": "Disalin",
"viewJson": "JSON",
"viewBuilder": "Pembangun",
"viewGraph": "Grafik",
"lastRun": "Lari terakhir",
"never": "Tidak pernah",
"duration": "{{seconds}}s",
"stepOutput": "Keluaran",
"stepError": "Kesalahan",
"dryRunBadge": "Uji coba",
"hostSelector": {
"trigger": "Host pemicu",
"host": "Host spesifik",
"fleet": "Armada",
"all": "Semua tuan rumah"
},
"triggerKinds": {
"metric_threshold": "Ambang batas metrik",
"host_status": "Pembawa acara naik atau turun",
"health_check": "Perubahan pemeriksaan kesehatan",
"schedule": "Sesuai jadwal",
"docker_event": "Acara kontainer",
"internal_event": "Acara Termix",
"webhook": "Webhook masuk"
},
"stepTypes": {
"notify": "Kirim pemberitahuan",
"http": "Memanggil URL",
"run_snippet": "Jalankan cuplikan kode",
"run_command": "Jalankan perintah",
"docker": "Mengendalikan sebuah kontainer",
"tunnel": "Mengendalikan terowongan",
"wol": "Bangunkan tuan rumah",
"wait": "Tunggu",
"set_var": "Tetapkan sebuah variabel",
"if": "Jika/sebaliknya",
"run_automation": "Jalankan otomatisasi lainnya",
"stop": "Berhenti"
},
"statuses": {
"running": "Berlari",
"success": "Kesuksesan",
"failed": "Gagal",
"timeout": "Waktu habis",
"skipped": "Dilewati",
"cancelled": "Dibatalkan"
},
"fields": {
"host": "Tuan rumah",
"metric": "Metrik",
"mount": "Titik pemasangan",
"operator": "Adalah",
"value": "Nilai",
"forSeconds": "Berlangsung selama (detik)",
"cooldown": "Waktu pendinginan (menit)",
"cron": "Ekspresi Cron",
"intervalSeconds": "Setiap (detik)",
"timezone": "Zona waktu",
"timezoneServer": "Waktu server",
"timezoneHint": "Waktu Cron dibaca di zona ini. Tidak berlaku untuk jadwal interval.",
"container": "Wadah",
"command": "Memerintah",
"url": "URL",
"method": "Metode",
"body": "Tubuh",
"channels": "Saluran",
"title": "Judul",
"message": "Pesan",
"snippet": "Potongan",
"seconds": "Detik",
"conditionLeftPlaceholder": "Nilai yang perlu diperiksa",
"conditionRightPlaceholder": "Bandingkan dengan",
"variableName": "Nama variabel",
"variableValue": "Nilai",
"elevated": "Jalankan dengan sudo",
"onError": "Jika ini gagal",
"allowPrivateNetwork": "Izinkan alamat jaringan lokal",
"allowPrivateNetworkHint": "Diperlukan untuk layanan yang dihosting sendiri di LAN Anda. Secara default dinonaktifkan untuk mencegah permintaan ke alamat internal."
},
"onError": {
"stop": "Hentikan larinya",
"continue": "Melanjutkan"
}
}
}
},
"nav": {
"automations": "Otomatisasi"
},
"onboarding": {
"feature_automations": "Otomatisasi",
"feature_automations_desc": "Jalankan langkah-langkah secara otomatis saat sesuatu terjadi, dan dapatkan notifikasi."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automazioni",
"tabAutomations": "Automazioni",
"tabRuns": "Corse",
"tabChannels": "Canali",
"createAutomation": "Nuova automazione",
"backToAutomations": "Torna alle automazioni",
"createTitle": "Nuova automazione",
"editTitle": "Automazione di modifica",
"empty": "Nessuna automazione ancora disponibile. Creane una per reagire a una metrica, a una pianificazione o a un evento.",
"emptyRuns": "Nessuna corsa ancora.",
"emptyChannels": "Nessun canale ancora. Aggiungine uno per inviare notifiche da qualche parte.",
"noChannelsHint": "Nessun canale configurato al momento. Aggiungine uno nella scheda Canali.",
"addChannel": "Aggiungi canale",
"testChannel": "Test",
"channelTestSent": "Notifica di test inviata",
"channelTestFailed": "Notifica di prova non riuscita",
"channelDeleted": "Canale eliminato",
"deleteChannelConfirm": "Eliminare questo canale? Le automazioni che lo utilizzano smetteranno di inviare notifiche.",
"nameLabel": "Nome",
"namePlaceholder": "Ad esempio, pulire /data quando è pieno",
"nameRequired": "Il nome è obbligatorio",
"descriptionLabel": "Descrizione",
"descriptionPlaceholder": "Descrizione facoltativa",
"enabledLabel": "Abilitato",
"concurrencyLabel": "Se è già in esecuzione",
"concurrencyHint": "Cosa fare se questo problema si ripresenta prima che l'ultima esecuzione sia terminata.",
"concurrency": {
"skip": "Salta la nuova corsa",
"queue": "Aspetta e corri dopo",
"allow": "Eseguili entrambi contemporaneamente"
},
"save": "Salva",
"created": "Automazione creata",
"updated": "Automazione salvata",
"deleted": "Automazione eliminata",
"deleteConfirm": "Eliminare questa automazione? Anche la sua cronologia di esecuzione verrà eliminata.",
"runNow": "Corri adesso",
"testRun": "Esecuzione del test",
"testRunHint": "Esegue ogni passaggio ma non tocca nulla al di fuori di Termix.",
"runStarted": "Corsa iniziata",
"runFinished": "Corsa terminata: {{status}}",
"trigger": "Grilletto",
"steps": "Passaggi",
"addStep": "Aggiungi passaggio",
"removeStep": "Rimuovere il passaggio",
"moveUp": "Spostarsi verso l'alto",
"moveDown": "Scendendo",
"noSteps": "Nessun passaggio ancora. Aggiungine uno per specificare cosa dovrebbe succedere.",
"webhookTokenTitle": "URL del webhook",
"webhookTokenDescription": "Questa immagine viene visualizzata una sola volta. Salvala ora, non sarà possibile recuperarla in seguito.",
"copy": "Copia",
"copied": "Copiato",
"viewJson": "JSON",
"viewBuilder": "Costruttore",
"viewGraph": "Grafico",
"lastRun": "Prima corsa",
"never": "Mai",
"duration": "{{seconds}}s",
"stepOutput": "Produzione",
"stepError": "Errore",
"dryRunBadge": "Esecuzione del test",
"hostSelector": {
"trigger": "Host che ha attivato l'evento",
"host": "Ospite specifico",
"fleet": "Flotta",
"all": "Tutti gli host"
},
"triggerKinds": {
"metric_threshold": "Soglia metrica",
"host_status": "L'host sale o scende",
"health_check": "modifiche al controllo sanitario",
"schedule": "Secondo un programma",
"docker_event": "evento contenitore",
"internal_event": "Evento Termix",
"webhook": "Webhook in entrata"
},
"stepTypes": {
"notify": "Invia una notifica",
"http": "Chiamare un medico",
"run_snippet": "Esegui uno snippet",
"run_command": "Eseguire un comando",
"docker": "Controllare un contenitore",
"tunnel": "Controllare un tunnel",
"wol": "Svegliare un ospite",
"wait": "Aspettare",
"set_var": "Imposta una variabile",
"if": "Se / altrimenti",
"run_automation": "Esegui un'altra automazione",
"stop": "Fermare"
},
"statuses": {
"running": "Corsa",
"success": "Successo",
"failed": "Fallito",
"timeout": "Tempo scaduto",
"skipped": "Saltato",
"cancelled": "Annullato"
},
"fields": {
"host": "Ospite",
"metric": "metrico",
"mount": "Punto di appoggio",
"operator": "È",
"value": "Valore",
"forSeconds": "Sostenuto per (secondi)",
"cooldown": "Tempo di raffreddamento (minuti)",
"cron": "espressione Cron",
"intervalSeconds": "Ogni (secondi)",
"timezone": "Fuso orario",
"timezoneServer": "Ora del server",
"timezoneHint": "In questa zona vengono letti i tempi di Cron. Non si applica alle programmazioni a intervalli.",
"container": "Contenitore",
"command": "Comando",
"url": "URL",
"method": "Metodo",
"body": "Corpo",
"channels": "Canali",
"title": "Titolo",
"message": "Messaggio",
"snippet": "Frammento",
"seconds": "secondi",
"conditionLeftPlaceholder": "Valore da controllare",
"conditionRightPlaceholder": "Confronta con",
"variableName": "Nome variabile",
"variableValue": "Valore",
"elevated": "Esegui con sudo",
"onError": "Se questo non funziona",
"allowPrivateNetwork": "Consenti indirizzi di rete locale",
"allowPrivateNetworkHint": "Necessario per un servizio self-hosted sulla tua LAN. Disattivato per impostazione predefinita per impedire richieste a indirizzi interni."
},
"onError": {
"stop": "Ferma la corsa",
"continue": "Proseguire"
}
}
}
},
"nav": {
"automations": "Automazioni"
},
"onboarding": {
"feature_automations": "Automazioni",
"feature_automations_desc": "Esegui automaticamente i passaggi quando si verifica un evento e ricevi una notifica."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "自動化",
"tabAutomations": "自動化",
"tabRuns": "走る",
"tabChannels": "チャンネル",
"createAutomation": "新しい自動化",
"backToAutomations": "自動化に戻る",
"createTitle": "新しい自動化",
"editTitle": "編集自動化",
"empty": "自動化はまだ設定されていません。指標、スケジュール、またはイベントに応じて自動化を設定してください。",
"emptyRuns": "まだ得点はありません。",
"emptyChannels": "まだチャンネルがありません。通知を送信するチャンネルを追加してください。",
"noChannelsHint": "チャンネルがまだ設定されていません。チャンネルタブで追加してください。",
"addChannel": "チャンネルを追加",
"testChannel": "テスト",
"channelTestSent": "テスト通知が送信されました",
"channelTestFailed": "テスト通知が失敗しました",
"channelDeleted": "チャンネルが削除されました",
"deleteChannelConfirm": "このチャンネルを削除しますか?このチャンネルを使用している自動化機能は通知を停止します。",
"nameLabel": "名前",
"namePlaceholder": "例:/data がいっぱいになったらクリーンアップする",
"nameRequired": "名前は必須です",
"descriptionLabel": "説明",
"descriptionPlaceholder": "オプションの説明",
"enabledLabel": "有効",
"concurrencyLabel": "既に実行中の場合",
"concurrencyHint": "前回の実行が終了する前に、これが再び発生した場合の対処法。",
"concurrency": {
"skip": "新しいランをスキップ",
"queue": "待ってから追いかける",
"allow": "両方を同時に実行する"
},
"save": "保存",
"created": "自動化が作成しました",
"updated": "自動化が保存されました",
"deleted": "自動化が削除されました",
"deleteConfirm": "この自動化を削除すると、実行履歴も削除されます。",
"runNow": "今すぐ実行",
"testRun": "試運転",
"testRunHint": "すべてのステップを実行しますが、Termix以外のものには一切触れません。",
"runStarted": "ランニング開始",
"runFinished": "実行完了: {{status}}",
"trigger": "トリガー",
"steps": "手順",
"addStep": "ステップを追加する",
"removeStep": "削除手順",
"moveUp": "上へ進む",
"moveDown": "下へ移動",
"noSteps": "まだ手順がありません。何が起こるべきかを記述する手順を追加してください。",
"webhookTokenTitle": "Webhook URL",
"webhookTokenDescription": "これは一度しか表示されません。今すぐ保存してください。後で取り出すことはできません。",
"copy": "コピー",
"copied": "コピーしました",
"viewJson": "JSON",
"viewBuilder": "ビルダー",
"viewGraph": "グラフ",
"lastRun": "最後の走行",
"never": "一度もない",
"duration": "{{seconds}}s",
"stepOutput": "出力",
"stepError": "エラー",
"dryRunBadge": "試運転",
"hostSelector": {
"trigger": "トリガーホスト",
"host": "特定の宿主",
"fleet": "艦隊",
"all": "すべてのホスト"
},
"triggerKinds": {
"metric_threshold": "メトリックしきい値",
"host_status": "ホストが起動または停止する",
"health_check": "健康チェックの変更点",
"schedule": "スケジュール通りに",
"docker_event": "コンテナイベント",
"internal_event": "ターミックスイベント",
"webhook": "受信ウェブフック"
},
"stepTypes": {
"notify": "通知を送信する",
"http": "URLを呼び出す",
"run_snippet": "スニペットを実行する",
"run_command": "コマンドを実行する",
"docker": "コンテナを制御する",
"tunnel": "トンネルを制御する",
"wol": "ホストを起こす",
"wait": "待って",
"set_var": "変数を設定する",
"if": "もし/そうでなければ",
"run_automation": "別の自動化を実行する",
"stop": "停止"
},
"statuses": {
"running": "ランニング",
"success": "成功",
"failed": "失敗した",
"timeout": "タイムアウト",
"skipped": "スキップしました",
"cancelled": "キャンセル"
},
"fields": {
"host": "ホスト",
"metric": "メトリック",
"mount": "取り付けポイント",
"operator": "は",
"value": "価値",
"forSeconds": "(秒)の間持続",
"cooldown": "クールダウン時間(分)",
"cron": "Cron発現",
"intervalSeconds": "(秒)ごとに",
"timezone": "タイムゾーン",
"timezoneServer": "サーバー時間",
"timezoneHint": "このゾーンでは、cronの実行時間が読み取られます。インターバルスケジュールには適用されません。",
"container": "容器",
"command": "指示",
"url": "URL",
"method": "方法",
"body": "体",
"channels": "チャンネル",
"title": "タイトル",
"message": "メッセージ",
"snippet": "抜粋",
"seconds": "秒",
"conditionLeftPlaceholder": "チェックすべき値",
"conditionRightPlaceholder": "比較対象",
"variableName": "変数名",
"variableValue": "価値",
"elevated": "sudo を付けて実行",
"onError": "これが失敗した場合",
"allowPrivateNetwork": "ローカルネットワークアドレスを許可する",
"allowPrivateNetworkHint": "LAN上でセルフホスト型サービスを実行する場合に必要です。内部アドレスへのリクエストを防ぐため、デフォルトでは無効になっています。"
},
"onError": {
"stop": "走るのをやめろ",
"continue": "続ける"
}
}
}
},
"nav": {
"automations": "自動化"
},
"onboarding": {
"feature_automations": "自動化",
"feature_automations_desc": "何かが発生した際に自動的に手順を実行し、通知を受け取ります。"
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "자동화",
"tabAutomations": "자동화",
"tabRuns": "실행",
"tabChannels": "채널",
"createAutomation": "새로운 자동화",
"backToAutomations": "자동화 목록으로 돌아가기",
"createTitle": "새로운 자동화",
"editTitle": "편집 자동화",
"empty": "아직 자동화가 설정되어 있지 않습니다. 지표, 일정 또는 이벤트에 반응하는 자동화를 생성하세요.",
"emptyRuns": "아직 실행된 적이 없습니다.",
"emptyChannels": "아직 채널이 없습니다. 알림을 보낼 채널을 추가하세요.",
"noChannelsHint": "아직 채널이 설정되지 않았습니다. 채널 탭에서 채널을 추가하세요.",
"addChannel": "채널 추가",
"testChannel": "시험",
"channelTestSent": "테스트 알림이 전송되었습니다.",
"channelTestFailed": "테스트 알림 실패",
"channelDeleted": "채널이 삭제되었습니다",
"deleteChannelConfirm": "이 채널을 삭제하시겠습니까? 그러면 해당 채널을 사용하는 자동화 기능이 더 이상 알림을 보내지 않게 됩니다.",
"nameLabel": "이름",
"namePlaceholder": "예: /data 폴더가 가득 차면 정리합니다.",
"nameRequired": "이름은 필수 입력 사항입니다.",
"descriptionLabel": "설명",
"descriptionPlaceholder": "선택적 설명",
"enabledLabel": "활성화됨",
"concurrencyLabel": "이미 실행 중인 경우",
"concurrencyHint": "마지막 실행이 완료되기 전에 이 문제가 다시 발생할 경우 어떻게 해야 할까요?",
"concurrency": {
"skip": "새로운 실행을 건너뛰세요",
"queue": "기다렸다가 실행하세요",
"allow": "두 가지를 동시에 실행하세요"
},
"save": "구하다",
"created": "자동화 생성됨",
"updated": "자동화 저장됨",
"deleted": "자동화 삭제됨",
"deleteConfirm": "이 자동화를 삭제하시겠습니까? 실행 기록도 함께 삭제됩니다.",
"runNow": "지금 실행하세요",
"testRun": "테스트 실행",
"testRunHint": "모든 단계를 실행하지만 Termix 외부의 어떤 것도 건드리지 않습니다.",
"runStarted": "실행 시작됨",
"runFinished": "실행 완료: {{status}}",
"trigger": "방아쇠",
"steps": "단계",
"addStep": "단계 추가",
"removeStep": "제거 단계",
"moveUp": "위로 이동",
"moveDown": "아래로 이동",
"noSteps": "아직 단계가 없습니다. 어떤 일이 일어나야 하는지 설명하는 단계를 추가하세요.",
"webhookTokenTitle": "웹훅 URL",
"webhookTokenDescription": "이 내용은 한 번만 표시됩니다. 지금 저장하세요. 나중에 불러올 수 없습니다.",
"copy": "복사",
"copied": "복사됨",
"viewJson": "JSON",
"viewBuilder": "건축업자",
"viewGraph": "그래프",
"lastRun": "마지막 실행",
"never": "절대",
"duration": "{{seconds}}s",
"stepOutput": "산출",
"stepError": "오류",
"dryRunBadge": "테스트 실행",
"hostSelector": {
"trigger": "트리거링 호스트",
"host": "특정 호스트",
"fleet": "함대",
"all": "모든 호스트"
},
"triggerKinds": {
"metric_threshold": "측정 임계값",
"host_status": "호스트가 올라가거나 내려갑니다.",
"health_check": "건강 검진 변경 사항",
"schedule": "정해진 일정에 따라",
"docker_event": "컨테이너 이벤트",
"internal_event": "터믹스 이벤트",
"webhook": "수신 웹훅"
},
"stepTypes": {
"notify": "알림 보내기",
"http": "URL을 호출합니다",
"run_snippet": "코드 조각을 실행하세요",
"run_command": "명령어를 실행하세요",
"docker": "컨테이너를 제어하세요",
"tunnel": "터널을 제어하세요",
"wol": "호스트를 깨우세요",
"wait": "기다리다",
"set_var": "변수를 설정하세요",
"if": "만약 / 그렇지 않다면",
"run_automation": "다른 자동화를 실행합니다",
"stop": "멈추다"
},
"statuses": {
"running": "달리기",
"success": "성공",
"failed": "실패한",
"timeout": "시간 초과됨",
"skipped": "건너뛰었습니다",
"cancelled": "취소"
},
"fields": {
"host": "주인",
"metric": "미터법",
"mount": "마운트 포인트",
"operator": "~이다",
"value": "값",
"forSeconds": "(초) 동안 지속됨",
"cooldown": "재사용 대기 시간(분)",
"cron": "크론 발현",
"intervalSeconds": "매 (초)",
"timezone": "시간대",
"timezoneServer": "서버 시간",
"timezoneHint": "크론 시간은 이 영역에서 읽습니다. 간격 스케줄에는 적용되지 않습니다.",
"container": "컨테이너",
"command": "명령",
"url": "URL",
"method": "방법",
"body": "몸",
"channels": "채널",
"title": "제목",
"message": "메시지",
"snippet": "단편",
"seconds": "초",
"conditionLeftPlaceholder": "확인할 값",
"conditionRightPlaceholder": "~와 비교하세요",
"variableName": "변수 이름",
"variableValue": "값",
"elevated": "sudo를 사용하여 실행하세요",
"onError": "이것이 실패하면",
"allowPrivateNetwork": "로컬 네트워크 주소를 허용합니다",
"allowPrivateNetworkHint": "LAN에서 자체 호스팅 서비스를 운영하는 데 필요합니다. 내부 주소로의 요청을 방지하기 위해 기본적으로 비활성화되어 있습니다."
},
"onError": {
"stop": "달리기를 멈춰라",
"continue": "계속하세요"
}
}
}
},
"nav": {
"automations": "자동화"
},
"onboarding": {
"feature_automations": "자동화",
"feature_automations_desc": "어떤 상황이 발생하면 자동으로 단계를 실행하고 알림을 받으세요."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatiseringen",
"tabAutomations": "Automatiseringen",
"tabRuns": "Loopt",
"tabChannels": "Kanalen",
"createAutomation": "Nieuwe automatisering",
"backToAutomations": "Terug naar automatiseringen",
"createTitle": "Nieuwe automatisering",
"editTitle": "Automatisering bewerken",
"empty": "Nog geen automatiseringen. Maak er een aan die reageert op een meetwaarde, een planning of een gebeurtenis.",
"emptyRuns": "Nog geen runs.",
"emptyChannels": "Nog geen kanalen. Voeg er een toe om meldingen ergens naartoe te sturen.",
"noChannelsHint": "Er zijn nog geen kanalen ingesteld. Voeg er een toe via het tabblad Kanalen.",
"addChannel": "Kanaal toevoegen",
"testChannel": "Test",
"channelTestSent": "Testmelding verzonden",
"channelTestFailed": "Testmelding mislukt",
"channelDeleted": "Kanaal verwijderd",
"deleteChannelConfirm": "Als u dit kanaal wilt verwijderen, zullen automatiseringsprocessen die ervan gebruikmaken geen meldingen meer versturen.",
"nameLabel": "Naam",
"namePlaceholder": "Bijvoorbeeld: maak /data leeg wanneer het vol is.",
"nameRequired": "Naam is verplicht",
"descriptionLabel": "Beschrijving",
"descriptionPlaceholder": "Optionele beschrijving",
"enabledLabel": "Ingeschakeld",
"concurrencyLabel": "Als het al draait",
"concurrencyHint": "Wat te doen als dit opnieuw wordt geactiveerd voordat de laatste run is voltooid?",
"concurrency": {
"skip": "Sla de nieuwe run over",
"queue": "Wacht en ren erachteraan.",
"allow": "Voer ze allebei tegelijk uit."
},
"save": "Redden",
"created": "Automatisering gecreëerd",
"updated": "Automatisering opgeslagen",
"deleted": "Automatisering verwijderd",
"deleteConfirm": "Als je deze automatisering verwijdert, wordt ook de uitvoeringsgeschiedenis ervan verwijderd.",
"runNow": "Ren nu",
"testRun": "Testrun",
"testRunHint": "Voert elke stap uit, maar raakt niets buiten Termix aan.",
"runStarted": "De run is gestart.",
"runFinished": "Run voltooid: {{status}}",
"trigger": "Trekker",
"steps": "Stappen",
"addStep": "Voeg stap toe",
"removeStep": "Verwijder stap",
"moveUp": "Omhoog gaan",
"moveDown": "Ga naar beneden",
"noSteps": "Nog geen stappen. Voeg er een toe om aan te geven wat er moet gebeuren.",
"webhookTokenTitle": "Webhook-URL",
"webhookTokenDescription": "Dit wordt eenmalig weergegeven. Sla het nu op, het kan later niet meer worden opgevraagd.",
"copy": "Kopiëren",
"copied": "Gekopieerd",
"viewJson": "JSON",
"viewBuilder": "Bouwer",
"viewGraph": "Grafiek",
"lastRun": "Laatste run",
"never": "Nooit",
"duration": "{{seconds}}s",
"stepOutput": "Uitvoer",
"stepError": "Fout",
"dryRunBadge": "Testrun",
"hostSelector": {
"trigger": "Activerende host",
"host": "Specifieke gastheer",
"fleet": "Vloot",
"all": "Alle hosts"
},
"triggerKinds": {
"metric_threshold": "Metrische drempelwaarde",
"host_status": "De host gaat omhoog of omlaag.",
"health_check": "Wijzigingen in de gezondheidscontrole",
"schedule": "Volgens een schema",
"docker_event": "Container-gebeurtenis",
"internal_event": "Termix-evenement",
"webhook": "Inkomende webhook"
},
"stepTypes": {
"notify": "Een melding verzenden",
"http": "Roep een URL aan",
"run_snippet": "Voer een codefragment uit",
"run_command": "Voer een commando uit",
"docker": "Een container besturen",
"tunnel": "Beheer een tunnel",
"wol": "Maak een gastheer wakker",
"wait": "Wachten",
"set_var": "Een variabele instellen",
"if": "Indien / anders",
"run_automation": "Voer nog een automatisering uit",
"stop": "Stop"
},
"statuses": {
"running": "Rennen",
"success": "Succes",
"failed": "Mislukt",
"timeout": "De tijd is verlopen.",
"skipped": "Overgeslagen",
"cancelled": "Geannuleerd"
},
"fields": {
"host": "Gastheer",
"metric": "Metrisch",
"mount": "Bergpunt",
"operator": "Is",
"value": "Waarde",
"forSeconds": "Aangehouden gedurende (seconden)",
"cooldown": "Afkoeltijd (minuten)",
"cron": "Cron-expressie",
"intervalSeconds": "Elke (seconden)",
"timezone": "Tijdzone",
"timezoneServer": "Servertijd",
"timezoneHint": "In deze zone worden de cron-tijden gelezen. Dit geldt niet voor intervalschema's.",
"container": "Container",
"command": "Commando",
"url": "URL",
"method": "Methode",
"body": "Lichaam",
"channels": "Kanalen",
"title": "Titel",
"message": "Bericht",
"snippet": "Fragment",
"seconds": "Seconden",
"conditionLeftPlaceholder": "Waarde om te controleren",
"conditionRightPlaceholder": "Vergelijk met",
"variableName": "Variabelenaam",
"variableValue": "Waarde",
"elevated": "Uitvoeren met sudo",
"onError": "Als dit mislukt",
"allowPrivateNetwork": "Sta lokale netwerkadressen toe.",
"allowPrivateNetworkHint": "Nodig voor een zelfgehoste service op uw LAN. Standaard uitgeschakeld om verzoeken naar interne adressen te voorkomen."
},
"onError": {
"stop": "Stop de loop",
"continue": "Ga zo door"
}
}
}
},
"nav": {
"automations": "Automatiseringen"
},
"onboarding": {
"feature_automations": "Automatiseringen",
"feature_automations_desc": "Voer stappen automatisch uit wanneer er iets gebeurt en ontvang meldingen."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatiseringer",
"tabAutomations": "Automatiseringer",
"tabRuns": "Løper",
"tabChannels": "Kanaler",
"createAutomation": "Ny automatisering",
"backToAutomations": "Tilbake til automatiseringer",
"createTitle": "Ny automatisering",
"editTitle": "Rediger automatisering",
"empty": "Ingen automatiseringer ennå. Opprett en for å reagere på en beregning, en tidsplan eller en hendelse.",
"emptyRuns": "Ingen løp ennå.",
"emptyChannels": "Ingen kanaler ennå. Legg til en for å sende varsler et sted.",
"noChannelsHint": "Ingen kanaler er konfigurert ennå. Legg til en i Kanaler-fanen.",
"addChannel": "Legg til kanal",
"testChannel": "Test",
"channelTestSent": "Testvarsel sendt",
"channelTestFailed": "Testvarsel mislyktes",
"channelDeleted": "Kanalen slettet",
"deleteChannelConfirm": "Vil du slette denne kanalen? Automatiseringer som bruker den, vil slutte å varsle.",
"nameLabel": "Navn",
"namePlaceholder": "f.eks. Rydd opp i /data når den fylles",
"nameRequired": "Navn er obligatorisk",
"descriptionLabel": "Beskrivelse",
"descriptionPlaceholder": "Valgfri beskrivelse",
"enabledLabel": "Aktivert",
"concurrencyLabel": "Hvis den allerede kjører",
"concurrencyHint": "Hva skal man gjøre når dette utløses igjen før siste kjøring er ferdig?",
"concurrency": {
"skip": "Hopp over den nye kjøringen",
"queue": "Vent og løp etter",
"allow": "Kjør begge samtidig"
},
"save": "Spare",
"created": "Automatisering opprettet",
"updated": "Automatisering lagret",
"deleted": "Automatisering slettet",
"deleteConfirm": "Vil du slette denne automatiseringen? Kjørehistorikken slettes også.",
"runNow": "Løp nå",
"testRun": "Testkjøring",
"testRunHint": "Kjører hvert trinn, men berører ikke noe utenfor Termix.",
"runStarted": "Løpet startet",
"runFinished": "Kjøring fullført: {{status}}",
"trigger": "Avtrekker",
"steps": "Trinn",
"addStep": "Legg til trinn",
"removeStep": "Fjern trinn",
"moveUp": "Flytt opp",
"moveDown": "Flytt ned",
"noSteps": "Ingen trinn ennå. Legg til ett for å si hva som skal skje.",
"webhookTokenTitle": "Webhook-URL",
"webhookTokenDescription": "Dette vises én gang. Lagre det nå, det kan ikke hentes frem senere.",
"copy": "Kopiere",
"copied": "Kopiert",
"viewJson": "JSON",
"viewBuilder": "Bygger",
"viewGraph": "Kurve",
"lastRun": "Siste løp",
"never": "Aldri",
"duration": "{{seconds}}s",
"stepOutput": "Produksjon",
"stepError": "Feil",
"dryRunBadge": "Testkjøring",
"hostSelector": {
"trigger": "Utløsende vert",
"host": "Spesifikk vert",
"fleet": "Flåte",
"all": "Alle verter"
},
"triggerKinds": {
"metric_threshold": "Metrisk terskel",
"host_status": "Verten går opp eller ned",
"health_check": "Endringer i helsesjekken",
"schedule": "På en tidsplan",
"docker_event": "Containerhendelse",
"internal_event": "Termix-arrangementet",
"webhook": "Innkommende webhook"
},
"stepTypes": {
"notify": "Send et varsel",
"http": "Kall en URL",
"run_snippet": "Kjør et utdrag",
"run_command": "Kjør en kommando",
"docker": "Kontroller en container",
"tunnel": "Kontroller en tunnel",
"wol": "Vekk en vert",
"wait": "Vente",
"set_var": "Angi en variabel",
"if": "Hvis / ellers",
"run_automation": "Kjør en annen automatisering",
"stop": "Stoppe"
},
"statuses": {
"running": "Løping",
"success": "Suksess",
"failed": "Mislyktes",
"timeout": "Tidsavbrutt",
"skipped": "Hoppet over",
"cancelled": "Avlyst"
},
"fields": {
"host": "Vert",
"metric": "Metrisk",
"mount": "Monteringspunkt",
"operator": "Er",
"value": "Verdi",
"forSeconds": "Vedvarende i (sekunder)",
"cooldown": "Nedkjølingstid (minutter)",
"cron": "Cron-uttrykk",
"intervalSeconds": "Hvert (sekund)",
"timezone": "Tidssone",
"timezoneServer": "Servertid",
"timezoneHint": "Cron-tider leses i denne sonen. Gjelder ikke for intervallplaner.",
"container": "Container",
"command": "Kommando",
"url": "URL-adresse",
"method": "Metode",
"body": "Kropp",
"channels": "Kanaler",
"title": "Tittel",
"message": "Beskjed",
"snippet": "Utdrag",
"seconds": "Sekunder",
"conditionLeftPlaceholder": "Verdi å sjekke",
"conditionRightPlaceholder": "Sammenlign mot",
"variableName": "Variabelnavn",
"variableValue": "Verdi",
"elevated": "Kjør med sudo",
"onError": "Hvis dette mislykkes",
"allowPrivateNetwork": "Tillat lokale nettverksadresser",
"allowPrivateNetworkHint": "Nødvendig for en selvhostet tjeneste på LAN-et ditt. Av som standard for å forhindre forespørsler til interne adresser."
},
"onError": {
"stop": "Stopp løpet",
"continue": "Fortsett"
}
}
}
},
"nav": {
"automations": "Automatiseringer"
},
"onboarding": {
"feature_automations": "Automatiseringer",
"feature_automations_desc": "Kjør trinnene automatisk når noe skjer, og bli varslet."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatyzacje",
"tabAutomations": "Automatyzacje",
"tabRuns": "Uruchomienia",
"tabChannels": "Kanały",
"createAutomation": "Nowa automatyzacja",
"backToAutomations": "Powrót do automatyzacji",
"createTitle": "Nowa automatyzacja",
"editTitle": "Edytuj automatyzację",
"empty": "Brak jeszcze automatyzacji. Utwórz automatyzację, która będzie reagować na metrykę, harmonogram lub zdarzenie.",
"emptyRuns": "Brak uruchomień.",
"emptyChannels": "Brak kanałów. Dodaj jeden, aby wysyłać powiadomienia.",
"noChannelsHint": "Nie skonfigurowano jeszcze żadnych kanałów. Dodaj jeden w zakładce Kanały.",
"addChannel": "Dodaj kanał",
"testChannel": "Test",
"channelTestSent": "Wysłano powiadomienie testowe",
"channelTestFailed": "Powiadomienie testowe nie powiodło się",
"channelDeleted": "Kanał usunięty",
"deleteChannelConfirm": "Usunąć ten kanał? Automatyzacje z niego korzystające przestaną wysyłać powiadomienia.",
"nameLabel": "Nazwa",
"namePlaceholder": "np. Wyczyść /data, gdy się zapełni",
"nameRequired": "Nazwa jest wymagana",
"descriptionLabel": "Opis",
"descriptionPlaceholder": "Opis opcjonalny",
"enabledLabel": "Włączona",
"concurrencyLabel": "Jeśli już działa",
"concurrencyHint": "Co zrobić, gdy zostanie wyzwolona ponownie przed zakończeniem poprzedniego uruchomienia.",
"concurrency": {
"skip": "Pomiń nowe uruchomienie",
"queue": "Poczekaj i uruchom po zakończeniu",
"allow": "Uruchom oba naraz"
},
"save": "Zapisz",
"created": "Utworzono automatyzację",
"updated": "Zapisano automatyzację",
"deleted": "Usunięto automatyzację",
"deleteConfirm": "Usunąć tę automatyzację? Jej historia uruchomień również zostanie usunięta.",
"runNow": "Uruchom teraz",
"testRun": "Uruchomienie próbne",
"testRunHint": "Wykonuje każdy krok, ale nie zmienia niczego poza Termixem.",
"runStarted": "Uruchomiono",
"runFinished": "Zakończono: {{status}}",
"trigger": "Wyzwalacz",
"steps": "Kroki",
"addStep": "Dodaj krok",
"removeStep": "Usuń krok",
"moveUp": "W górę",
"moveDown": "W dół",
"noSteps": "Brak kroków. Dodaj jeden, aby określić, co powinno się stać.",
"webhookTokenTitle": "Adres URL webhooka",
"webhookTokenDescription": "Pokazywany tylko raz. Zapisz go teraz, później nie da się go odzyskać.",
"copy": "Kopiuj",
"copied": "Skopiowano",
"viewJson": "JSON",
"viewBuilder": "Kreator",
"viewGraph": "Graf",
"lastRun": "Ostatnie uruchomienie",
"never": "Nigdy",
"duration": "{{seconds}}s",
"stepOutput": "Wyjście",
"stepError": "Błąd",
"dryRunBadge": "Uruchomienie próbne",
"hostSelector": {
"trigger": "Host wyzwalający",
"host": "Konkretny host",
"fleet": "Flota",
"all": "Wszystkie hosty"
},
"triggerKinds": {
"metric_threshold": "Próg metryki",
"host_status": "Host wstaje lub pada",
"health_check": "Zmiana kontroli stanu",
"schedule": "Zgodnie z harmonogramem",
"docker_event": "Zdarzenie kontenera",
"internal_event": "Zdarzenie Termixa",
"webhook": "Przychodzący webhook"
},
"stepTypes": {
"notify": "Wyślij powiadomienie",
"http": "Wywołaj adres URL",
"run_snippet": "Uruchom snippet",
"run_command": "Uruchom polecenie",
"docker": "Kontroluj kontener",
"tunnel": "Kontroluj tunel",
"wol": "Obudź host",
"wait": "Czekaj",
"set_var": "Ustaw zmienną",
"if": "Jeśli / w przeciwnym razie",
"run_automation": "Uruchom inną automatyzację",
"stop": "Zatrzymaj"
},
"statuses": {
"running": "Działa",
"success": "Powodzenie",
"failed": "Niepowodzenie",
"timeout": "Przekroczono limit czasu",
"skipped": "Pominięto",
"cancelled": "Anulowano"
},
"fields": {
"host": "Host",
"metric": "Metryka",
"mount": "Punkt montowania",
"operator": "Jest",
"value": "Wartość",
"forSeconds": "Utrzymywany przez (sekundy)",
"cooldown": "Czas wyciszenia (minuty)",
"cron": "Wyrażenie cron",
"intervalSeconds": "Co (sekundy)",
"timezone": "Strefa czasowa",
"timezoneServer": "Czas serwera",
"timezoneHint": "Czasy cron są odczytywane w tej strefie. Nie dotyczy harmonogramów interwałowych.",
"container": "Kontener",
"command": "Polecenie",
"url": "Adres URL",
"method": "Metoda",
"body": "Treść",
"channels": "Kanały",
"title": "Tytuł",
"message": "Wiadomość",
"snippet": "Snippet",
"seconds": "Sekundy",
"conditionLeftPlaceholder": "Wartość do sprawdzenia",
"conditionRightPlaceholder": "Porównaj z",
"variableName": "Nazwa zmiennej",
"variableValue": "Wartość",
"elevated": "Uruchom z sudo",
"onError": "Jeśli to się nie powiedzie",
"allowPrivateNetwork": "Zezwalaj na lokalne adresy sieciowe",
"allowPrivateNetworkHint": "Wymagane dla własnej usługi w sieci LAN. Domyślnie wyłączone, aby zapobiec żądaniom do adresów wewnętrznych."
},
"onError": {
"stop": "Zatrzymaj uruchomienie",
"continue": "Kontynuuj"
}
}
}
},
"nav": {
"automations": "Automatyzacje"
},
"onboarding": {
"feature_automations": "Automatyzacje",
"feature_automations_desc": "Uruchamiaj kroki automatycznie, gdy coś się wydarzy, i otrzymuj powiadomienia."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automações",
"tabAutomations": "Automações",
"tabRuns": "Corridas",
"tabChannels": "Canais",
"createAutomation": "Nova Automação",
"backToAutomations": "Voltar para Automações",
"createTitle": "Nova Automação",
"editTitle": "Editar Automação",
"empty": "Ainda não há automações. Crie uma para reagir a uma métrica, a um agendamento ou a um evento.",
"emptyRuns": "Ainda não houve nenhuma corrida.",
"emptyChannels": "Ainda não há canais. Adicione um para enviar notificações para algum lugar.",
"noChannelsHint": "Nenhum canal configurado ainda. Adicione um na aba Canais.",
"addChannel": "Adicionar canal",
"testChannel": "Teste",
"channelTestSent": "Notificação de teste enviada",
"channelTestFailed": "Falha na notificação de teste",
"channelDeleted": "Canal excluído",
"deleteChannelConfirm": "Excluir este canal? As automações que o utilizam deixarão de enviar notificações.",
"nameLabel": "Nome",
"namePlaceholder": "Por exemplo, limpe a pasta /data quando ela estiver cheia.",
"nameRequired": "O nome é obrigatório.",
"descriptionLabel": "Descrição",
"descriptionPlaceholder": "Descrição opcional",
"enabledLabel": "Habilitado",
"concurrencyLabel": "Se já estiver em execução",
"concurrencyHint": "O que fazer quando isso for acionado novamente antes da última execução terminar?",
"concurrency": {
"skip": "Pule a nova corrida",
"queue": "Espere e corra atrás.",
"allow": "Execute ambos simultaneamente"
},
"save": "Salvar",
"created": "Automação criada",
"updated": "Automação economizou",
"deleted": "Automação excluída",
"deleteConfirm": "Excluir esta automação? O histórico de execução dela também será excluído.",
"runNow": "Corra agora",
"testRun": "Teste de execução",
"testRunHint": "Executa cada etapa, mas não toca em nada fora do Termix.",
"runStarted": "A corrida começou.",
"runFinished": "Execução concluída: {{status}}",
"trigger": "Acionar",
"steps": "Passos",
"addStep": "Adicionar etapa",
"removeStep": "Remover etapa",
"moveUp": "Suba",
"moveDown": "Desça",
"noSteps": "Ainda não há etapas. Adicione uma para descrever o que deve acontecer.",
"webhookTokenTitle": "URL do Webhook",
"webhookTokenDescription": "Esta mensagem será exibida apenas uma vez. Salve-a agora, pois não poderá ser recuperada posteriormente.",
"copy": "Cópia",
"copied": "Copiado",
"viewJson": "JSON",
"viewBuilder": "Construtor",
"viewGraph": "Gráfico",
"lastRun": "Última corrida",
"never": "Nunca",
"duration": "{{seconds}}s",
"stepOutput": "Saída",
"stepError": "Erro",
"dryRunBadge": "Teste de execução",
"hostSelector": {
"trigger": "Host que desencadeia o ataque",
"host": "hospedeiro específico",
"fleet": "Frota",
"all": "Todos os anfitriões"
},
"triggerKinds": {
"metric_threshold": "Limiar métrico",
"host_status": "O host sobe ou desce.",
"health_check": "Alterações na verificação de saúde",
"schedule": "De acordo com um cronograma",
"docker_event": "Evento de contêiner",
"internal_event": "Evento Termix",
"webhook": "Webhook de entrada"
},
"stepTypes": {
"notify": "Enviar notificação",
"http": "Chamar um URL",
"run_snippet": "Executar um trecho de código",
"run_command": "Executar um comando",
"docker": "Controle um contêiner",
"tunnel": "Controle um túnel",
"wol": "Acorde um anfitrião",
"wait": "Espere",
"set_var": "Defina uma variável",
"if": "Se/caso contrário",
"run_automation": "Execute outra automação",
"stop": "Parar"
},
"statuses": {
"running": "Correndo",
"success": "Sucesso",
"failed": "Fracassado",
"timeout": "Tempo limite excedido",
"skipped": "Ignorado",
"cancelled": "Cancelado"
},
"fields": {
"host": "Hospedar",
"metric": "Métrica",
"mount": "Ponto de montagem",
"operator": "É",
"value": "Valor",
"forSeconds": "Sustentado por (segundos)",
"cooldown": "Tempo de resfriamento (minutos)",
"cron": "Expressão de Cron",
"intervalSeconds": "A cada (segundos)",
"timezone": "Fuso horário",
"timezoneServer": "Hora do servidor",
"timezoneHint": "Os horários cron são lidos nesta zona. Não se aplica a agendamentos por intervalo.",
"container": "Recipiente",
"command": "Comando",
"url": "URL",
"method": "Método",
"body": "Corpo",
"channels": "Canais",
"title": "Título",
"message": "Mensagem",
"snippet": "Trecho",
"seconds": "Segundos",
"conditionLeftPlaceholder": "Valor a verificar",
"conditionRightPlaceholder": "Compare com",
"variableName": "Nome da variável",
"variableValue": "Valor",
"elevated": "Executar com sudo",
"onError": "Se isso falhar",
"allowPrivateNetwork": "Permitir endereços de rede local",
"allowPrivateNetworkHint": "Necessário para um serviço autohospedado em sua rede local (LAN). Desativado por padrão para evitar solicitações a endereços internos."
},
"onError": {
"stop": "Pare a corrida",
"continue": "Continuar"
}
}
}
},
"nav": {
"automations": "Automações"
},
"onboarding": {
"feature_automations": "Automações",
"feature_automations_desc": "Execute etapas automaticamente quando algo acontecer e receba notificações."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automações",
"tabAutomations": "Automações",
"tabRuns": "Corridas",
"tabChannels": "Canais",
"createAutomation": "Nova Automação",
"backToAutomations": "Voltar para Automações",
"createTitle": "Nova Automação",
"editTitle": "Editar Automação",
"empty": "Ainda não há automatizações. Crie uma para reagir a uma métrica, a um agendamento ou a um evento.",
"emptyRuns": "Ainda não houve qualquer corrida.",
"emptyChannels": "Ainda não há canais. Adicione um para enviar notificações para algum lugar.",
"noChannelsHint": "Nenhum canal configurado ainda. Adicione um no separador Canais.",
"addChannel": "Adicionar canal",
"testChannel": "Teste",
"channelTestSent": "Notificação de teste enviada",
"channelTestFailed": "Falha na notificação de teste",
"channelDeleted": "Canal excluído",
"deleteChannelConfirm": "Apagar este canal? As automatizações que o utilizam deixarão de enviar notificações.",
"nameLabel": "Nome",
"namePlaceholder": "Por exemplo, limpe a pasta /data quando esta estiver cheia.",
"nameRequired": "O nome é obrigatório.",
"descriptionLabel": "Descrição",
"descriptionPlaceholder": "Descrição opcional",
"enabledLabel": "Habilitado",
"concurrencyLabel": "Se já estiver em execução",
"concurrencyHint": "O que fazer quando este for novamente acionado antes da última execução terminar?",
"concurrency": {
"skip": "Salte a nova corrida",
"queue": "Espere e corra atrás.",
"allow": "Execute ambos em simultâneo"
},
"save": "Guardar",
"created": "Automação criada",
"updated": "A automação economizou",
"deleted": "Automação excluída",
"deleteConfirm": "Apagar esta automação? O histórico de execução da mesma também será eliminado.",
"runNow": "Corra agora",
"testRun": "Teste de execução",
"testRunHint": "Executa cada passo, mas não toca em nada fora do Termix.",
"runStarted": "A corrida começou.",
"runFinished": "Execução concluída: {{status}}",
"trigger": "Gatilho",
"steps": "Passos",
"addStep": "Adicionar etapa",
"removeStep": "Remover etapa",
"moveUp": "Suba",
"moveDown": "Desça",
"noSteps": "Ainda não há etapas. Adicione uma para descrever o que deve acontecer.",
"webhookTokenTitle": "URL do Webhook",
"webhookTokenDescription": "Esta mensagem será apresentada apenas uma vez. Guarde-a agora, pois não poderá ser recuperada posteriormente.",
"copy": "Cópia",
"copied": "Copiado",
"viewJson": "JSON",
"viewBuilder": "Construtor",
"viewGraph": "Gráfico",
"lastRun": "Última corrida",
"never": "Nunca",
"duration": "{{seconds}}s",
"stepOutput": "Saída",
"stepError": "Erro",
"dryRunBadge": "Teste de execução",
"hostSelector": {
"trigger": "Host que desencadeia o ataque",
"host": "hospedeiro específico",
"fleet": "Frota",
"all": "Todos os anfitriões"
},
"triggerKinds": {
"metric_threshold": "Limiar métrico",
"host_status": "O host sobe ou desce.",
"health_check": "Alterações na verificação de saúde",
"schedule": "De acordo com um cronograma",
"docker_event": "Evento de contentor",
"internal_event": "Evento Termix",
"webhook": "Webhook de entrada"
},
"stepTypes": {
"notify": "Enviar notificação",
"http": "Chamar um URL",
"run_snippet": "Executar um excerto de código",
"run_command": "Executar um comando",
"docker": "Controle um contentor",
"tunnel": "Controle um túnel",
"wol": "Acorde um anfitrião",
"wait": "Espera",
"set_var": "Defina uma variável",
"if": "Se/caso contrário",
"run_automation": "Execute outra automatização",
"stop": "Parar"
},
"statuses": {
"running": "Corrida",
"success": "Sucesso",
"failed": "Falhou",
"timeout": "Tempo limite excedido",
"skipped": "Ignorado",
"cancelled": "Cancelado"
},
"fields": {
"host": "Anfitrião",
"metric": "Métrica",
"mount": "Ponto de montagem",
"operator": "É",
"value": "Valor",
"forSeconds": "Sustentado por (segundos)",
"cooldown": "Tempo de arrefecimento (minutos)",
"cron": "Expressão de Cron",
"intervalSeconds": "A cada (segundos)",
"timezone": "Fuso horário",
"timezoneServer": "Hora do servidor",
"timezoneHint": "Os tempos cron são lidos nesta zona. Não se aplica a agendamentos por intervalo.",
"container": "Recipiente",
"command": "Comando",
"url": "URL",
"method": "Método",
"body": "Corpo",
"channels": "Canais",
"title": "Título",
"message": "Mensagem",
"snippet": "Trecho",
"seconds": "Segundos",
"conditionLeftPlaceholder": "Valor a verificar",
"conditionRightPlaceholder": "Compare com",
"variableName": "Nome da variável",
"variableValue": "Valor",
"elevated": "Executar com sudo",
"onError": "Se isso falhar",
"allowPrivateNetwork": "Permitir endereços de rede local",
"allowPrivateNetworkHint": "Necessário para um serviço auto-hospedado na sua rede local (LAN). Desativado por defeito para evitar pedidos a endereços internos."
},
"onError": {
"stop": "Pare a corrida",
"continue": "Continuar"
}
}
}
},
"nav": {
"automations": "Automações"
},
"onboarding": {
"feature_automations": "Automações",
"feature_automations_desc": "Execute automaticamente os passos quando algo acontece e receba notificações."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Automatizări",
"tabAutomations": "Automatizări",
"tabRuns": "Aleargă",
"tabChannels": "Canale",
"createAutomation": "Automatizare nouă",
"backToAutomations": "Înapoi la Automatizări",
"createTitle": "Automatizare nouă",
"editTitle": "Editare automatizare",
"empty": "Încă nu există automatizări. Creați una pentru a reacționa la o metrică, un program sau un eveniment.",
"emptyRuns": "Nicio alergare încă.",
"emptyChannels": "Încă nu există canale. Adaugă unul pentru a trimite notificări undeva.",
"noChannelsHint": "Încă nu a fost configurat niciun canal. Adăugați unul în fila Canale.",
"addChannel": "Adăugați canal",
"testChannel": "Test",
"channelTestSent": "Notificare de testare trimisă",
"channelTestFailed": "Notificarea de testare a eșuat",
"channelDeleted": "Canalul a fost șters",
"deleteChannelConfirm": "Ștergeți acest canal? Automatizările care îl folosesc vor înceta să mai trimită notificări.",
"nameLabel": "Nume",
"namePlaceholder": "De exemplu, curățați /data când se umple",
"nameRequired": "Numele este obligatoriu",
"descriptionLabel": "Descriere",
"descriptionPlaceholder": "Descriere opțională",
"enabledLabel": "Activat",
"concurrencyLabel": "Dacă deja rulează",
"concurrencyHint": "Ce trebuie făcut când acest lucru este declanșat din nou înainte de finalizarea ultimei rulări.",
"concurrency": {
"skip": "Sari peste noua cursă",
"queue": "Așteaptă și aleargă după",
"allow": "Rulați ambele simultan"
},
"save": "Salva",
"created": "Automatizare creată",
"updated": "Automatizare salvată",
"deleted": "Automatizare ștearsă",
"deleteConfirm": "Ștergeți această automatizare? Și istoricul execuțiilor sale este șters.",
"runNow": "Aleargă acum",
"testRun": "Test de funcționare",
"testRunHint": "Aleargă la fiecare pas, dar nu atinge nimic din afara Termixului.",
"runStarted": "Cursa a început",
"runFinished": "Execuția s-a încheiat: {{status}}",
"trigger": "Declanșator",
"steps": "Pași",
"addStep": "Adăugați un pas",
"removeStep": "Eliminați pasul",
"moveUp": "Mută în sus",
"moveDown": "Mută în jos",
"noSteps": "Încă nu există pași. Adăugați unul pentru a specifica ce ar trebui să se întâmple.",
"webhookTokenTitle": "URL-ul webhook-ului",
"webhookTokenDescription": "Aceasta se afișează o singură dată. Salvați-o acum, nu poate fi recuperată ulterior.",
"copy": "Copie",
"copied": "Copiat",
"viewJson": "JSON",
"viewBuilder": "Constructor",
"viewGraph": "Grafic",
"lastRun": "Ultima alergare",
"never": "Nu",
"duration": "{{seconds}}s",
"stepOutput": "Ieșire",
"stepError": "Eroare",
"dryRunBadge": "Test de funcționare",
"hostSelector": {
"trigger": "Gazdă declanșatoare",
"host": "Gazdă specifică",
"fleet": "Flotă",
"all": "Toate gazdele"
},
"triggerKinds": {
"metric_threshold": "Pragul metric",
"host_status": "Gazda se activează sau se deconectează",
"health_check": "Modificări la verificarea stării de sănătate",
"schedule": "Pe un program",
"docker_event": "Eveniment container",
"internal_event": "Evenimentul Termix",
"webhook": "Webhook de intrare"
},
"stepTypes": {
"notify": "Trimiteți o notificare",
"http": "Apelați o adresă URL",
"run_snippet": "Executați un fragment",
"run_command": "Executați o comandă",
"docker": "Controlează un container",
"tunnel": "Controlează un tunel",
"wol": "Trezește o gazdă",
"wait": "Așteaptă",
"set_var": "Setați o variabilă",
"if": "Dacă / altfel",
"run_automation": "Rulați o altă automatizare",
"stop": "Stop"
},
"statuses": {
"running": "Funcţionare",
"success": "Succes",
"failed": "Eșuat",
"timeout": "Expirat",
"skipped": "Omis",
"cancelled": "Anulat"
},
"fields": {
"host": "Gazdă",
"metric": "Metric",
"mount": "Punct de montare",
"operator": "Este",
"value": "Valoare",
"forSeconds": "Menținut timp de (secunde)",
"cooldown": "Timp de răcire (minute)",
"cron": "Expresie Cron",
"intervalSeconds": "La fiecare (secunde)",
"timezone": "Fus orar",
"timezoneServer": "Ora serverului",
"timezoneHint": "Timpii cron sunt citiți în această zonă. Nu se aplică programărilor cu intervale.",
"container": "Container",
"command": "Comanda",
"url": "URL-ul",
"method": "Metodă",
"body": "Corp",
"channels": "Canale",
"title": "Titlu",
"message": "Mesaj",
"snippet": "Fragment",
"seconds": "Secunde",
"conditionLeftPlaceholder": "Valoare de verificat",
"conditionRightPlaceholder": "Comparați cu",
"variableName": "Nume variabilă",
"variableValue": "Valoare",
"elevated": "Execută cu sudo",
"onError": "Dacă acest lucru eșuează",
"allowPrivateNetwork": "Permiteți adrese de rețea locală",
"allowPrivateNetworkHint": "Necesar pentru un serviciu auto-găzduit în rețeaua locală. Dezactivat în mod implicit pentru a preveni solicitările către adrese interne."
},
"onError": {
"stop": "Oprește alergarea",
"continue": "Continuă"
}
}
}
},
"nav": {
"automations": "Automatizări"
},
"onboarding": {
"feature_automations": "Automatizări",
"feature_automations_desc": "Execută pașii automat când se întâmplă ceva și primește notificări."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Автоматизация",
"tabAutomations": "Автоматизация",
"tabRuns": "Беги",
"tabChannels": "Каналы",
"createAutomation": "Новая автоматизация",
"backToAutomations": "Назад к автоматизации",
"createTitle": "Новая автоматизация",
"editTitle": "Редактировать автоматизацию",
"empty": "Автоматизации пока нет. Создайте автоматизацию, которая будет реагировать на показатель, расписание или событие.",
"emptyRuns": "Пока ни одного заезда.",
"emptyChannels": "Каналов пока нет. Добавьте канал, чтобы отправлять уведомления куда-нибудь.",
"noChannelsHint": "Каналы пока не созданы. Добавьте его на вкладке «Каналы».",
"addChannel": "Добавить канал",
"testChannel": "Тест",
"channelTestSent": "Уведомление о тестировании отправлено.",
"channelTestFailed": "Тестовое уведомление не удалось.",
"channelDeleted": "Канал удален",
"deleteChannelConfirm": "Удалить этот канал? Тогда автоматизация, использующая его, перестанет отправлять уведомления.",
"nameLabel": "Имя",
"namePlaceholder": "Например, очищать каталог /data, когда он заполняется.",
"nameRequired": "Имя обязательно для указания.",
"descriptionLabel": "Описание",
"descriptionPlaceholder": "Дополнительное описание",
"enabledLabel": "Включено",
"concurrencyLabel": "Если он уже запущен",
"concurrencyHint": "Что делать, если это снова произойдет до завершения последнего запуска?",
"concurrency": {
"skip": "Пропустите новый запуск",
"queue": "Подожди и беги после",
"allow": "Запустите оба одновременно."
},
"save": "Сохранять",
"created": "Создана автоматизация",
"updated": "Автоматизация спасла",
"deleted": "Автоматизация удалена",
"deleteConfirm": "Удалить эту автоматизацию? История её выполнения также будет удалена.",
"runNow": "Запустите сейчас",
"testRun": "Тестовый запуск",
"testRunHint": "Программа выполняет каждый этап, но не затрагивает ничего за пределами Termix.",
"runStarted": "Запуск начался",
"runFinished": "Выполнение завершено: {{status}}",
"trigger": "Курок",
"steps": "Шаги",
"addStep": "Добавить шаг",
"removeStep": "Удалить шаг",
"moveUp": "Поднимитесь выше",
"moveDown": "Спуститесь вниз",
"noSteps": "Шагов пока нет. Добавьте один, чтобы указать, что должно произойти.",
"webhookTokenTitle": "URL веб-перехватчика",
"webhookTokenDescription": "Эта информация отображается только один раз. Сохраните её сейчас, позже восстановить её будет невозможно.",
"copy": "Копия",
"copied": "Скопировано",
"viewJson": "JSON",
"viewBuilder": "Строитель",
"viewGraph": "График",
"lastRun": "Последний забег",
"never": "Никогда",
"duration": "{{seconds}}с",
"stepOutput": "Выход",
"stepError": "Ошибка",
"dryRunBadge": "Тестовый запуск",
"hostSelector": {
"trigger": "Запуск хоста",
"host": "Конкретный хост",
"fleet": "Флот",
"all": "Все хосты"
},
"triggerKinds": {
"metric_threshold": "Пороговое значение метрики",
"host_status": "Хост переходит в режим повышения или понижения.",
"health_check": "Изменения в результатах медицинского осмотра",
"schedule": "По расписанию",
"docker_event": "Событие контейнера",
"internal_event": "Мероприятие Termix",
"webhook": "Входящий веб-хук"
},
"stepTypes": {
"notify": "Отправить уведомление",
"http": "Вызов URL-адреса",
"run_snippet": "Запустить фрагмент кода",
"run_command": "Выполните команду",
"docker": "Управлять контейнером",
"tunnel": "Контролировать туннель",
"wol": "Разбудить хозяина",
"wait": "Ждать",
"set_var": "Задайте переменную",
"if": "Если / в противном случае",
"run_automation": "Запустите еще одну автоматизацию",
"stop": "Останавливаться"
},
"statuses": {
"running": "Бег",
"success": "Успех",
"failed": "Неуспешный",
"timeout": "Время истекло",
"skipped": "Пропущено",
"cancelled": "Отменено"
},
"fields": {
"host": "Хозяин",
"metric": "Метрика",
"mount": "Маунт-Пойнт",
"operator": "Является",
"value": "Ценить",
"forSeconds": "Длительность (секунды)",
"cooldown": "Время восстановления (минуты)",
"cron": "Выражение Cron",
"intervalSeconds": "Каждые (секунды)",
"timezone": "Часовой пояс",
"timezoneServer": "Время сервера",
"timezoneHint": "В этой зоне считываются временные параметры Cron. Это не относится к интервальным расписаниям.",
"container": "Контейнер",
"command": "Командование",
"url": "URL",
"method": "Метод",
"body": "Тело",
"channels": "Каналы",
"title": "Заголовок",
"message": "Сообщение",
"snippet": "Фрагмент",
"seconds": "Секунды",
"conditionLeftPlaceholder": "Значение для проверки",
"conditionRightPlaceholder": "Сравните с",
"variableName": "Имя переменной",
"variableValue": "Ценить",
"elevated": "Запустить с правами суперпользователя (sudo)",
"onError": "Если это не поможет",
"allowPrivateNetwork": "Разрешить использование локальных сетевых адресов",
"allowPrivateNetworkHint": "Необходим для самостоятельного размещения сервиса в вашей локальной сети. По умолчанию отключен, чтобы предотвратить запросы к внутренним адресам."
},
"onError": {
"stop": "Остановить бег",
"continue": "Продолжать"
}
}
}
},
"nav": {
"automations": "Автоматизация"
},
"onboarding": {
"feature_automations": "Автоматизация",
"feature_automations_desc": "Запускайте шаги автоматически при возникновении событий и получайте уведомления."
}
}
@@ -0,0 +1,149 @@
{
"newUi": {
"sidebar": {
"automations": {
"title": "Аутоматизације",
"tabAutomations": "Аутоматизације",
"tabRuns": "Трчања",
"tabChannels": "Канали",
"createAutomation": "Нова аутоматизација",
"backToAutomations": "Назад на аутоматизацију",
"createTitle": "Нова аутоматизација",
"editTitle": "Уреди аутоматизацију",
"empty": "Још увек нема аутоматизација. Направите једну да бисте реаговали на метрику, распоред или догађај.",
"emptyRuns": "Још нема трчања.",
"emptyChannels": "Још нема канала. Додајте један да бисте слали обавештења негде.",
"noChannelsHint": "Још увек нема подешених канала. Додајте један на картици Канали.",
"addChannel": "Додај канал",
"testChannel": "Тест",
"channelTestSent": "Обавештење о тестирању је послато",
"channelTestFailed": "Обавештење о тестирању није успело",
"channelDeleted": "Канал је обрисан",
"deleteChannelConfirm": "Да ли желите да обришете овај канал? Аутоматизације које га користе ће престати да шаљу обавештења.",
"nameLabel": "Име",
"namePlaceholder": "нпр. Очисти /подаци када се попуне",
"nameRequired": "Име је обавезно",
"descriptionLabel": "Опис",
"descriptionPlaceholder": "Опциони опис",
"enabledLabel": "Омогућено",
"concurrencyLabel": "Ако већ ради",
"concurrencyHint": "Шта урадити када се ово поново покрене пре него што се заврши последње покретање.",
"concurrency": {
"skip": "Прескочи нову вожњу",
"queue": "Чекај и трчи за њим",
"allow": "Покрени оба одједном"
},
"save": "Сачувај",
"created": "Аутоматизација је креирана",
"updated": "Аутоматизација је сачувана",
"deleted": "Аутоматизација је обрисана",
"deleteConfirm": "Да ли желите да обришете ову аутоматизацију? Њена историја покретања је такође обрисана.",
"runNow": "Трчи сада",
"testRun": "Тестирање",
"testRunHint": "Покреће сваки корак, али не додирује ништа изван Термикса.",
"runStarted": "Трчање је започето",
"runFinished": "Трка завршена: {{status}}",
"trigger": "Окидач",
"steps": "Кораци",
"addStep": "Додај корак",
"removeStep": "Уклони корак",
"moveUp": "Помери горе",
"moveDown": "Помери надоле",
"noSteps": "Још нема корака. Додајте један да бисте рекли шта треба да се деси.",
"webhookTokenTitle": "URL вебхука",
"webhookTokenDescription": "Ово се приказује једном. Сачувајте то сада, не можете то касније преузети.",
"copy": "Копија",
"copied": "Копирано",
"viewJson": "JSON",
"viewBuilder": "Градитељ",
"viewGraph": "График",
"lastRun": "Последње трчање",
"never": "Никад",
"duration": "{{seconds}}с",
"stepOutput": "Излаз",
"stepError": "Грешка",
"dryRunBadge": "Тестирање",
"hostSelector": {
"trigger": "Покретач хоста",
"host": "Одређени домаћин",
"fleet": "Флота",
"all": "Сви домаћини"
},
"triggerKinds": {
"metric_threshold": "Праг метрике",
"host_status": "Домаћин иде горе или доле",
"health_check": "Промене у провери исправности",
"schedule": "По распореду",
"docker_event": "Догађај контејнера",
"internal_event": "Термикс догађај",
"webhook": "Долазни вебхук"
},
"stepTypes": {
"notify": "Пошаљи обавештење",
"http": "Позовите URL",
"run_snippet": "Покрени исечак",
"run_command": "Покрените команду",
"docker": "Контролишите контејнер",
"tunnel": "Контролишите тунел",
"wol": "Пробудите домаћина",
"wait": "Чекај",
"set_var": "Поставите променљиву",
"if": "Ако / иначе",
"run_automation": "Покрените још једну аутоматизацију",
"stop": "Заустави"
},
"statuses": {
"running": "Трчање",
"success": "Успех",
"failed": "Неуспешно",
"timeout": "Временско ограничење је истекло",
"skipped": "Прескочено",
"cancelled": "Отказано"
},
"fields": {
"host": "Домаћин",
"metric": "Метрика",
"mount": "Тачка монтирања",
"operator": "Је",
"value": "Вредност",
"forSeconds": "Задржано (секунде)",
"cooldown": "Хлађење (минути)",
"cron": "Cron израз",
"intervalSeconds": "Сваких (секунде)",
"timezone": "Временска зона",
"timezoneServer": "Време сервера",
"timezoneHint": "Cron времена се очитавају у овој зони. Не односи се на интервалне распореде.",
"container": "Контејнер",
"command": "Команда",
"url": "URL",
"method": "Метод",
"body": "Тело",
"channels": "Канали",
"title": "Наслов",
"message": "Порука",
"snippet": "Исечак",
"seconds": "Секунде",
"conditionLeftPlaceholder": "Вредност за проверу",
"conditionRightPlaceholder": "Упореди са",
"variableName": "Назив променљиве",
"variableValue": "Вредност",
"elevated": "Покрени са sudo-ом",
"onError": "Ако ово не успе",
"allowPrivateNetwork": "Дозволи локалне мрежне адресе",
"allowPrivateNetworkHint": "Потребно за самостално хостовану услугу на вашој локалној мрежи. Подразумевано искључено да би се спречили захтеви ка интерним адресама."
},
"onError": {
"stop": "Заустави трчање",
"continue": "Настави"
}
}
}
},
"nav": {
"automations": "Аутоматизације"
},
"onboarding": {
"feature_automations": "Аутоматизације",
"feature_automations_desc": "Аутоматски покрените кораке када се нешто деси и добијте обавештења."
}
}
Loaded 100 of 721 files, more files were not shown because too many files have changed in this diff. Show more