Merge remote-tracking branch 'upstream/master' into sorting-and-search

This commit is contained in:
Marshu 2025-07-18 16:03:33 +08:00
commit efcf154bd7
106 changed files with 4941 additions and 1870 deletions

View file

@ -3,7 +3,7 @@ name: ❓ Ask for help
description: |
Submit any question related to Uptime Kuma
#title: "[Help]"
labels: ["help", "P3-low"]
labels: ["help"]
body:
- type: markdown
attributes:

View file

@ -3,7 +3,7 @@ name: 🐛 Bug Report
description: |
Submit a bug report to help us improve
#title: "[Bug]"
labels: ["bug", "P2-medium"]
labels: ["bug"]
body:
- type: markdown
attributes:

View file

@ -3,28 +3,15 @@ name: 🚀 Feature Request
description: |
Submit a proposal for a new feature
# title: "[Feature]"
labels: ["feature-request", "P3-low"]
labels: ["feature-request"]
body:
- type: markdown
attributes:
value: |
## ❗Important Announcement
### 🚧 Temporary Delay in Feature Requests and Pull Request Reviews
**At this time, we may be slower to respond to new feature requests and review pull requests. Existing requests and PRs will remain in the backlog but may not be prioritized immediately.**
- **Reason**: Our current focus is on addressing bugs, improving system performance, and implementing essential updates. This will help stabilize the project and ensure smoother management.
- **Impact**: While no new feature requests or pull requests are being outright rejected, there may be significant delays in reviews. We encourage the community to help by reviewing PRs or assisting other users in the meantime.
- **What You Can Do**: If you're interested in contributing, reviewing open PRs by following our [Review Guidelines](https://github.com/louislam/uptime-kuma/blob/master/.github/REVIEW_GUIDELINES.md) or offering help to other users is greatly appreciated. All feature requests and PRs will be revisited once the suspension period is lifted.
We appreciate your patience and understanding as we continue to improve Uptime Kuma.
### 🚫 Please Avoid Unnecessary Pinging of Maintainers
**We kindly ask you to refrain from pinging maintainers unless absolutely necessary. Pings are reserved for critical/urgent pull requests that require immediate attention.**
**Why**: Reserving pings for urgent matters ensures maintainers can prioritize critical tasks effectively.
We kindly ask you to refrain from pinging maintainers unless absolutely necessary.
Pings are for critical/urgent pull requests that require immediate attention.
- type: textarea
id: related-issues
validations:

View file

@ -3,7 +3,7 @@ name: 🛡️ Security Issue
description: |
Notify Louis Lam about a security concern. Please do NOT include any sensitive details in this issue.
# title: "Security Issue"
labels: ["security", "P1-high"]
labels: ["security"]
assignees: [louislam]
body:
- type: markdown

View file

@ -1,44 +1,35 @@
**⚠️ Please Note: We do not accept all types of pull requests, and we want to ensure we dont waste your time. Before submitting, make sure you have read our pull request guidelines: [Pull Request Rules](https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma)**
## ❗ Important Announcement
## ❗ Important Announcements
<details><summary>Click here for more details:</summary>
</p>
### 🚧 Temporary Delay in Feature Requests and Pull Request Reviews
**At this time, we may be slower to respond to new feature requests and review pull requests. Existing requests and PRs will remain in the backlog but may not be prioritized immediately.**
- **Reason**: Our current focus is on addressing bugs, improving system performance, and implementing essential updates. This will help stabilize the project and ensure smoother management.
- **Impact**: While no new feature requests or pull requests are being outright rejected, there may be significant delays in reviews. We encourage the community to help by reviewing PRs or assisting other users in the meantime.
- **What You Can Do**: If you're interested in contributing, reviewing open PRs by following our [Review Guidelines](https://github.com/louislam/uptime-kuma/blob/master/.github/REVIEW_GUIDELINES.md) or offering support to other users is greatly appreciated. All feature requests and PRs will be revisited once the suspension period is lifted.
We appreciate your patience and understanding as we continue to improve Uptime Kuma.
**⚠️ Please Note: We do not accept all types of pull requests, and we want to ensure we dont waste your time. Before submitting, make sure you have read our pull request guidelines: [Pull Request Rules](https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma)**
### 🚫 Please Avoid Unnecessary Pinging of Maintainers
**We kindly ask you to refrain from pinging maintainers unless absolutely necessary. Pings are reserved for critical/urgent pull requests that require immediate attention.**
**Why**: Reserving pings for urgent matters ensures maintainers can prioritize critical tasks effectively.
We kindly ask you to refrain from pinging maintainers unless absolutely necessary. Pings are for critical/urgent pull requests that require immediate attention.
</p>
</details>
## 📋 Overview
Provide a clear summary of the purpose and scope of this pull request:
<!-- Provide a clear summary of the purpose and scope of this pull request:-->
- **What problem does this pull request address?**
- Please provide a detailed explanation here.
- **What features or functionality does this pull request introduce or enhance?**
- Please provide a detailed explanation here.
## 🔄 Changes
<!--
Please link any GitHub issues or tasks that this pull request addresses.
Use the appropriate issue numbers or links to enable auto-closing.
-->
### 🛠️ Type of change
- Relates to #issue-number
- Resolves #issue-number
## 🛠️ Type of change
<!-- Please select all options that apply -->
@ -52,23 +43,12 @@ Provide a clear summary of the purpose and scope of this pull request:
- [ ] 🔧 Other (please specify):
- Provide additional details here.
## 🔗 Related Issues
<!--
Please link any GitHub issues or tasks that this pull request addresses. Use the appropriate issue numbers or links.
**Note**: Include only issues directly related to this PR. Remove any irrelevant reference.
-->
- Relates to #issue-number
- Resolves #issue-number
- Fixes #issue-number
## 📄 Checklist *
## 📄 Checklist
<!-- Please select all options that apply -->
- [ ] 🔍 My code adheres to the style guidelines of this project.
- [ ] 🦿 I have indicated where (if any) I used an LLM for the contributions
- [ ] ✅ I ran ESLint and other code linters for modified files.
- [ ] 🛠️ I have reviewed and tested my code.
- [ ] 📝 I have commented my code, especially in hard-to-understand areas (e.g., using JSDoc for methods).
@ -82,10 +62,11 @@ Please link any GitHub issues or tasks that this pull request addresses. Use the
## 📷 Screenshots or Visual Changes
<!--
Please upload the image directly here by pasting it or dragging and dropping. Avoid using external image services as the image will be uploaded automatically.
If this pull request introduces visual changes, please provide the following details.
If not, remove this section.
Please upload the image directly here by pasting it or dragging and dropping.
Avoid using external image services as the image will be uploaded automatically.
-->
- **UI Modifications**: Highlight any changes made to the user interface.
@ -97,26 +78,3 @@ If not, remove this section.
| `DOWN` | ![Before](image-link) | ![After](image-link) |
| Certificate-expiry | ![Before](image-link) | ![After](image-link) |
| Testing | ![Before](image-link) | ![After](image-link) |
## Additional Context
Provide any relevant details to assist reviewers in understanding the changes.
<details><summary>Click here for more details:</summary>
</p>
**Key Considerations**:
- **Design decisions** Key choices or trade-offs made during development.
- **Alternative solutions** Approaches considered but not implemented, along with reasons.
- **Relevant links** Specifications, discussions, or resources that provide context.
- **Dependencies** Related pull requests or issues that must be resolved before merging.
- **Additional context** Any other details that may help reviewers understand the changes.
Provide details here
## 💬 Requested Feedback
<!-- If a part of our docs is unclear, you are unsure how to do something/.. this is where we would appreciate your feedback -->
- `Mention documents needing feedback here`

View file

@ -86,7 +86,7 @@ to review the appropriate one for your contribution.
PR:
- A text may not be currently localisable. In this case, **adding a new
language key** via `$t("languageKey")` might be nessesary
language key** via `$t("languageKey")` might be necessary
- language keys need to be **added to `en.json`** to be visible in weblate. If
this has not happened, a PR is appreciated.
- **Adding a new language** requires a new file see

View file

@ -0,0 +1,12 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.string("smtp_security").defaultTo(null);
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("smtp_security");
});
};

View file

@ -0,0 +1,24 @@
/* SQL:
ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
ALTER TABLE monitor ADD ping_per_request_timeout INTEGER default 2 not null;
*/
exports.up = function (knex) {
// Add new columns to table monitor
return knex.schema
.alterTable("monitor", function (table) {
table.integer("ping_count").defaultTo(1).notNullable();
table.boolean("ping_numeric").defaultTo(true).notNullable();
table.integer("ping_per_request_timeout").defaultTo(2).notNullable();
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.dropColumn("ping_count");
table.dropColumn("ping_numeric");
table.dropColumn("ping_per_request_timeout");
});
};

View file

@ -0,0 +1,13 @@
// Add column custom_url to monitor_group table
exports.up = function (knex) {
return knex.schema
.alterTable("monitor_group", function (table) {
table.text("custom_url", "text");
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor_group", function (table) {
table.dropColumn("custom_url");
});
};

View file

@ -0,0 +1,13 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.boolean("ip_family").defaultTo(null);
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.dropColumn("ip_family");
});
};

View file

@ -0,0 +1,12 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.string("manual_status").defaultTo(null);
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("manual_status");
});
};

View file

@ -0,0 +1,34 @@
// Add column last_start_date to maintenance table
exports.up = async function (knex) {
await knex.schema
.alterTable("maintenance", function (table) {
table.datetime("last_start_date");
});
// Perform migration for recurring-interval strategy
const recurringMaintenances = await knex("maintenance").where({
strategy: "recurring-interval",
cron: "* * * * *"
}).select("id", "start_time");
// eslint-disable-next-line camelcase
const maintenanceUpdates = recurringMaintenances.map(async ({ start_time, id }) => {
// eslint-disable-next-line camelcase
const [ hourStr, minuteStr ] = start_time.split(":");
const hour = parseInt(hourStr, 10);
const minute = parseInt(minuteStr, 10);
const cron = `${minute} ${hour} * * *`;
await knex("maintenance")
.where({ id })
.update({ cron });
});
await Promise.all(maintenanceUpdates);
};
exports.down = function (knex) {
return knex.schema.alterTable("maintenance", function (table) {
table.dropColumn("last_start_date");
});
};

View file

@ -0,0 +1,13 @@
// Fix: Change manual_status column type to smallint
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.smallint("manual_status").alter();
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.string("manual_status").alter();
});
};

View file

@ -0,0 +1,12 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.string("oauth_audience").nullable().defaultTo(null);
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.string("oauth_audience").alter();
});
};

View file

@ -79,6 +79,10 @@ USER node
RUN git config --global user.email "no-reply@no-reply.com"
RUN git config --global user.name "PR Tester"
RUN git clone https://github.com/louislam/uptime-kuma.git .
# Hide the warning when running in detached head state
RUN git config --global advice.detachedHead false
RUN npm ci
EXPOSE 3000 3001

View file

@ -1,33 +0,0 @@
const childProcess = require("child_process");
if (!process.env.UPTIME_KUMA_GH_REPO) {
console.error("Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)");
process.exit(1);
}
let inputArray = process.env.UPTIME_KUMA_GH_REPO.split(":");
if (inputArray.length !== 2) {
console.error("Invalid format. Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)");
}
let name = inputArray[0];
let branch = inputArray[1];
console.log("Checkout pr");
// Checkout the pr
let result = childProcess.spawnSync("git", [ "remote", "add", name, `https://github.com/${name}/uptime-kuma` ]);
console.log(result.stdout.toString());
console.error(result.stderr.toString());
result = childProcess.spawnSync("git", [ "fetch", name, branch ]);
console.log(result.stdout.toString());
console.error(result.stderr.toString());
result = childProcess.spawnSync("git", [ "checkout", `${name}/${branch}`, "--force" ]);
console.log(result.stdout.toString());
console.error(result.stderr.toString());

34
extra/checkout-pr.mjs Normal file
View file

@ -0,0 +1,34 @@
import childProcess from "child_process";
import { parsePrName } from "./kuma-pr/pr-lib.mjs";
let { name, branch } = parsePrName(process.env.UPTIME_KUMA_GH_REPO);
console.log(`Checking out PR from ${name}:${branch}`);
// Checkout the pr
let result = childProcess.spawnSync("git", [ "remote", "add", name, `https://github.com/${name}/uptime-kuma` ], {
stdio: "inherit"
});
if (result.status !== 0) {
console.error("Failed to add remote repository.");
process.exit(1);
}
result = childProcess.spawnSync("git", [ "fetch", name, branch ], {
stdio: "inherit"
});
if (result.status !== 0) {
console.error("Failed to fetch the branch.");
process.exit(1);
}
result = childProcess.spawnSync("git", [ "checkout", `${name}/${branch}`, "--force" ], {
stdio: "inherit"
});
if (result.status !== 0) {
console.error("Failed to checkout the branch.");
process.exit(1);
}

View file

@ -37,7 +37,7 @@ const github = require("@actions/github");
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please **DO NOT open blank issues and use our [issue-templates](https://github.com/louislam/uptime-kuma/issues/new/choose) instead**.\nBlank Issues do not contain the context nessesary for a good discussions.`
body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please **DO NOT open blank issues and use our [issue-templates](https://github.com/louislam/uptime-kuma/issues/new/choose) instead**.\nBlank Issues do not contain the context necessary for a good discussions.`
});
// Close the issue

26
extra/kuma-pr/index.mjs Normal file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env node
import { spawn } from "child_process";
import { parsePrName } from "./pr-lib.mjs";
const prName = process.argv[2];
// Pre-check the prName here, so testers don't need to wait until the Docker image is pulled to see the error.
try {
parsePrName(prName);
} catch (error) {
console.error(error.message);
process.exit(1);
}
spawn("docker", [
"run",
"--rm",
"-it",
"-p", "3000:3000",
"-p", "3001:3001",
"--pull", "always",
"-e", `UPTIME_KUMA_GH_REPO=${prName}`,
"louislam/uptime-kuma:pr-test2"
], {
stdio: "inherit",
});

View file

@ -0,0 +1,8 @@
{
"name": "kuma-pr",
"version": "1.0.0",
"type": "module",
"bin": {
"kuma-pr": "./index.mjs"
}
}

39
extra/kuma-pr/pr-lib.mjs Normal file
View file

@ -0,0 +1,39 @@
/**
* Parse <name>:<branch> to an object.
* @param {string} prName <name>:<branch>
* @returns {object} An object with name and branch properties.
*/
export function parsePrName(prName) {
let name = "louislam";
let branch;
const errorMessage = "Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)";
if (!prName) {
throw new Error(errorMessage);
}
prName = prName.trim();
if (prName === "") {
throw new Error(errorMessage);
}
let inputArray = prName.split(":");
// Just realized that owner's prs are not prefixed with "louislam:"
if (inputArray.length === 1) {
branch = inputArray[0];
} else if (inputArray.length === 2) {
name = inputArray[0];
branch = inputArray[1];
} else {
throw new Error("Invalid format. The format is like this: mhkarimi1383:goalert-notification");
}
return {
name,
branch
};
}

2029
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "uptime-kuma",
"version": "2.0.0-beta.2",
"version": "2.0.0-beta.3",
"license": "MIT",
"repository": {
"type": "git",
@ -32,7 +32,7 @@
"test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063",
"playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json",
"playwright-show-report": "playwright show-report ./private/playwright-report",
"tsc": "tsc",
"tsc": "tsc --project ./tsconfig-backend.json",
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
"build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push",
"build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push",
@ -57,7 +57,7 @@
"release-nightly": "node ./extra/release/nightly.mjs",
"git-remove-tag": "git tag -d",
"build-dist-and-restart": "npm run build && npm run start-server-dev",
"start-pr-test": "node extra/checkout-pr.js && npm install && npm run dev",
"start-pr-test": "node extra/checkout-pr.mjs && npm install && npm run dev",
"build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go",
"deploy-demo-server": "node extra/deploy-demo-server.js",
"sort-contributors": "node extra/sort-contributors.js",

View file

@ -26,7 +26,7 @@ exports.login = async function (username, password) {
// Upgrade the hash to bcrypt
if (passwordHash.needRehash(user.password)) {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
passwordHash.generate(password),
await passwordHash.generate(password),
user.id,
]);
}

View file

@ -1,4 +1,5 @@
const fs = require("fs");
const fsAsync = fs.promises;
const { R } = require("redbean-node");
const { setSetting, setting } = require("./util-server");
const { log, sleep } = require("../src/util");
@ -18,7 +19,7 @@ const KumaColumnCompiler = require("./utils/knex/lib/dialects/mysql2/schema/mysq
class Database {
/**
* Boostrap database for SQLite
* Bootstrap database for SQLite
* @type {string}
*/
static templatePath = "./db/kuma.db";
@ -707,12 +708,12 @@ class Database {
/**
* Get the size of the database (SQLite only)
* @returns {number} Size of database
* @returns {Promise<number>} Size of database
*/
static getSize() {
static async getSize() {
if (Database.dbConfig.type === "sqlite") {
log.debug("db", "Database.getSize()");
let stats = fs.statSync(Database.sqlitePath);
let stats = await fsAsync.stat(Database.sqlitePath);
log.debug("db", stats);
return stats.size;
}
@ -736,7 +737,7 @@ class Database {
if (Database.dbConfig.type === "sqlite") {
return "DATETIME('now', ? || ' hours')";
} else {
return "DATE_ADD(NOW(), INTERVAL ? HOUR)";
return "DATE_ADD(UTC_TIMESTAMP(), INTERVAL ? HOUR)";
}
}

View file

@ -1,10 +1,10 @@
const axios = require("axios");
const { R } = require("redbean-node");
const https = require("https");
const fs = require("fs");
const fsAsync = require("fs").promises;
const path = require("path");
const Database = require("./database");
const { axiosAbortSignal } = require("./util-server");
const { axiosAbortSignal, fsExists } = require("./util-server");
class DockerHost {
@ -81,7 +81,7 @@ class DockerHost {
options.socketPath = dockerHost.dockerDaemon;
} else if (dockerHost.dockerType === "tcp") {
options.baseURL = DockerHost.patchDockerURL(dockerHost.dockerDaemon);
options.httpsAgent = new https.Agent(DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL));
options.httpsAgent = new https.Agent(await DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL));
}
try {
@ -141,9 +141,9 @@ class DockerHost {
* File names can also be overridden via 'DOCKER_TLS_FILE_NAME_(CA|KEY|CERT)'.
* @param {string} dockerType i.e. "tcp" or "socket"
* @param {string} url The docker host URL rewritten to https://
* @returns {object} HTTP agent options
* @returns {Promise<object>} HTTP agent options
*/
static getHttpsAgentOptions(dockerType, url) {
static async getHttpsAgentOptions(dockerType, url) {
let baseOptions = {
maxCachedSessions: 0,
rejectUnauthorized: true
@ -156,10 +156,10 @@ class DockerHost {
let certPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameCert);
let keyPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameKey);
if (dockerType === "tcp" && fs.existsSync(caPath) && fs.existsSync(certPath) && fs.existsSync(keyPath)) {
let ca = fs.readFileSync(caPath);
let key = fs.readFileSync(keyPath);
let cert = fs.readFileSync(certPath);
if (dockerType === "tcp" && await fsExists(caPath) && await fsExists(certPath) && await fsExists(keyPath)) {
let ca = await fsAsync.readFile(caPath);
let key = await fsAsync.readFile(keyPath);
let cert = await fsAsync.readFile(certPath);
certOptions = {
ca,
key,

View file

@ -33,7 +33,7 @@ class Group extends BeanModel {
*/
async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(`
SELECT monitor.*, monitor_group.send_url FROM monitor, monitor_group
SELECT monitor.*, monitor_group.send_url, monitor_group.custom_url FROM monitor, monitor_group
WHERE monitor.id = monitor_group.monitor_id
AND group_id = ?
ORDER BY monitor_group.weight

View file

@ -158,12 +158,22 @@ class Maintenance extends BeanModel {
bean.active = obj.active;
if (obj.dateRange[0]) {
const parsedDate = new Date(obj.dateRange[0]);
if (isNaN(parsedDate.getTime()) || parsedDate.getFullYear() > 9999) {
throw new Error("Invalid start date");
}
bean.start_date = obj.dateRange[0];
} else {
bean.start_date = null;
}
if (obj.dateRange[1]) {
const parsedDate = new Date(obj.dateRange[1]);
if (isNaN(parsedDate.getTime()) || parsedDate.getFullYear() > 9999) {
throw new Error("Invalid end date");
}
bean.end_date = obj.dateRange[1];
} else {
bean.end_date = null;
@ -192,7 +202,7 @@ class Maintenance extends BeanModel {
* @returns {void}
*/
static validateCron(cron) {
let job = new Cron(cron, () => {});
let job = new Cron(cron, () => { });
job.stop();
}
@ -229,11 +239,13 @@ class Maintenance extends BeanModel {
apicache.clear();
});
} else if (this.cron != null) {
let current = dayjs();
// Here should be cron or recurring
try {
this.beanMeta.status = "scheduled";
let startEvent = (customDuration = 0) => {
let startEvent = async (customDuration = 0) => {
log.info("maintenance", "Maintenance id: " + this.id + " is under maintenance now");
this.beanMeta.status = "under-maintenance";
@ -248,6 +260,10 @@ class Maintenance extends BeanModel {
this.beanMeta.status = "scheduled";
UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id);
}, duration);
// Set last start date to current time
this.last_start_date = current.toISOString();
R.store(this);
};
// Create Cron
@ -258,9 +274,25 @@ class Maintenance extends BeanModel {
const startDateTime = startDate.hour(hour).minute(minute);
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
interval: this.interval_day * 24 * 60 * 60,
startAt: startDateTime.toISOString(),
}, startEvent);
}, () => {
if (!this.lastStartDate || this.interval_day === 1) {
return startEvent();
}
// If last start date is set, it means the maintenance has been started before
let lastStartDate = dayjs(this.lastStartDate)
.subtract(1.1, "hour"); // Subtract 1.1 hour to avoid issues with timezone differences
// Check if the interval is enough
if (current.diff(lastStartDate, "day") < this.interval_day) {
log.debug("maintenance", "Maintenance id: " + this.id + " is still in the window, skipping start event");
return;
}
log.debug("maintenance", "Maintenance id: " + this.id + " is not in the window, starting event");
return startEvent();
});
} else {
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
@ -269,7 +301,6 @@ class Maintenance extends BeanModel {
// Continue if the maintenance is still in the window
let runningTimeslot = this.getRunningTimeslot();
let current = dayjs();
if (runningTimeslot) {
let duration = dayjs(runningTimeslot.endDate).diff(current, "second") * 1000;
@ -413,8 +444,11 @@ class Maintenance extends BeanModel {
} else if (!this.strategy.startsWith("recurring-")) {
this.cron = "";
} else if (this.strategy === "recurring-interval") {
// For intervals, the pattern is calculated in the run function as the interval-option is set
this.cron = "* * * * *";
// For intervals, the pattern is used to check if the execution should be started
let array = this.start_time.split(":");
let hour = parseInt(array[0]);
let minute = parseInt(array[1]);
this.cron = `${minute} ${hour} * * *`;
this.duration = this.calcDuration();
log.debug("maintenance", "Cron: " + this.cron);
log.debug("maintenance", "Duration: " + this.duration);

View file

@ -2,7 +2,11 @@ const dayjs = require("dayjs");
const axios = require("axios");
const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
SQL_DATETIME_FORMAT, evaluateJsonQuery
SQL_DATETIME_FORMAT, evaluateJsonQuery,
PING_PACKET_SIZE_MIN, PING_PACKET_SIZE_MAX, PING_PACKET_SIZE_DEFAULT,
PING_GLOBAL_TIMEOUT_MIN, PING_GLOBAL_TIMEOUT_MAX, PING_GLOBAL_TIMEOUT_DEFAULT,
PING_COUNT_MIN, PING_COUNT_MAX, PING_COUNT_DEFAULT,
PING_PER_REQUEST_TIMEOUT_MIN, PING_PER_REQUEST_TIMEOUT_MAX, PING_PER_REQUEST_TIMEOUT_DEFAULT
} = require("../../src/util");
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
@ -53,7 +57,7 @@ class Monitor extends BeanModel {
};
if (this.sendUrl) {
obj.url = this.url;
obj.url = this.customUrl ?? this.url;
}
if (showTags) {
@ -153,8 +157,15 @@ class Monitor extends BeanModel {
snmpOid: this.snmpOid,
jsonPathOperator: this.jsonPathOperator,
snmpVersion: this.snmpVersion,
smtpSecurity: this.smtpSecurity,
rabbitmqNodes: JSON.parse(this.rabbitmqNodes),
conditions: JSON.parse(this.conditions),
ipFamily: this.ipFamily,
// ping advanced options
ping_numeric: this.isPingNumeric(),
ping_count: this.ping_count,
ping_per_request_timeout: this.ping_per_request_timeout,
};
if (includeSensitiveData) {
@ -170,6 +181,7 @@ class Monitor extends BeanModel {
oauth_client_secret: this.oauth_client_secret,
oauth_token_url: this.oauth_token_url,
oauth_scopes: this.oauth_scopes,
oauth_audience: this.oauth_audience,
oauth_auth_method: this.oauth_auth_method,
pushToken: this.pushToken,
databaseConnectionString: this.databaseConnectionString,
@ -247,6 +259,14 @@ class Monitor extends BeanModel {
return Boolean(this.expiryNotification);
}
/**
* Check if ping should use numeric output only
* @returns {boolean} True if IP addresses will be output instead of symbolic hostnames
*/
isPingNumeric() {
return Boolean(this.ping_numeric);
}
/**
* Parse to boolean
* @returns {boolean} Should TLS errors be ignored?
@ -408,10 +428,26 @@ class Monitor extends BeanModel {
}
}
let agentFamily = undefined;
if (this.ipFamily === "ipv4") {
agentFamily = 4;
}
if (this.ipFamily === "ipv6") {
agentFamily = 6;
}
const httpsAgentOptions = {
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: !this.getIgnoreTls(),
secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT,
autoSelectFamily: true,
...(agentFamily ? { family: agentFamily } : {})
};
const httpAgentOptions = {
maxCachedSessions: 0,
autoSelectFamily: true,
...(agentFamily ? { family: agentFamily } : {})
};
log.debug("monitor", `[${this.name}] Prepare Options for axios`);
@ -473,6 +509,7 @@ class Monitor extends BeanModel {
if (proxy && proxy.active) {
const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, {
httpsAgentOptions: httpsAgentOptions,
httpAgentOptions: httpAgentOptions,
});
options.proxy = false;
@ -481,6 +518,10 @@ class Monitor extends BeanModel {
}
}
if (!options.httpAgent) {
options.httpAgent = new http.Agent(httpAgentOptions);
}
if (!options.httpsAgent) {
let jar = new CookieJar();
let httpsCookieAgentOptions = {
@ -584,7 +625,7 @@ class Monitor extends BeanModel {
bean.status = UP;
} else if (this.type === "ping") {
bean.ping = await ping(this.hostname, this.packetSize);
bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.timeout, this.ping_per_request_timeout);
bean.msg = "";
bean.status = UP;
} else if (this.type === "push") { // Type: Push
@ -656,7 +697,7 @@ class Monitor extends BeanModel {
bean.msg = res.data.response.servers[0].name;
try {
bean.ping = await ping(this.hostname, this.packetSize);
bean.ping = await ping(this.hostname, PING_COUNT_DEFAULT, "", true, this.packetSize, PING_GLOBAL_TIMEOUT_DEFAULT, PING_PER_REQUEST_TIMEOUT_DEFAULT);
} catch (_) { }
} else {
throw new Error("Server not found on Steam");
@ -706,7 +747,7 @@ class Monitor extends BeanModel {
} else if (dockerHost._dockerType === "tcp") {
options.baseURL = DockerHost.patchDockerURL(dockerHost._dockerDaemon);
options.httpsAgent = new https.Agent(
DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL)
await DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL)
);
}
@ -1273,7 +1314,7 @@ class Monitor extends BeanModel {
/**
* Send a notification about a monitor
* @param {boolean} isFirstBeat Is this beat the first of this monitor?
* @param {Monitor} monitor The monitor to send a notificaton about
* @param {Monitor} monitor The monitor to send a notification about
* @param {Bean} bean Status information about monitor
* @returns {void}
*/
@ -1468,6 +1509,31 @@ class Monitor extends BeanModel {
if (this.interval < MIN_INTERVAL_SECOND) {
throw new Error(`Interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`);
}
if (this.type === "ping") {
// ping parameters validation
if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})`);
}
if (this.ping_per_request_timeout && (this.ping_per_request_timeout < PING_PER_REQUEST_TIMEOUT_MIN || this.ping_per_request_timeout > PING_PER_REQUEST_TIMEOUT_MAX)) {
throw new Error(`Per-ping timeout must be between ${PING_PER_REQUEST_TIMEOUT_MIN} and ${PING_PER_REQUEST_TIMEOUT_MAX} seconds (default: ${PING_PER_REQUEST_TIMEOUT_DEFAULT})`);
}
if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX} (default: ${PING_COUNT_DEFAULT})`);
}
if (this.timeout) {
const pingGlobalTimeout = Math.round(Number(this.timeout));
if (pingGlobalTimeout < this.ping_per_request_timeout || pingGlobalTimeout < PING_GLOBAL_TIMEOUT_MIN || pingGlobalTimeout > PING_GLOBAL_TIMEOUT_MAX) {
throw new Error(`Timeout must be between ${PING_GLOBAL_TIMEOUT_MIN} and ${PING_GLOBAL_TIMEOUT_MAX} seconds (default: ${PING_GLOBAL_TIMEOUT_DEFAULT})`);
}
this.timeout = pingGlobalTimeout;
}
}
}
/**
@ -1681,7 +1747,7 @@ class Monitor extends BeanModel {
*/
async makeOidcTokenClientCredentialsRequest() {
log.debug("monitor", `[${this.name}] The oauth access-token undefined or expired. Requesting a new token`);
const oAuthAccessToken = await getOidcTokenClientCredentials(this.oauth_token_url, this.oauth_client_id, this.oauth_client_secret, this.oauth_scopes, this.oauth_auth_method);
const oAuthAccessToken = await getOidcTokenClientCredentials(this.oauth_token_url, this.oauth_client_id, this.oauth_client_secret, this.oauth_scopes, this.oauth_audience, this.oauth_auth_method);
if (this.oauthAccessToken?.expires_at) {
log.debug("monitor", `[${this.name}] Obtained oauth access-token. Expires at ${new Date(this.oauthAccessToken?.expires_at * 1000)}`);
} else {

View file

@ -120,8 +120,8 @@ class StatusPage extends BeanModel {
const head = $("head");
if (statusPage.googleAnalyticsTagId) {
let escapedGoogleAnalyticsScript = googleAnalytics.getGoogleAnalyticsScript(statusPage.googleAnalyticsTagId);
if (statusPage.google_analytics_tag_id) {
let escapedGoogleAnalyticsScript = googleAnalytics.getGoogleAnalyticsScript(statusPage.google_analytics_tag_id);
head.append($(escapedGoogleAnalyticsScript));
}

View file

@ -14,7 +14,7 @@ class User extends BeanModel {
*/
static async resetPassword(userID, newPassword) {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
passwordHash.generate(newPassword),
await passwordHash.generate(newPassword),
userID
]);
}
@ -25,7 +25,7 @@ class User extends BeanModel {
* @returns {Promise<void>}
*/
async resetPassword(newPassword) {
const hashedPassword = passwordHash.generate(newPassword);
const hashedPassword = await passwordHash.generate(newPassword);
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
hashedPassword,

View file

@ -89,6 +89,9 @@ function NtlmClient(credentials, AxiosConfig) {
switch (_b.label) {
case 0:
error = err.response;
// The header may look like this: `Negotiate, NTLM, Basic realm="itsahiddenrealm.example.net"`Add commentMore actions
// so extract the 'NTLM' part first
const ntlmheader = error.headers['www-authenticate'].split(',').find(_ => _.match(/ *NTLM/))?.trim() || '';
if (!(error && error.status === 401
&& error.headers['www-authenticate']
&& error.headers['www-authenticate'].includes('NTLM'))) return [3 /*break*/, 3];
@ -96,12 +99,12 @@ function NtlmClient(credentials, AxiosConfig) {
// include the Negotiate option when responding with the T2 message
// There is nore we could do to ensure we are processing correctly,
// but this is the easiest option for now
if (error.headers['www-authenticate'].length < 50) {
if (ntlmheader.length < 50) {
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
error.config.headers["Authorization"] = t1Msg;
}
else {
t2Msg = ntlm.decodeType2Message((error.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
t2Msg = ntlm.decodeType2Message((ntlmheader.match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
error.config.headers["X-retry"] = "false";
error.config.headers["Authorization"] = t3Msg;

View file

@ -0,0 +1,36 @@
const { MonitorType } = require("./monitor-type");
const { UP, DOWN, PENDING } = require("../../src/util");
class ManualMonitorType extends MonitorType {
name = "Manual";
type = "manual";
description = "A monitor that allows manual control of the status";
supportsConditions = false;
conditionVariables = [];
/**
* @inheritdoc
*/
async check(monitor, heartbeat) {
if (monitor.manual_status !== null) {
heartbeat.status = monitor.manual_status;
switch (monitor.manual_status) {
case UP:
heartbeat.msg = "Up";
break;
case DOWN:
heartbeat.msg = "Down";
break;
default:
heartbeat.msg = "Pending";
}
} else {
heartbeat.status = PENDING;
heartbeat.msg = "Manual monitoring - No status set";
}
}
}
module.exports = {
ManualMonitorType
};

View file

@ -0,0 +1,35 @@
const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");
const nodemailer = require("nodemailer");
class SMTPMonitorType extends MonitorType {
name = "smtp";
/**
* @inheritdoc
*/
async check(monitor, heartbeat, _server) {
let options = {
port: monitor.port || 25,
host: monitor.hostname,
secure: monitor.smtpSecurity === "secure", // use SMTPS (not STARTTLS)
ignoreTLS: monitor.smtpSecurity === "nostarttls", // don't use STARTTLS even if it's available
requireTLS: monitor.smtpSecurity === "starttls", // use STARTTLS or fail
};
let transporter = nodemailer.createTransport(options);
try {
await transporter.verify();
heartbeat.status = UP;
heartbeat.msg = "SMTP connection verifies successfully";
} catch (e) {
throw new Error(`SMTP connection doesn't verify: ${e}`);
} finally {
transporter.close();
}
}
}
module.exports = {
SMTPMonitorType,
};

View file

@ -18,17 +18,28 @@ class Discord extends NotificationProvider {
webhookUrl.searchParams.append("thread_id", notification.threadId);
}
// Check if the webhook has an avatar
let webhookHasAvatar = true;
try {
const webhookInfo = await axios.get(webhookUrl.toString());
webhookHasAvatar = !!webhookInfo.data.avatar;
} catch (e) {
// If we can't verify, we assume he has an avatar to avoid forcing the default avatar
webhookHasAvatar = true;
}
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
let discordtestdata = {
username: discordDisplayName,
content: msg,
};
if (!webhookHasAvatar) {
discordtestdata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png";
}
if (notification.discordChannelType === "createNewForumPost") {
discordtestdata.thread_name = notification.postName;
}
await axios.post(webhookUrl.toString(), discordtestdata);
return okMsg;
}
@ -61,6 +72,9 @@ class Discord extends NotificationProvider {
],
}],
};
if (!webhookHasAvatar) {
discorddowndata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png";
}
if (notification.discordChannelType === "createNewForumPost") {
discorddowndata.thread_name = notification.postName;
}
@ -98,6 +112,9 @@ class Discord extends NotificationProvider {
],
}],
};
if (!webhookHasAvatar) {
discordupdata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png";
}
if (notification.discordChannelType === "createNewForumPost") {
discordupdata.thread_name = notification.postName;

View file

@ -38,7 +38,7 @@ class FlashDuty extends NotificationProvider {
}
/**
* Generate a monitor url from the monitors infomation
* Generate a monitor url from the monitors information
* @param {object} monitorInfo Monitor details
* @returns {string|undefined} Monitor URL
*/
@ -73,13 +73,13 @@ class FlashDuty extends NotificationProvider {
}
const options = {
method: "POST",
url: "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + notification.flashdutyIntegrationKey,
url: notification.flashdutyIntegrationKey.startsWith("http") ? notification.flashdutyIntegrationKey : "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + notification.flashdutyIntegrationKey,
headers: { "Content-Type": "application/json" },
data: {
description: `[${title}] [${monitorInfo.name}] ${body}`,
title,
event_status: eventStatus || "Info",
alert_key: String(monitorInfo.id) || Math.random().toString(36).substring(7),
alert_key: monitorInfo.id ? String(monitorInfo.id) : Math.random().toString(36).substring(7),
labels,
}
};

View file

@ -0,0 +1,53 @@
const { getMonitorRelativeURL, UP } = require("../../src/util");
const { setting } = require("../util-server");
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Notifery extends NotificationProvider {
name = "notifery";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
const url = "https://api.notifery.com/event";
let data = {
title: notification.notiferyTitle || "Uptime Kuma Alert",
message: msg,
};
if (notification.notiferyGroup) {
data.group = notification.notiferyGroup;
}
// Link to the monitor
const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorJSON) {
data.message += `\n\nMonitor: ${baseURL}${getMonitorRelativeURL(monitorJSON.id)}`;
}
if (heartbeatJSON) {
data.code = heartbeatJSON.status === UP ? 0 : 1;
if (heartbeatJSON.ping) {
data.duration = heartbeatJSON.ping;
}
}
try {
const headers = {
"Content-Type": "application/json",
"x-api-key": notification.notiferyApiKey,
};
await axios.post(url, data, { headers });
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Notifery;

View file

@ -41,8 +41,8 @@ class Ntfy extends NotificationProvider {
if (heartbeatJSON.status === DOWN) {
tags = [ "red_circle" ];
status = "Down";
// if priority is not 5, increase priority for down alerts
priority = priority === 5 ? priority : priority + 1;
// defaults to max(priority + 1, 5)
priority = notification.ntfyPriorityDown || (priority === 5 ? priority : priority + 1);
} else if (heartbeatJSON["status"] === UP) {
tags = [ "green_circle" ];
status = "Up";

View file

@ -15,7 +15,7 @@ class PromoSMS extends NotificationProvider {
notification.promosmsAllowLongSMS = false;
}
//TODO: Add option for enabling special characters. It will decrese message max length from 160 to 70 chars.
//TODO: Add option for enabling special characters. It will decrease message max length from 160 to 70 chars.
//Lets remove non ascii char
let cleanMsg = msg.replace(/[^\x00-\x7F]/g, "");

View file

@ -11,7 +11,7 @@ class PushDeer extends NotificationProvider {
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
const serverUrl = notification.pushdeerServer || "https://api2.pushdeer.com";
// capture group below is nessesary to prevent an ReDOS-attack
// capture group below is necessary to prevent an ReDOS-attack
const url = `${serverUrl.trim().replace(/([^/])\/+$/, "$1")}/message/push`;
let valid = msg != null && monitorJSON != null && heartbeatJSON != null;

View file

@ -13,6 +13,7 @@ const DingDing = require("./notification-providers/dingding");
const Discord = require("./notification-providers/discord");
const Elks = require("./notification-providers/46elks");
const Feishu = require("./notification-providers/feishu");
const Notifery = require("./notification-providers/notifery");
const FreeMobile = require("./notification-providers/freemobile");
const GoogleChat = require("./notification-providers/google-chat");
const Gorush = require("./notification-providers/gorush");
@ -169,6 +170,7 @@ class Notification {
new YZJ(),
new SMSPlanet(),
new SpugPush(),
new Notifery(),
];
for (let item of list) {
if (! item.name) {

View file

@ -5,10 +5,10 @@ const saltRounds = 10;
/**
* Hash a password
* @param {string} password Password to hash
* @returns {string} Hash
* @returns {Promise<string>} Hash
*/
exports.generate = function (password) {
return bcrypt.hashSync(password, saltRounds);
return bcrypt.hash(password, saltRounds);
};
/**

View file

@ -2,6 +2,7 @@ const PrometheusClient = require("prom-client");
const { log } = require("../src/util");
const commonLabels = [
"monitor_id",
"monitor_name",
"monitor_type",
"monitor_url",
@ -40,6 +41,7 @@ class Prometheus {
*/
constructor(monitor) {
this.monitorLabelValues = {
monitor_id: monitor.id,
monitor_name: monitor.name,
monitor_type: monitor.type,
monitor_url: monitor.url,

View file

@ -50,7 +50,7 @@ router.all("/api/push/:pushToken", async (request, response) => {
let msg = request.query.msg || "OK";
let ping = parseFloat(request.query.ping) || null;
let statusString = request.query.status || "up";
let status = (statusString === "up") ? UP : DOWN;
const statusFromParam = (statusString === "up") ? UP : DOWN;
let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
pushToken
@ -80,7 +80,7 @@ router.all("/api/push/:pushToken", async (request, response) => {
msg = "Monitor under maintenance";
bean.status = MAINTENANCE;
} else {
determineStatus(status, previousHeartbeat, monitor.maxretries, monitor.isUpsideDown(), bean);
determineStatus(statusFromParam, previousHeartbeat, monitor.maxretries, monitor.isUpsideDown(), bean);
}
// Calculate uptime
@ -92,21 +92,21 @@ router.all("/api/push/:pushToken", async (request, response) => {
log.debug("router", "PreviousStatus: " + previousHeartbeat?.status);
log.debug("router", "Current Status: " + bean.status);
bean.important = Monitor.isImportantBeat(isFirstBeat, previousHeartbeat?.status, status);
bean.important = Monitor.isImportantBeat(isFirstBeat, previousHeartbeat?.status, bean.status);
if (Monitor.isImportantForNotification(isFirstBeat, previousHeartbeat?.status, status)) {
if (Monitor.isImportantForNotification(isFirstBeat, previousHeartbeat?.status, bean.status)) {
// Reset down count
bean.downCount = 0;
log.debug("monitor", `[${this.name}] sendNotification`);
log.debug("monitor", `[${monitor.name}] sendNotification`);
await Monitor.sendNotification(isFirstBeat, monitor, bean);
} else {
if (bean.status === DOWN && this.resendInterval > 0) {
if (bean.status === DOWN && monitor.resendInterval > 0) {
++bean.downCount;
if (bean.downCount >= this.resendInterval) {
if (bean.downCount >= monitor.resendInterval) {
// Send notification again, because we are still DOWN
log.debug("monitor", `[${this.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`);
await Monitor.sendNotification(isFirstBeat, this, bean);
log.debug("monitor", `[${monitor.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${monitor.resendInterval}`);
await Monitor.sendNotification(isFirstBeat, monitor, bean);
// Reset down count
bean.downCount = 0;

View file

@ -674,7 +674,7 @@ let needSetup = false;
let user = R.dispense("user");
user.username = username;
user.password = passwordHash.generate(password);
user.password = await passwordHash.generate(password);
await R.store(user);
needSetup = false;
@ -792,6 +792,7 @@ let needSetup = false;
bean.url = monitor.url;
bean.method = monitor.method;
bean.body = monitor.body;
bean.ipFamily = monitor.ipFamily;
bean.headers = monitor.headers;
bean.basic_auth_user = monitor.basic_auth_user;
bean.basic_auth_pass = monitor.basic_auth_pass;
@ -801,6 +802,7 @@ let needSetup = false;
bean.oauth_auth_method = monitor.oauth_auth_method;
bean.oauth_token_url = monitor.oauth_token_url;
bean.oauth_scopes = monitor.oauth_scopes;
bean.oauth_audience = monitor.oauth_audience;
bean.tlsCa = monitor.tlsCa;
bean.tlsCert = monitor.tlsCert;
bean.tlsKey = monitor.tlsKey;
@ -866,6 +868,7 @@ let needSetup = false;
monitor.kafkaProducerAllowAutoTopicCreation;
bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly;
bean.remote_browser = monitor.remote_browser;
bean.smtpSecurity = monitor.smtpSecurity;
bean.snmpVersion = monitor.snmpVersion;
bean.snmpOid = monitor.snmpOid;
bean.jsonPathOperator = monitor.jsonPathOperator;
@ -874,6 +877,12 @@ let needSetup = false;
bean.rabbitmqUsername = monitor.rabbitmqUsername;
bean.rabbitmqPassword = monitor.rabbitmqPassword;
bean.conditions = JSON.stringify(monitor.conditions);
bean.manual_status = monitor.manual_status;
// ping advanced options
bean.ping_numeric = monitor.ping_numeric;
bean.ping_count = monitor.ping_count;
bean.ping_per_request_timeout = monitor.ping_per_request_timeout;
bean.validate();

View file

@ -20,14 +20,14 @@ module.exports.apiKeySocketHandler = (socket) => {
checkLogin(socket);
let clearKey = nanoid(40);
let hashedKey = passwordHash.generate(clearKey);
let hashedKey = await passwordHash.generate(clearKey);
key["key"] = hashedKey;
let bean = await APIKey.save(key, socket.userID);
log.debug("apikeys", "Added API Key");
log.debug("apikeys", key);
// Append key ID and prefix to start of key seperated by _, used to get
// Append key ID and prefix to start of key separated by _, used to get
// correct hash when validating key.
let formattedKey = "uk" + bean.id + "_" + clearKey;
await sendAPIKeyList(socket);

View file

@ -14,7 +14,7 @@ module.exports.databaseSocketHandler = (socket) => {
checkLogin(socket);
callback({
ok: true,
size: Database.getSize(),
size: await Database.getSize(),
});
} catch (error) {
callback({

View file

@ -4,7 +4,7 @@ const { sendInfo } = require("../client");
const { checkLogin } = require("../util-server");
const GameResolver = require("gamedig/lib/GameResolver");
const { testChrome } = require("../monitor-types/real-browser-monitor-type");
const fs = require("fs");
const fsAsync = require("fs").promises;
const path = require("path");
let gameResolver = new GameResolver();
@ -90,17 +90,29 @@ module.exports.generalSocketHandler = (socket, server) => {
}
});
socket.on("getPushExample", (language, callback) => {
socket.on("getPushExample", async (language, callback) => {
try {
checkLogin(socket);
if (!/^[a-z-]+$/.test(language)) {
throw new Error("Invalid language");
}
} catch (e) {
callback({
ok: false,
msg: e.message,
});
return;
}
try {
let dir = path.join("./extra/push-examples", language);
let files = fs.readdirSync(dir);
let files = await fsAsync.readdir(dir);
for (let file of files) {
if (file.startsWith("index.")) {
callback({
ok: true,
code: fs.readFileSync(path.join(dir, file), "utf8"),
code: await fsAsync.readFile(path.join(dir, file), "utf8"),
});
return;
}

View file

@ -211,6 +211,10 @@ module.exports.statusPageSocketHandler = (socket) => {
relationBean.send_url = monitor.sendUrl;
}
if (monitor.url !== undefined) {
relationBean.custom_url = monitor.url;
}
await R.store(relationBean);
}

View file

@ -582,7 +582,7 @@ class UptimeCalculator {
let totalPing = 0;
let endTimestamp;
// Get the eariest timestamp of the required period based on the type
// Get the earliest timestamp of the required period based on the type
switch (type) {
case "day":
endTimestamp = key - 86400 * (num - 1);
@ -710,7 +710,7 @@ class UptimeCalculator {
let endTimestamp;
// Get the eariest timestamp of the required period based on the type
// Get the earliest timestamp of the required period based on the type
switch (type) {
case "day":
endTimestamp = key - 86400 * (num - 1);

View file

@ -113,10 +113,12 @@ class UptimeKumaServer {
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
UptimeKumaServer.monitorTypeList["smtp"] = new SMTPMonitorType();
UptimeKumaServer.monitorTypeList["group"] = new GroupMonitorType();
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
UptimeKumaServer.monitorTypeList["manual"] = new ManualMonitorType();
// Allow all CORS origins (polling) in development
let cors = undefined;
@ -552,8 +554,10 @@ const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor
const { TailscalePing } = require("./monitor-types/tailscale-ping");
const { DnsMonitorType } = require("./monitor-types/dns");
const { MqttMonitorType } = require("./monitor-types/mqtt");
const { SMTPMonitorType } = require("./monitor-types/smtp");
const { GroupMonitorType } = require("./monitor-types/group");
const { SNMPMonitorType } = require("./monitor-types/snmp");
const { MongodbMonitorType } = require("./monitor-types/mongodb");
const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
const { ManualMonitorType } = require("./monitor-types/manual");
const Monitor = require("./model/monitor");

View file

@ -1,7 +1,11 @@
const tcpp = require("tcp-ping");
const ping = require("@louislam/ping");
const { R } = require("redbean-node");
const { log, genSecret, badgeConstants } = require("../src/util");
const {
log, genSecret, badgeConstants,
PING_PACKET_SIZE_DEFAULT, PING_GLOBAL_TIMEOUT_DEFAULT,
PING_COUNT_DEFAULT, PING_PER_REQUEST_TIMEOUT_DEFAULT
} = require("../src/util");
const passwordHash = require("./password-hash");
const { Resolver } = require("dns");
const iconv = require("iconv-lite");
@ -19,6 +23,7 @@ const radiusClient = require("node-radius-client");
const redis = require("redis");
const oidc = require("openid-client");
const tls = require("tls");
const { exists } = require("fs");
const {
dictionaries: {
@ -47,13 +52,13 @@ exports.initJWTSecret = async () => {
jwtSecretBean.key = "jwtSecret";
}
jwtSecretBean.value = passwordHash.generate(genSecret());
jwtSecretBean.value = await passwordHash.generate(genSecret());
await R.store(jwtSecretBean);
return jwtSecretBean;
};
/**
* Decodes a jwt and returns the payload portion without verifying the jqt.
* Decodes a jwt and returns the payload portion without verifying the jwt.
* @param {string} jwt The input jwt as a string
* @returns {object} Decoded jwt payload object
*/
@ -62,15 +67,16 @@ exports.decodeJwt = (jwt) => {
};
/**
* Gets a Access Token form a oidc/oauth2 provider
* @param {string} tokenEndpoint The token URI form the auth service provider
* Gets an Access Token from an oidc/oauth2 provider
* @param {string} tokenEndpoint The token URI from the auth service provider
* @param {string} clientId The oidc/oauth application client id
* @param {string} clientSecret The oidc/oauth application client secret
* @param {string} scope The scope the for which the token should be issued for
* @param {string} authMethod The method on how to sent the credentials. Default client_secret_basic
* @param {string} scope The scope(s) for which the token should be issued for
* @param {string} audience The audience for which the token should be issued for
* @param {string} authMethod The method used to send the credentials. Default client_secret_basic
* @returns {Promise<oidc.TokenSet>} TokenSet promise if the token request was successful
*/
exports.getOidcTokenClientCredentials = async (tokenEndpoint, clientId, clientSecret, scope, authMethod = "client_secret_basic") => {
exports.getOidcTokenClientCredentials = async (tokenEndpoint, clientId, clientSecret, scope, audience, authMethod = "client_secret_basic") => {
const oauthProvider = new oidc.Issuer({ token_endpoint: tokenEndpoint });
let client = new oauthProvider.Client({
client_id: clientId,
@ -86,6 +92,10 @@ exports.getOidcTokenClientCredentials = async (tokenEndpoint, clientId, clientSe
if (scope) {
grantParams.scope = scope;
}
if (audience) {
grantParams.audience = audience;
}
return await client.grant(grantParams);
};
@ -118,20 +128,33 @@ exports.tcping = function (hostname, port) {
/**
* Ping the specified machine
* @param {string} hostname Hostname / address of machine
* @param {number} size Size of packet to send
* @param {string} destAddr Hostname / IP address of machine to ping
* @param {number} count Number of packets to send before stopping
* @param {string} sourceAddr Source address for sending/receiving echo requests
* @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames
* @param {number} size Size (in bytes) of echo request to send
* @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent
* @param {number} timeout Maximum time in seconds to wait for each response
* @returns {Promise<number>} Time for ping in ms rounded to nearest integer
*/
exports.ping = async (hostname, size = 56) => {
exports.ping = async (
destAddr,
count = PING_COUNT_DEFAULT,
sourceAddr = "",
numeric = true,
size = PING_PACKET_SIZE_DEFAULT,
deadline = PING_GLOBAL_TIMEOUT_DEFAULT,
timeout = PING_PER_REQUEST_TIMEOUT_DEFAULT,
) => {
try {
return await exports.pingAsync(hostname, false, size);
return await exports.pingAsync(destAddr, false, count, sourceAddr, numeric, size, deadline, timeout);
} catch (e) {
// If the host cannot be resolved, try again with ipv6
log.debug("ping", "IPv6 error message: " + e.message);
// As node-ping does not report a specific error for this, try again if it is an empty message with ipv6 no matter what.
if (!e.message) {
return await exports.pingAsync(hostname, true, size);
return await exports.pingAsync(destAddr, true, count, sourceAddr, numeric, size, deadline, timeout);
} else {
throw e;
}
@ -140,18 +163,35 @@ exports.ping = async (hostname, size = 56) => {
/**
* Ping the specified machine
* @param {string} hostname Hostname / address of machine to ping
* @param {string} destAddr Hostname / IP address of machine to ping
* @param {boolean} ipv6 Should IPv6 be used?
* @param {number} size Size of ping packet to send
* @param {number} count Number of packets to send before stopping
* @param {string} sourceAddr Source address for sending/receiving echo requests
* @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames
* @param {number} size Size (in bytes) of echo request to send
* @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent
* @param {number} timeout Maximum time in seconds to wait for each response
* @returns {Promise<number>} Time for ping in ms rounded to nearest integer
*/
exports.pingAsync = function (hostname, ipv6 = false, size = 56) {
exports.pingAsync = function (
destAddr,
ipv6 = false,
count = PING_COUNT_DEFAULT,
sourceAddr = "",
numeric = true,
size = PING_PACKET_SIZE_DEFAULT,
deadline = PING_GLOBAL_TIMEOUT_DEFAULT,
timeout = PING_PER_REQUEST_TIMEOUT_DEFAULT,
) {
return new Promise((resolve, reject) => {
ping.promise.probe(hostname, {
ping.promise.probe(destAddr, {
v6: ipv6,
min_reply: 1,
deadline: 10,
min_reply: count,
sourceAddr: sourceAddr,
numeric: numeric,
packetSize: size,
deadline: deadline,
timeout: timeout
}).then((res) => {
// If ping failed, it will set field to unknown
if (res.alive) {
@ -1062,3 +1102,17 @@ module.exports.axiosAbortSignal = (timeoutMs) => {
}
}
};
/**
* Async version of fs.existsSync
* @param {PathLike} path File path
* @returns {Promise<boolean>} True if file exists, false otherwise
*/
function fsExists(path) {
return new Promise(function (resolve, reject) {
exists(path, function (exists) {
resolve(exists);
});
});
}
module.exports.fsExists = fsExists;

View file

@ -7,11 +7,17 @@
class="beat-hover-area"
:class="{ 'empty': (beat === 0) }"
:style="beatHoverAreaStyle"
:title="getBeatTitle(beat)"
:aria-label="getBeatAriaLabel(beat)"
role="status"
tabindex="0"
@mouseenter="showTooltip(beat, $event)"
@mouseleave="hideTooltip"
@focus="showTooltip(beat, $event)"
@blur="hideTooltip"
>
<div
class="beat"
:class="{ 'empty': (beat === 0), 'down': (beat.status === 0), 'pending': (beat.status === 2), 'maintenance': (beat.status === 3) }"
:class="{ 'empty': (beat === 0 || beat === null || beat.status === null), 'down': (beat.status === DOWN), 'pending': (beat.status === PENDING), 'maintenance': (beat.status === MAINTENANCE) }"
:style="beatStyle"
/>
</div>
@ -24,13 +30,27 @@
<div v-if="$root.styleElapsedTime === 'with-line'" class="connecting-line"></div>
<div>{{ timeSinceLastBeat }}</div>
</div>
<!-- Custom Tooltip -->
<Tooltip
:visible="tooltipVisible"
:content="tooltipContent"
:x="tooltipX"
:y="tooltipY"
:position="tooltipPosition"
/>
</div>
</template>
<script>
import dayjs from "dayjs";
import { DOWN, UP, PENDING, MAINTENANCE } from "../util.ts";
import Tooltip from "./Tooltip.vue";
export default {
components: {
Tooltip,
},
props: {
/** Size of the heartbeat bar */
size: {
@ -46,6 +66,11 @@ export default {
heartbeatList: {
type: Array,
default: null,
},
/** Heartbeat bar days */
heartbeatBarDays: {
type: Number,
default: 0
}
},
data() {
@ -56,10 +81,25 @@ export default {
beatHoverAreaPadding: 4,
move: false,
maxBeat: -1,
// Tooltip data
tooltipVisible: false,
tooltipContent: null,
tooltipX: 0,
tooltipY: 0,
tooltipPosition: "below",
tooltipTimeoutId: null,
};
},
computed: {
/**
* Normalized heartbeatBarDays as a number
* @returns {number} Number of days for heartbeat bar
*/
normalizedHeartbeatBarDays() {
return Math.max(0, Math.min(365, Math.floor(this.heartbeatBarDays || 0)));
},
/**
* If heartbeatList is null, get it from $root.heartbeatList
* @returns {object} Heartbeat list
@ -80,6 +120,12 @@ export default {
if (!this.beatList) {
return 0;
}
// For configured ranges, no padding needed since we show all beats
if (this.normalizedHeartbeatBarDays > 0) {
return 0;
}
let num = this.beatList.length - this.maxBeat;
if (this.move) {
@ -98,8 +144,20 @@ export default {
return [];
}
// If heartbeat days is configured (not auto), data is already aggregated from server
if (this.normalizedHeartbeatBarDays > 0 && this.beatList.length > 0) {
// Show all beats from server - they are already properly aggregated
return this.beatList;
}
// Original logic for auto mode (heartbeatBarDays = 0)
let placeholders = [];
// Handle case where maxBeat is -1 (no limit)
if (this.maxBeat <= 0) {
return this.beatList;
}
let start = this.beatList.length - this.maxBeat;
if (this.move) {
@ -172,13 +230,17 @@ export default {
* @returns {string} The time elapsed in minutes or hours.
*/
timeSinceFirstBeat() {
if (this.normalizedHeartbeatBarDays === 1) {
return (this.normalizedHeartbeatBarDays * 24) + "h";
}
if (this.normalizedHeartbeatBarDays >= 2) {
return this.normalizedHeartbeatBarDays + "d";
}
// Need to calculate from actual data
const firstValidBeat = this.shortBeatList.at(this.numPadding);
const minutes = dayjs().diff(dayjs.utc(firstValidBeat?.time), "minutes");
if (minutes > 60) {
return (minutes / 60).toFixed(0) + "h";
} else {
return minutes + "m";
}
return minutes > 60 ? Math.floor(minutes / 60) + "h" : minutes + "m";
},
/**
@ -205,7 +267,7 @@ export default {
},
watch: {
beatList: {
handler(val, oldVal) {
handler() {
this.move = true;
setTimeout(() => {
@ -217,6 +279,10 @@ export default {
},
unmounted() {
window.removeEventListener("resize", this.resize);
// Clean up tooltip timeout
if (this.tooltipTimeoutId) {
clearTimeout(this.tooltipTimeoutId);
}
},
beforeMount() {
if (this.heartbeatList === null) {
@ -256,7 +322,23 @@ export default {
*/
resize() {
if (this.$refs.wrap) {
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatHoverAreaPadding * 2));
const newMaxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatHoverAreaPadding * 2));
// If maxBeat changed and we're in configured days mode, notify parent to reload data
if (newMaxBeat !== this.maxBeat && this.normalizedHeartbeatBarDays > 0) {
this.maxBeat = newMaxBeat;
// Find the closest parent with reloadHeartbeatData method (StatusPage)
let parent = this.$parent;
while (parent && !parent.reloadHeartbeatData) {
parent = parent.$parent;
}
if (parent && parent.reloadHeartbeatData) {
parent.reloadHeartbeatData(newMaxBeat);
}
} else {
this.maxBeat = newMaxBeat;
}
}
},
@ -267,7 +349,105 @@ export default {
* @returns {string} Beat title
*/
getBeatTitle(beat) {
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
if (!beat) {
return "";
}
// Show timestamp for all beats (both individual and aggregated)
return `${this.$root.datetime(beat.time)}${beat.msg ? ` - ${beat.msg}` : ""}`;
},
/**
* Get the aria-label for accessibility
* @param {object} beat Beat to get aria-label from
* @returns {string} Aria label
*/
getBeatAriaLabel(beat) {
switch (beat?.status) {
case DOWN:
return `Down at ${this.$root.datetime(beat.time)}`;
case UP:
return `Up at ${this.$root.datetime(beat.time)}`;
case PENDING:
return `Pending at ${this.$root.datetime(beat.time)}`;
case MAINTENANCE:
return `Maintenance at ${this.$root.datetime(beat.time)}`;
default:
return "No data";
}
},
/**
* Show custom tooltip
* @param {object} beat Beat data
* @param {Event} event Mouse event
* @returns {void}
*/
showTooltip(beat, event) {
if (beat === 0 || !beat) {
this.hideTooltip();
return;
}
// Clear any existing timeout
if (this.tooltipTimeoutId) {
clearTimeout(this.tooltipTimeoutId);
}
// Small delay for better UX
this.tooltipTimeoutId = setTimeout(() => {
this.tooltipContent = beat;
// Calculate position relative to viewport
const rect = event.target.getBoundingClientRect();
// Position relative to viewport
const x = rect.left + (rect.width / 2);
const y = rect.top;
// Check if tooltip would go off-screen and adjust position
const tooltipHeight = 80; // Approximate tooltip height
const viewportHeight = window.innerHeight;
const spaceAbove = y;
const spaceBelow = viewportHeight - y - rect.height;
if (spaceAbove > tooltipHeight && spaceBelow < tooltipHeight) {
// Show above - arrow points down
this.tooltipPosition = "above";
this.tooltipY = y - 10;
} else {
// Show below - arrow points up
this.tooltipPosition = "below";
this.tooltipY = y + rect.height + 10;
}
// Ensure tooltip doesn't go off the left or right edge
const tooltipWidth = 120; // Approximate tooltip width
let adjustedX = x;
if ((x - tooltipWidth / 2) < 10) {
adjustedX = tooltipWidth / 2 + 10;
} else if ((x + tooltipWidth / 2) > (window.innerWidth - 10)) {
adjustedX = window.innerWidth - tooltipWidth / 2 - 10;
}
this.tooltipX = adjustedX;
this.tooltipVisible = true;
}, 150);
},
/**
* Hide custom tooltip
* @returns {void}
*/
hideTooltip() {
if (this.tooltipTimeoutId) {
clearTimeout(this.tooltipTimeoutId);
this.tooltipTimeoutId = null;
}
this.tooltipVisible = false;
this.tooltipContent = null;
},
},

View file

@ -10,7 +10,7 @@
</div>
<div class="modal-body">
<div class="my-3 form-check">
<input id="show-clickable-link" v-model="monitor.isClickAble" class="form-check-input" type="checkbox" @click="toggleLink(monitor.group_index, monitor.monitor_index)" />
<input id="show-clickable-link" v-model="monitor.isClickAble" class="form-check-input" type="checkbox" data-testid="show-clickable-link" @click="toggleLink(monitor.group_index, monitor.monitor_index)" />
<label class="form-check-label" for="show-clickable-link">
{{ $t("Show Clickable Link") }}
</label>
@ -19,6 +19,16 @@
</div>
</div>
<!-- Custom URL -->
<template v-if="monitor.isClickAble">
<label for="customUrl" class="form-label">{{ $t("Custom URL") }}</label>
<input id="customUrl" :value="monitor.url" type="url" class="form-control" data-testid="custom-url-input" @input="e => changeUrl(monitor.group_index, monitor.monitor_index, e.target!.value)">
<div class="form-text mb-3">
{{ $t("customUrlDescription") }}
</div>
</template>
<button
class="btn btn-primary btn-add-group me-2"
@click="$refs.badgeGeneratorDialog.show(monitor.id, monitor.name)"
@ -29,7 +39,7 @@
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger" data-bs-dismiss="modal">
<button type="submit" class="btn btn-danger" data-bs-dismiss="modal" data-testid="monitor-settings-close">
{{ $t("Close") }}
</button>
</div>
@ -78,6 +88,7 @@ export default {
monitor_index: monitor.index,
group_index: group.index,
isClickAble: this.showLink(monitor),
url: monitor.element.url,
};
this.MonitorSettingDialog.show();
@ -110,6 +121,17 @@ export default {
}
return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode;
},
/**
* Toggle the value of sendUrl
* @param {number} groupIndex Index of group monitor is member of
* @param {number} index Index of monitor within group
* @param {string} value The new value of the url
* @returns {void}
*/
changeUrl(groupIndex, index, value) {
this.$root.publicGroupList[groupIndex].monitorList[index].url = value;
},
},
};
</script>

View file

@ -168,7 +168,8 @@ export default {
"waha": "WhatsApp (WAHA)",
"gtxmessaging": "GtxMessaging",
"Cellsynt": "Cellsynt",
"SendGrid": "SendGrid"
"SendGrid": "SendGrid",
"notifery": "Notifery"
};
// Put notifications here if it's not supported in most regions or its documentation is not in English

View file

@ -174,6 +174,38 @@ export default {
this.modal.show();
},
/**
* Show dialog to clone a proxy
* @param {number} proxyID ID of proxy to clone
* @returns {void}
*/
showClone(proxyID) {
if (proxyID) {
for (let proxy of this.$root.proxyList) {
if (proxy.id === proxyID) {
// Create a clone of the proxy data
this.proxy = {
protocol: proxy.protocol,
host: proxy.host,
port: proxy.port,
auth: proxy.auth,
username: proxy.username,
password: proxy.password,
active: proxy.active,
default: false, // Cloned proxy should not be default
applyExisting: false,
};
break;
}
}
}
// Set id to null to indicate this is a new proxy (clone)
this.id = null;
this.modal.show();
},
/**
* Submit form data for saving
* @returns {void}

View file

@ -66,6 +66,7 @@
v-if="editMode"
:class="{'link-active': true, 'btn-link': true}"
icon="cog" class="action me-3"
data-testid="monitor-settings"
@click="$refs.monitorSettingDialog.show(group, monitor)"
/>
</span>

View file

@ -4,7 +4,7 @@
<div v-if="selectedTags.length > 0" class="mb-2 p-1">
<tag
v-for="item in selectedTags"
:key="item.id"
:key="`${item.tag_id || item.id}-${item.value || ''}`"
:item="item"
:remove="deleteTag"
/>
@ -20,10 +20,20 @@
<font-awesome-icon class="me-1" icon="plus" /> {{ $t("Add") }}
</button>
</div>
<div ref="modal" class="modal fade" tabindex="-1">
<div ref="modal" class="modal fade" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<h4 v-if="stagedForBatchAdd.length > 0">{{ $t("Add Tags") }}</h4>
<div v-if="stagedForBatchAdd.length > 0" class="mb-3 staging-area" style="max-height: 150px; overflow-y: auto;">
<Tag
v-for="stagedTag in stagedForBatchAdd"
:key="stagedTag.keyForList"
:item="mapStagedTagToDisplayItem(stagedTag)"
:remove="() => unstageTag(stagedTag)"
/>
</div>
<vue-multiselect
v-model="newDraftTag.select"
class="mb-2"
@ -58,14 +68,11 @@
<div class="w-50 pe-2">
<input
v-model="newDraftTag.name" class="form-control"
:class="{'is-invalid': validateDraftTag.nameInvalid}"
:class="{'is-invalid': validateDraftTag.invalid && (validateDraftTag.messageKey === 'tagNameColorRequired' || validateDraftTag.messageKey === 'tagNameExists')}"
:placeholder="$t('Name')"
data-testid="tag-name-input"
@keydown.enter.prevent="onEnter"
/>
<div class="invalid-feedback">
{{ $t("Tag with this name already exist.") }}
</div>
</div>
<div class="w-50 ps-2">
<vue-multiselect
@ -104,27 +111,24 @@
<div class="mb-2">
<input
v-model="newDraftTag.value" class="form-control"
:class="{'is-invalid': validateDraftTag.valueInvalid}"
:class="{'is-invalid': validateDraftTag.invalid && validateDraftTag.messageKey === 'tagAlreadyOnMonitor'}"
:placeholder="$t('value (optional)')"
data-testid="tag-value-input"
@keydown.enter.prevent="onEnter"
/>
<div class="invalid-feedback">
{{ $t("Tag with this value already exist.") }}
</div>
</div>
<div class="mb-2">
<button
type="button"
class="btn btn-secondary float-end"
:disabled="processing || validateDraftTag.invalid"
data-testid="tag-submit-button"
@click.stop="addDraftTag"
>
{{ $t("Add") }}
</button>
<div v-if="validateDraftTag.invalid && validateDraftTag.messageKey" class="form-text text-danger mb-2">
{{ $t(validateDraftTag.messageKey, validateDraftTag.messageParams) }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click.stop="clearStagingAndCloseModal">{{ $t("Cancel") }}</button>
<button type="button" class="btn btn-outline-primary me-2" :disabled="processing || validateDraftTag.invalid" @click.stop="stageCurrentTag">
{{ $t("Add Another Tag") }}
</button>
<button type="button" class="btn btn-primary" :disabled="processing || (stagedForBatchAdd.length === 0 && validateDraftTag.invalid)" data-testid="add-tags-final-button" @click.stop="confirmAndCommitStagedTags">{{ $t("Done") }}</button>
</div>
</div>
</div>
</div>
@ -176,71 +180,146 @@ export default {
newTags: [],
/** @type {Tag[]} */
deleteTags: [],
/**
* @type {Array<object>} Holds tag objects staged for addition.
* Each object: { name, color, value, isNewSystemTag, systemTagId, keyForList }
*/
stagedForBatchAdd: [],
newDraftTag: {
name: null,
select: null,
color: null,
value: "",
invalid: true,
nameInvalid: false,
},
};
},
computed: {
tagOptions() {
const tagOptions = this.existingTags;
const tagOptions = [ ...this.existingTags ]; // Create a copy
// Add tags from newTags
for (const tag of this.newTags) {
if (!tagOptions.find(t => t.name === tag.name && t.color === tag.color)) {
tagOptions.push(tag);
}
}
// Add newly created system tags from staging area
for (const stagedTag of this.stagedForBatchAdd) {
if (stagedTag.isNewSystemTag) {
// Check if this system tag is already in the options
if (!tagOptions.find(t => t.name === stagedTag.name && t.color === stagedTag.color)) {
// Create a tag option object for the dropdown
tagOptions.push({
id: null, // Will be assigned when actually created
name: stagedTag.name,
color: stagedTag.color
});
}
}
}
return tagOptions;
},
selectedTags() {
return this.preSelectedTags.concat(this.newTags).filter(tag => !this.deleteTags.find(monitorTag => monitorTag.tag_id === tag.tag_id));
// Helper function to normalize tag values for comparison
const normalizeValue = (value) => {
if (value === null || value === undefined) {
return "";
}
return String(value).trim();
};
// Helper function to get tag ID from different structures
const getTagId = (tag) => tag.tag_id || tag.id;
return this.preSelectedTags.concat(this.newTags).filter(tag =>
!this.deleteTags.find(monitorTag => {
const tagIdMatch = getTagId(monitorTag) === getTagId(tag);
const valueMatch = normalizeValue(monitorTag.value) === normalizeValue(tag.value);
return tagIdMatch && valueMatch;
})
);
},
/**
* @returns {boolean} True if more new system tags can be staged, false otherwise.
*/
canStageMoreNewSystemTags() {
return true; // Always allow adding more tags, no limit
},
/**
* Provides the color options for the tag color selector.
* @returns {Array<object>} Array of color options.
*/
colorOptions() {
return colorOptions(this);
},
/**
* Validates the current draft tag based on several conditions.
* @returns {{invalid: boolean, messageKey: string|null, messageParams: object|null}} Object indicating validity, and a message key/params if invalid.
*/
validateDraftTag() {
let nameInvalid = false;
let valueInvalid = false;
let invalid = true;
if (this.deleteTags.find(tag => tag.name === this.newDraftTag.select?.name && tag.value === this.newDraftTag.value)) {
// Undo removing a Tag
nameInvalid = false;
valueInvalid = false;
invalid = false;
} else if (this.existingTags.filter(tag => tag.name === this.newDraftTag.name).length > 0 && this.newDraftTag.select == null) {
// Try to create new tag with existing name
nameInvalid = true;
invalid = true;
} else if (this.newTags.concat(this.preSelectedTags).filter(tag => (
tag.name === this.newDraftTag.select?.name && tag.value === this.newDraftTag.value
) || (
tag.name === this.newDraftTag.name && tag.value === this.newDraftTag.value
)).length > 0) {
// Try to add a tag with existing name and value
valueInvalid = true;
invalid = true;
} else if (this.newDraftTag.select != null) {
// Select an existing tag, no need to validate
invalid = false;
valueInvalid = false;
} else if (this.newDraftTag.color == null || this.newDraftTag.name === "") {
// Missing form inputs
nameInvalid = false;
invalid = true;
} else {
// Looks valid
invalid = false;
nameInvalid = false;
valueInvalid = false;
// If defining a new system tag (newDraftTag.select == null)
if (this.newDraftTag.select == null) {
if (!this.newDraftTag.name || this.newDraftTag.name.trim() === "" || !this.newDraftTag.color) {
// Keep button disabled, but don't show the explicit message for this case
return {
invalid: true,
messageKey: null,
messageParams: null,
};
}
if (this.tagOptions.find(opt => opt.name.toLowerCase() === this.newDraftTag.name.trim().toLowerCase())) {
return {
invalid: true,
messageKey: "tagNameExists",
messageParams: null,
};
}
}
// For any tag definition (new or existing system tag + value)
const draftTagName = this.newDraftTag.select ? this.newDraftTag.select.name : this.newDraftTag.name.trim();
const draftTagValue = this.newDraftTag.value ? this.newDraftTag.value.trim() : ""; // Treat null/undefined value as empty string for comparison
// Check if (name + value) combination already exists in this.stagedForBatchAdd
if (this.stagedForBatchAdd.find(staged => staged.name === draftTagName && staged.value === draftTagValue)) {
return {
invalid: true,
messageKey: "tagAlreadyStaged",
messageParams: null,
};
}
// Check if (name + value) combination already exists in this.selectedTags (final list on monitor)
// AND it's NOT an "undo delete"
const isUndoDelete = this.deleteTags.find(dTag =>
dTag.tag_id === (this.newDraftTag.select ? this.newDraftTag.select.id : null) &&
dTag.value === draftTagValue
);
if (!isUndoDelete && this.selectedTags.find(sTag => sTag.name === draftTagName && sTag.value === draftTagValue)) {
return {
invalid: true,
messageKey: "tagAlreadyOnMonitor",
messageParams: null,
};
}
// If an existing tag is selected at this point, it has passed all relevant checks
if (this.newDraftTag.select != null) {
return {
invalid: false,
messageKey: null,
messageParams: null,
};
}
// If it's a new tag definition, and it passed its specific checks, it's valid.
// (This also serves as a final default to valid if other logic paths were missed, though ideally covered above)
return {
invalid,
nameInvalid,
valueInvalid,
invalid: false,
messageKey: null,
messageParams: null,
};
},
},
@ -257,6 +336,9 @@ export default {
* @returns {void}
*/
showAddDialog() {
this.stagedForBatchAdd = [];
this.clearDraftTag();
this.getExistingTags();
this.modal.show();
},
/**
@ -300,37 +382,6 @@ export default {
return this.$root.theme === "light" ? "var(--bs-body-color)" : "inherit";
}
},
/**
* Add a draft tag
* @returns {void}
*/
addDraftTag() {
console.log("Adding Draft Tag: ", this.newDraftTag);
if (this.newDraftTag.select != null) {
if (this.deleteTags.find(tag => tag.name === this.newDraftTag.select.name && tag.value === this.newDraftTag.value)) {
// Undo removing a tag
this.deleteTags = this.deleteTags.filter(tag => !(tag.name === this.newDraftTag.select.name && tag.value === this.newDraftTag.value));
} else {
// Add an existing Tag
this.newTags.push({
id: this.newDraftTag.select.id,
color: this.newDraftTag.select.color,
name: this.newDraftTag.select.name,
value: this.newDraftTag.value,
new: true,
});
}
} else {
// Add new Tag
this.newTags.push({
color: this.newDraftTag.color.color,
name: this.newDraftTag.name.trim(),
value: this.newDraftTag.value,
new: true,
});
}
this.clearDraftTag();
},
/**
* Remove a draft tag
* @returns {void}
@ -341,10 +392,8 @@ export default {
select: null,
color: null,
value: "",
invalid: true,
nameInvalid: false,
// invalid: true, // Initial validation will be handled by computed prop
};
this.modal.hide();
},
/**
* Add a tag asynchronously
@ -386,7 +435,7 @@ export default {
*/
onEnter() {
if (!this.validateDraftTag.invalid) {
this.addDraftTag();
this.stageCurrentTag();
}
},
/**
@ -475,7 +524,119 @@ export default {
console.warn("Modal hide failed:", e);
}
}
}
this.stagedForBatchAdd = [];
},
/**
* Stages the current draft tag for batch addition.
* @returns {void}
*/
stageCurrentTag() {
if (this.validateDraftTag.invalid) {
return;
}
const isNew = this.newDraftTag.select == null;
const name = isNew ? this.newDraftTag.name.trim() : this.newDraftTag.select.name;
const color = isNew ? this.newDraftTag.color.color : this.newDraftTag.select.color;
const value = this.newDraftTag.value ? this.newDraftTag.value.trim() : "";
const stagedTagObject = {
name: name,
color: color,
value: value,
isNewSystemTag: isNew,
systemTagId: isNew ? null : this.newDraftTag.select.id,
keyForList: `staged-${Date.now()}-${Math.random().toString(36).substring(2, 15)}` // Unique key
};
this.stagedForBatchAdd.push(stagedTagObject);
this.clearDraftTag(); // Reset input fields for the next tag
},
/**
* Removes a tag from the staged list.
* @param {object} tagToUnstage The tag object to remove from staging.
* @returns {void}
*/
unstageTag(tagToUnstage) {
this.stagedForBatchAdd = this.stagedForBatchAdd.filter(tag => tag.keyForList !== tagToUnstage.keyForList);
},
/**
* Maps a staged tag object to the structure expected by the Tag component.
* @param {object} stagedTag The staged tag object.
* @returns {object} Object with name, color, value for the Tag component.
*/
mapStagedTagToDisplayItem(stagedTag) {
return {
name: stagedTag.name,
color: stagedTag.color,
value: stagedTag.value,
// id: stagedTag.keyForList, // Pass keyForList as id for the Tag component if it expects an id for display/keying internally beyond v-for key
};
},
/**
* Clears the staging list, draft inputs, and closes the modal.
* @returns {void}
*/
clearStagingAndCloseModal() {
this.stagedForBatchAdd = [];
this.clearDraftTag(); // Clears input fields
this.modal.hide();
},
/**
* Processes all staged tags, adds them to the monitor, and closes the modal.
* @returns {void}
*/
confirmAndCommitStagedTags() {
// Phase 1: If there's a currently valid newDraftTag that hasn't been staged yet,
// (e.g. user typed a full tag and directly clicked the footer "Add"), then stage it now.
// stageCurrentTag has its own check for validateDraftTag.invalid and will clear the draft.
if (!this.validateDraftTag.invalid) {
// Check if newDraftTag actually has content, to avoid staging an empty cleared draft.
// A valid draft implies it has content, but double-checking select or name is safer.
if (this.newDraftTag.select || (this.newDraftTag.name && this.newDraftTag.color)) {
this.stageCurrentTag();
}
}
// Phase 2: Process everything that is now in stagedForBatchAdd.
if (this.stagedForBatchAdd.length === 0) {
this.clearDraftTag(); // Ensure draft is clear even if nothing was committed
this.modal.hide();
return;
}
for (const sTag of this.stagedForBatchAdd) {
let isAnUndo = false; // Flag to track if this was an undo
// Check if it's an "undo delete"
if (sTag.systemTagId) { // Only existing system tags can be an undo delete
const undoDeleteIndex = this.deleteTags.findIndex(
dTag => dTag.tag_id === sTag.systemTagId && dTag.value === sTag.value
);
if (undoDeleteIndex > -1) {
this.deleteTags.splice(undoDeleteIndex, 1);
isAnUndo = true;
}
}
// Only add to newTags if it's not an "undo delete" operation.
// An "undo delete" means the tag is now considered active again from its previous state.
if (!isAnUndo) {
const tagObjectForNewTags = {
id: sTag.systemTagId, // This will be null for brand new system tags
color: sTag.color,
name: sTag.name,
value: sTag.value,
new: true, // As per plan, signals new to this monitor transaction
};
this.newTags.push(tagObjectForNewTags);
}
}
// newDraftTag should have been cleared if stageCurrentTag ran in Phase 1, or earlier.
// Call clearDraftTag again to be certain the form is reset before closing.
this.clearDraftTag();
this.modal.hide();
},
},
};
</script>

276
src/components/Tooltip.vue Normal file
View file

@ -0,0 +1,276 @@
<template>
<teleport to="body">
<div
v-if="content"
ref="tooltip"
class="tooltip-wrapper"
:style="tooltipStyle"
:class="{ 'tooltip-above': position === 'above' }"
>
<div class="tooltip-content">
<slot :content="content">
<!-- Default content if no slot provided -->
<div class="tooltip-status" :class="statusClass">
{{ statusText }}
</div>
<div class="tooltip-time">{{ timeText }}</div>
<div v-if="content?.msg" class="tooltip-message">{{ content.msg }}</div>
</slot>
</div>
<div class="tooltip-arrow" :class="{ 'arrow-above': position === 'above' }"></div>
</div>
</teleport>
</template>
<script>
import { DOWN, UP, PENDING, MAINTENANCE } from "../util.ts";
export default {
name: "Tooltip",
props: {
/** Whether tooltip is visible */
visible: {
type: Boolean,
default: false
},
/** Content object to display */
content: {
type: Object,
default: null
},
/** X position (viewport coordinates) */
x: {
type: Number,
default: 0
},
/** Y position (viewport coordinates) */
y: {
type: Number,
default: 0
},
/** Position relative to target element */
position: {
type: String,
default: "below",
validator: (value) => [ "above", "below" ].includes(value)
}
},
computed: {
tooltipStyle() {
return {
left: this.x + "px",
top: this.y + "px",
};
},
statusText() {
if (!this.content || this.content === 0) {
return this.$t("Unknown");
}
switch (this.content.status) {
case DOWN:
return this.$t("Down");
case UP:
return this.$t("Up");
case PENDING:
return this.$t("Pending");
case MAINTENANCE:
return this.$t("Maintenance");
default:
return this.$t("Unknown");
}
},
statusClass() {
if (!this.content || this.content === 0) {
return "status-empty";
}
switch (this.content.status) {
case DOWN:
return "status-down";
case UP:
return "status-up";
case PENDING:
return "status-pending";
case MAINTENANCE:
return "status-maintenance";
default:
return "status-unknown";
}
},
timeText() {
if (!this.content || this.content === 0) {
return "";
}
return this.$root.datetime(this.content.time);
},
}
};
</script>
<style lang="scss" scoped>
@import "../assets/vars.scss";
.tooltip-wrapper {
position: fixed;
z-index: 9999;
pointer-events: none;
transform: translateX(-50%);
.tooltip-content {
background: rgba(17, 24, 39, 0.95);
backdrop-filter: blur(8px);
border: 1px solid rgba(75, 85, 99, 0.3);
border-radius: 8px;
padding: 8px 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25);
min-width: 120px;
text-align: center;
position: relative;
&::before {
content: "";
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 14px;
height: 2px;
background: rgba(17, 24, 39, 0.95);
top: -1px;
}
.tooltip-status {
font-size: 16px;
font-weight: 600;
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 0.5px;
&.status-up {
color: $primary;
}
&.status-down {
color: $danger;
}
&.status-pending {
color: $warning;
}
&.status-maintenance {
color: $maintenance;
}
&.status-empty {
color: $secondary-text;
}
}
.tooltip-time {
color: #d1d5db;
font-size: 13px;
margin-bottom: 2px;
}
.tooltip-message {
color: #f3f4f6;
font-size: 12px;
margin-top: 4px;
padding-top: 4px;
border-top: 1px solid rgba(75, 85, 99, 0.3);
}
}
.tooltip-arrow {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 12px;
height: 6px;
overflow: hidden;
top: -6px;
&::before {
content: "";
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%) translateY(-50%) rotate(45deg);
width: 8px;
height: 8px;
background: rgba(17, 24, 39, 0.95);
border: 1px solid rgba(75, 85, 99, 0.3);
border-bottom: none;
border-right: none;
}
&.arrow-above {
top: auto;
bottom: -6px;
&::before {
top: 0%;
transform: translateX(-50%) translateY(-50%) rotate(225deg);
border: 1px solid rgba(75, 85, 99, 0.3);
border-bottom: none;
border-right: none;
}
}
}
// Smooth entrance animation
animation: tooltip-fade-in 0.2s $easing-out;
&.tooltip-above {
transform: translateX(-50%) translateY(-8px);
.tooltip-content::before {
top: auto;
bottom: -1px;
}
}
}
// Dark theme adjustments
.dark .tooltip-wrapper {
.tooltip-content {
background: rgba(31, 41, 55, 0.95);
border-color: rgba(107, 114, 128, 0.3);
&::before {
background: rgba(31, 41, 55, 0.95);
}
}
.tooltip-arrow {
&::before {
background: rgba(31, 41, 55, 0.95);
border-color: rgba(107, 114, 128, 0.3);
}
}
}
@keyframes tooltip-fade-in {
from {
opacity: 0;
transform: translateX(-50%) translateY(4px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
// Accessibility improvements
@media (prefers-reduced-motion: reduce) {
.tooltip-wrapper {
animation: none !important;
}
}
</style>

View file

@ -1,7 +1,10 @@
<template>
<div class="mb-3">
<label for="flashduty-integration-url" class="form-label">Integration Key</label>
<HiddenInput id="flashduty-integration-url" v-model="$parent.notification.flashdutyIntegrationKey" autocomplete="false"></HiddenInput>
<label for="flashduty-integration-url" class="form-label">{{ $t("FlashDuty Push URL") }} <span style="color: red;"><sup>*</sup></span></label>
<HiddenInput id="flashduty-integration-url" v-model="$parent.notification.flashdutyIntegrationKey" autocomplete="false" :placeholder="$t('FlashDuty Push URL Placeholder')" />
<div class="form-text">
<p><span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}</p>
</div>
<i18n-t tag="div" keypath="wayToGetFlashDutyKey" class="form-text">
<a href="https://flashcat.cloud/product/flashduty?from=kuma" target="_blank">{{ $t("here") }}</a>
</i18n-t>
@ -18,7 +21,6 @@
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,

View file

@ -18,7 +18,7 @@
{{ $t("matrixDesc1") }}
</p>
<i18n-t tag="p" keypath="matrixDesc2" style="margin-top: 8px;">
<code>curl -XPOST -d '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/v3/login"</code>.
<code>curl -XPOST --json '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/v3/login"</code>.
</i18n-t>
</div>
</template>

View file

@ -0,0 +1,49 @@
<template>
<div class="mb-3">
<label for="notifery-api-key" class="form-label">{{
$t("API Key")
}}</label>
<HiddenInput
id="notifery-api-key"
v-model="$parent.notification.notiferyApiKey"
:required="true"
autocomplete="new-password"
></HiddenInput>
</div>
<div class="mb-3">
<label for="notifery-title" class="form-label">{{ $t("Title") }}</label>
<input
id="notifery-title"
v-model="$parent.notification.notiferyTitle"
type="text"
class="form-control"
placeholder="Uptime Kuma Alert"
/>
</div>
<div class="mb-3">
<label for="notifery-group" class="form-label">{{ $t("Group") }}</label>
<input
id="notifery-group"
v-model="$parent.notification.notiferyGroup"
type="text"
class="form-control"
:placeholder="$t('Optional')"
/>
</div>
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
<a href="https://docs.notifery.com/api/event/" target="_blank">https://docs.notifery.com/api/event/</a>
</i18n-t>
</template>
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,
},
};
</script>

View file

@ -13,13 +13,20 @@
<div class="mb-3">
<label for="ntfy-priority" class="form-label">{{ $t("Priority") }}</label>
<input id="ntfy-priority" v-model="$parent.notification.ntfyPriority" type="number" class="form-control" required min="1" max="5" step="1">
<label for="ntfy-priority-down" class="form-label">{{ $t("ntfyPriorityDown") }}</label>
<input id="ntfy-priority-down" v-model="$parent.notification.ntfyPriorityDown" type="number" class="form-control" required min="1" max="5" step="1">
<div class="form-text">
<p v-if="$parent.notification.ntfyPriority >= 5">
<p v-if="$parent.notification.ntfyPriority == $parent.notification.ntfyPriorityDown && $parent.notification.ntfyPriority >= 5">
{{ $t("ntfyPriorityHelptextAllEvents") }}
</p>
<i18n-t v-else-if="$parent.notification.ntfyPriority > $parent.notification.ntfyPriorityDown" tag="p" keypath="ntfyPriorityHelptextPriorityHigherThanDown">
<code>DOWN</code>
<code>{{ $parent.notification.ntfyPriority }}</code>
<code>{{ $parent.notification.ntfyPriorityDown }}</code>
</i18n-t>
<i18n-t v-else tag="p" keypath="ntfyPriorityHelptextAllExceptDown">
<code>DOWN</code>
<code>{{ $parent.notification.ntfyPriority + 1 }}</code>
<code>{{ $parent.notification.ntfyPriorityDown }}</code>
</i18n-t>
</div>
</div>
@ -69,6 +76,11 @@ export default {
this.$parent.notification.ntfyPriority = 5;
}
// Setting down priority if it's undefined
if (typeof this.$parent.notification.ntfyPriorityDown === "undefined") {
this.$parent.notification.ntfyPriorityDown = 5;
}
// Handling notifications that added before 1.22.0
if (typeof this.$parent.notification.ntfyAuthenticationMethod === "undefined") {
if (!this.$parent.notification.ntfyusername) {

View file

@ -4,6 +4,7 @@ import AliyunSMS from "./AliyunSms.vue";
import Apprise from "./Apprise.vue";
import Bark from "./Bark.vue";
import Bitrix24 from "./Bitrix24.vue";
import Notifery from "./Notifery.vue";
import ClickSendSMS from "./ClickSendSMS.vue";
import CallMeBot from "./CallMeBot.vue";
import SMSC from "./SMSC.vue";
@ -149,6 +150,7 @@ const NotificationFormList = {
"ZohoCliq": ZohoCliq,
"SevenIO": SevenIO,
"whapi": Whapi,
"notifery": Notifery,
"waha": WAHA,
"gtxmessaging": GtxMessaging,
"Cellsynt": Cellsynt,

View file

@ -13,7 +13,8 @@
<li v-for="(proxy, index) in $root.proxyList" :key="index" class="list-group-item">
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("Default") }}</span><br>
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a> |
<a href="#" @click="$refs.proxyDialog.showClone(proxy.id)">{{ $t("Clone") }}</a>
</li>
</ul>

View file

@ -75,11 +75,20 @@ export function currentLocale() {
if (locale in messages) {
return locale;
}
// some locales are further specified such as "en-US".
// If we only have a generic locale for this, we can use it too
const genericLocale = locale.split("-")[0];
if (genericLocale in messages) {
return genericLocale;
// If the locale is a 2-letter code, we can try to find a regional variant
// e.g. "fr" may not be in the messages, but "fr-FR" is
if (locale.length === 2) {
const regionalLocale = `${locale}-${locale.toUpperCase()}`;
if (regionalLocale in messages) {
return regionalLocale;
}
} else {
// Some locales are further specified such as "en-US".
// If we only have a generic locale for this, we can use it too
const genericLocale = locale.slice(0, 2);
if (genericLocale in messages) {
return genericLocale;
}
}
}
return "en";

3
src/lang/ang.json Normal file
View file

@ -0,0 +1,3 @@
{
"languageName": "RTSP Username"
}

View file

@ -145,7 +145,7 @@
"Save": "يحفظ",
"Notifications": "إشعارات",
"Not available, please setup.": "غير متوفر من فضلك الإعداد.",
"Setup Notification": "إشعار الإعداد",
"Setup Notification": "إشعار جديد",
"Light": "نور",
"Dark": "داكن",
"Auto": "آلي",
@ -598,7 +598,7 @@
"Retry": "إعادة المحاولة",
"Topic": "عنوان",
"WeCom Bot Key": "WECOM BOT KEY",
"Setup Proxy": "وكيل الإعداد",
"Setup Proxy": "إعداد الوكيل",
"Proxy Protocol": "بروتوكول الوكيل",
"Proxy Server": "مخدم بروكسي",
"Proxy server has authentication": "خادم الوكيل لديه مصادقة",
@ -740,5 +740,26 @@
"leave blank for default body": "اترك فارغاً ليتم تعيين النص تلقائياً",
"emailTemplateServiceName": "اسم الخدمة",
"emailTemplateHostnameOrURL": "اسم المضيف أو عنوان URL",
"smspartnerPhoneNumber": "رقم الهاتف"
"smspartnerPhoneNumber": "رقم الهاتف",
"endDateTime": "تاريخ/وقت الإنتهاء",
"chromeExecutableAutoDetect": "كشف تلقائي",
"Edit Maintenance": "تعديل الصيانة",
"smspartnerSenderName": "اسم مرسل الرسائل القصيرة",
"telegramServerUrl": "(اختياري) رابط الخادم",
"emailCustomisableContent": "محتوى قابل للتخصيص",
"emailTemplateMsg": "رسالة الإشعار",
"Select message type": "اختر نوع الرسالة",
"Your User ID": "معرف المستخدم الخاص بك",
"setup a new monitor group": "إنشاء مجموعة مراقبة جديدة",
"Expires": "تاريخ الانتهاء",
"templateStatus": "الحالة",
"telegramUseTemplate": "استخدم قالب رسالة مخصص",
"telegramUseTemplateDescription": "إذا تم التفعيل، سيتم إرسال الرسالة باستخدام قالب مخصص.",
"now": "الآن",
"-year": "-سنة",
"and": "و",
"Add a domain": "إضافة نطاق",
"Remove domain": "إزالة النطاق '{0}'",
"time ago": "منذ {0}",
"startDateTime": "تاريخ/وقت البدء"
}

View file

@ -933,5 +933,43 @@
"threadForumPostID": "Трэд / ID паста",
"whatHappensAtForumPost": "Стварыць новы пост на форуме. Гэта НЕ размяшчае паведамленні ў існуючым пасце. Для публікацыі ў існуючай публікацыі выкарыстоўвайце \"{option}\"",
"now": "зараз",
"-year": "-год"
"-year": "-год",
"telegramServerUrl": "(Неабавязкова) URL сервера",
"telegramServerUrlDescription": "Каб зняць абмежаванні API бота Telegram або атрымаць доступ у заблакіраваных рэгіёнах (Кітай, Іран і інш.), націсніце {0} для атрымання дадатковай інфармацыі. Значэнне па змаўчанні: {1}",
"smspartnerPhoneNumber": "Нумар(ы) тэлефона",
"smspartnerSenderName": "Імя адпраўніка SMS",
"Command": "Каманда",
"mongodbCommandDescription": "Выканаць каманду MongoDB для базы даных. Інфармацыю пра даступныя каманды можна знайсці ў {documentation}",
"Community String": "Радок супольнасці",
"snmpCommunityStringHelptext": "Гэты радок выконвае функцыю пароля для аўтэнтыфікацыі і кантролю доступу да прылад з падтрымкай SNMP. Павінен адпавядаць канфігурацыі вашай SNMP-прылады.",
"OID (Object Identifier)": "OID (Ідэнтыфікатар аб’екта)",
"snmpOIDHelptext": "Увядзіце OID для датчыка або статусу, які вы хочаце маніторыць. Выкарыстоўвайце інструменты кіравання сеткай, такія як аглядальнікі MIB або праграмы SNMP, калі вы не ўпэўнены ў патрэбным OID.",
"SNMP Version": "Версія SNMP",
"Please enter a valid OID.": "Калі ласка, увядзіце карэктны OID.",
"wayToGetThreemaGateway": "Вы можаце зарэгістравацца для Threema Gateway {0}.",
"threemaRecipient": "Атрымальнік",
"templateServiceName": "назва сэрвісу",
"templateHostnameOrURL": "імя хоста або URL",
"templateStatus": "статус",
"telegramUseTemplate": "Выкарыстоўваць уласны шаблон паведамлення",
"telegramUseTemplateDescription": "Калі ўключана, паведамленне будзе адпраўлена з выкарыстаннем уласнага шаблона.",
"telegramTemplateFormatDescription": "Telegram дазваляе выкарыстоўваць розныя мовы разметкі для паведамленняў, падрабязнасці глядзіце ў Telegram {0}.",
"cacheBusterParamDescription": "Выпадкова згенераваны параметр для абыходу кэша.",
"Send rich messages": "Адправіць пашыраныя паведамленні",
"Bitrix24 Webhook URL": "URL вэбхука Bitrix24",
"wayToGetBitrix24Webhook": "Вы можаце стварыць вэбхук, выканаўшы крокі, апісаныя тут: {0}",
"bitrix24SupportUserID": "Увядзіце свой ідэнтыфікатар карыстальніка ў Bitrix24. Вы можаце даведацца ідэнтыфікатар па спасылцы, перайшоўшы ў профіль карыстальніка.",
"Condition": "Умова",
"aboutSlackUsername": "Змяняе адлюстраванае імя адпраўніка паведамлення. Калі вы хочаце згадваць кагосьці, уключыце гэта ў сяброўскае імя замест гэтага.",
"time ago": "{0} таму",
"Json Query Expression": "Выраз запыту JSON",
"and": "i",
"smspartnerApiurl": "Вы можаце знайсці свой ключ API на панэлі кіравання па адрасе {0}",
"smspartnerSenderNameInfo": "Павінна быць ад 3 да 11 звычайных сімвалаў",
"Message format": "Фармат паведамлення",
"ignoredTLSError": "Памылкі TLS/SSL былі праігнараваныя",
"shrinkDatabaseDescriptionSqlite": "Запусціць аперацыю {vacuum} для SQLite. {auto_vacuum} ужо ўключаны, але ён не дэфрагментуе базу даных і не перапакоўвае асобныя старонкі базы даных так, як гэта робіць каманда {vacuum}.",
"wayToGetDiscordThreadId": "Атрыманне ідэнтыфікатара тэмы або паведамлення на форуме аналагічнае атрыманню ідэнтыфікатара канала. Падрабязней пра тое, як атрымаць ідэнтыфікатары, чытайце тут {0}",
"smspartnerPhoneNumberHelptext": "Нумар павінен быць у міжнародным фармаце {0}, {1}. Некалькі нумароў павінны быць падзелены {2}",
"cacheBusterParam": "Дадайце параметр {0}"
}

View file

@ -640,8 +640,8 @@
"smseagleRecipient": "Получател(и) (при повече от един разделете със запетая)",
"smseagleToken": "API токен за достъп",
"smseagleUrl": "Вашият SMSEagle URL на устройството",
"smseagleEncoding": "Изпрати като Unicode",
"smseaglePriority": "Приоритет на съобщението (0-9, по подразбиране = 0)",
"smseagleEncoding": "Изпрати като Unicode (по подразбиране=GSM-7)",
"smseaglePriority": "Приоритет на съобщението (0-9, най-висок приоритет = 9)",
"IconUrl": "Икона URL адрес",
"webhookAdditionalHeadersTitle": "Допълнителни хедъри",
"webhookAdditionalHeadersDesc": "Задава допълнителни хедъри, изпратени с уеб куката. Всеки хедър трябва да бъде дефиниран като JSON ключ/стойност.",
@ -828,7 +828,7 @@
"noOrBadCertificate": "Няма/лош сертификат",
"Select": "Избери",
"selectedMonitorCount": "Избрано: {0}",
"wayToGetFlashDutyKey": "Можете да отидете на страница 'Channel -> (Select a Channel) -> Integrations -> Add a new integration' и да добавите 'UptimeKuma', за да получите 'push' адрес и да копирате ключа за интегриране в адреса. За повече информация, моля посетете",
"wayToGetFlashDutyKey": "За да интегрирате Uptime Kuma с Flashduty: Отидете на Канали > Изберете канал > Интеграции > Добавяне на нова интеграция, изберете Uptime Kuma и копирайте Push URL адреса.",
"PushDeer Server": "PushDeer сървър",
"pushDeerServerDescription": "Оставете празно, за да използвате официалния сървър",
"Check/Uncheck": "Постави/Премахни отметка",
@ -1118,5 +1118,62 @@
"wayToGetWahaSession": "От тази сесия WAHA изпраща известия до чат ID. Можете да го намерите в таблото за управление на WAHA.",
"telegramServerUrlDescription": "За премахване на API бот ограниченията за Telegram или за получаване на достъп в блокирани зони (Китай, Иран и др.). За повече информация щракнете върху {0}. По подразбиране: {1}",
"telegramServerUrl": "(По избор) URL адрес на сървъра",
"Font Twemoji by Twitter licensed under": "Шрифт Twemoji от Twitter, лицензиран под"
"Font Twemoji by Twitter licensed under": "Шрифт Twemoji от Twitter, лицензиран под",
"the smsplanet documentation": "документацията на smsplanet",
"Phone numbers": "Телефонни номера",
"Sender name": "Име на подател",
"smsplanetNeedToApproveName": "Трябва да бъде одобрен в клиентския панел",
"smsplanetApiToken": "Токен код за SMSPlanet API",
"smsplanetApiDocs": "Подробна информация, за получаване на API токен кодове, можете да намерите в {the_smsplanet_documentation}.",
"defaultFriendlyName": "Нов монитор",
"Use HTML for custom E-mail body": "Използвайте HTML за персонализирано тяло на имейл",
"smseagleGroupV2": "ID(та) на група от телефонния указател",
"smseagleContactV2": "ID(та) на контакти в телефонния указател",
"smseagleMsgTts": "Обаждане чрез Гласово синтезиран текст",
"smseagleTtsModel": "ID на модел за Гласово синтезиран текст",
"smseagleApiv2": "APIv2 (препоръчва се за нови интеграции)",
"SpugPush Template Code": "Код на шаблона",
"FlashDuty Push URL": "Push URL адрес",
"FlashDuty Push URL Placeholder": "Копирай от страницата за интегриране на предупреждения",
"pingCountLabel": "Максимален брой пакети",
"pingCountDescription": "Брой пакети за изпращане преди спиране",
"pingPerRequestTimeoutDescription": "Това е максималното време на изчакване (в секунди), преди да се приеме, че един пинг пакет е загубен",
"pingGlobalTimeoutDescription": "Общото време в секунди преди пинга да спре, независимо от изпратените пакети",
"pingIntervalAdjustedInfo": "Интервалът е коригиран въз основа на броя на пакетите, глобалното време за изчакване и времето за изчакване на пинг",
"smtpHelpText": "'SMTPS' тества дали SMTP/TLS работи; 'Игнорирай TLS' се свързва използвайки обикновен текст; 'STARTTLS' се свързва, като издава команда STARTTLS и проверява сертификата на сървъра. Нито един от тях не изпраща имейл.",
"smseagleMsgType": "Тип съобщение",
"smseagleMsgSms": "SMS съобщение (по подразбиране)",
"smseagleMsgRing": "Прозвъняване",
"smseagleMsgTtsAdvanced": "Разширено повикване чрез Гласово синтезиран текст",
"smseagleDuration": "Времетраене (в секунди)",
"smseagleApiType": "Версия на API",
"smseagleApiv1": "APIv1 (за съществуващи проекти и обратна съвместимост)",
"smseagleDocs": "Проверете документацията или наличността на APIv2: {0}",
"smseagleComma": "При няколко, трябва да бъдат разделени със запетая",
"pingNumericLabel": "Числен изход",
"pingNumericDescription": "Ако е отбелязано, ще се извеждат IP адреси вместо символни имена на хостове",
"pingGlobalTimeoutLabel": "Глобално време за изчакване",
"pingPerRequestTimeoutLabel": "Време за изчакване на пинг",
"Custom URL": "Персонализиран URL адрес",
"customUrlDescription": "Ще се използва като URL адрес, върху който може да се кликне, вместо този на монитора.",
"OneChatAccessToken": "Токен код за достъп до OneChat",
"OneChatUserIdOrGroupId": "OneChat ID на потребител или ID на група",
"OneChatBotId": "Бот ID на OneChat",
"Disable URL in Notification": "Деактивиране на URL адрес в известие",
"ntfyPriorityHelptextPriorityHigherThanDown": "Нормалният приоритет трябва да е по-висок от приоритета {0}. Приоритет {1} е по-висок от приоритета {2} на {0}",
"ntfyPriorityDown": "Приоритет за събития от тип Недостъпен",
"tagAlreadyOnMonitor": "Този етикет (име и стойност) вече е на монитора или чака добавяне.",
"tagNameExists": "Вече съществува системен етикет с това име. Изберете го от списъка или използвайте друго име.",
"Add Another Tag": "Добави друг етикет",
"Add Tags": "Добави етикети",
"tagAlreadyStaged": "Този етикет (име и стойност) вече е подготвен за тази група.",
"Staged Tags for Batch Add": "Подготвени етикети за групово добавяне",
"Clear Form": "Изчисти формата",
"pause": "Пауза",
"Happy Eyeballs algorithm": "Алгоритъм \"Happy Eyeballs\"",
"Ip Family": "IP Семейство",
"ipFamilyDescriptionAutoSelect": "Използва {happyEyeballs} за определяне на IP семейството.",
"Manual": "Ръковосдство",
"OAuth Audience": "OAuth аудитория",
"Optional: The audience to request the JWT for": "По желание: Аудиторията, за която да се поиска JWT"
}

View file

@ -724,8 +724,8 @@
"smseagleToken": "API Zugriffstoken",
"smseagleTo": "Telefonnummer(n)",
"smseagleUrl": "Ihre SMSEagle Geräte URL",
"smseagleEncoding": "Als Unicode senden",
"smseaglePriority": "Nachrichtenpriorität (0-9, Standard = 0)",
"smseagleEncoding": "Als Unicode senden (Standard=GSM-7)",
"smseaglePriority": "Nachrichtenpriorität (0-9, höchste Priorität = 9)",
"smseagleContact": "Telefonbuch Kontaktname(n)",
"confirmDeleteTagMsg": "Möchtest du dieses Tag wirklich löschen? Monitore, die mit diesem Tag verknüpft sind, werden nicht gelöscht.",
"wayToGetKookBotToken": "Erstelle eine Anwendung und erhalte den Bot-Token unter {0}",
@ -820,7 +820,7 @@
"nostrSender": "Privater Schlüssel des Absenders (nsec)",
"nostrRecipientsHelp": "npub-Format, eine pro Zeile",
"noOrBadCertificate": "Kein/schlechtes Zertifikat",
"wayToGetFlashDutyKey": "Gehe zu Channel -> (Wähle einen Channel) -> Integrationen -> Neue Integration hinzufügen', füge ein 'Uptime Kuma' hinzu, um eine Push-Adresse zu erhalten, und kopiere den Integrationsschlüssel in die Adresse. Für weitere Informationen besuche",
"wayToGetFlashDutyKey": "Um Uptime Kuma mit Flashduty zu integrieren: Gehe zu Channels > Wähle einen Channel > Integrationen > Füge eine neue Integration hinzu, wähle Uptime Kuma und kopiere die Push URL.",
"nostrRelays": "Nostr Relays",
"nostrRelaysHelp": "Eine Relay-URL pro Zeile",
"nostrRecipients": "Öffentliche Schlüssel des Empfängers (npub)",
@ -1115,5 +1115,62 @@
"wayToWriteWahaChatId": "Die Telefonnummer mit internationaler Vorwahl, ohne den anfänglichen Pluszeichen ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}). Die Benachrichtigungen werden an diese Chat-ID von der WAHA-Sitzung gesendet.",
"telegramServerUrl": "(Optional) Server URL",
"telegramServerUrlDescription": "Um die Telegram-Bot-API-Beschränkungen aufzuheben oder in gesperrten Gebieten (China, Iran usw.) Zugriff zu erhalten. Weitere Informationen findest du unter {0}. Standard: {1}",
"Font Twemoji by Twitter licensed under": "Schriftart Twemoji von Twitter lizenziert unter"
"Font Twemoji by Twitter licensed under": "Schriftart Twemoji von Twitter lizenziert unter",
"smsplanetApiToken": "Token für die SMSPlanet API",
"smsplanetApiDocs": "Ausführliche Informationen zum Erhalt von API-Tokens findest du in {the_smsplanet_documentation}.",
"the smsplanet documentation": "die smsplanet Dokumentation",
"Phone numbers": "Telefonnummern",
"Sender name": "Absendername",
"smsplanetNeedToApproveName": "Muss im Kundenpanel genehmigt werden",
"Use HTML for custom E-mail body": "HTML für benutzerdefinierten E-Mail-Text verwenden",
"smseagleApiv2": "APIv2 (empfohlen für neue Integrationen)",
"SpugPush Template Code": "Vorlage Code",
"FlashDuty Push URL": "Push-URL",
"FlashDuty Push URL Placeholder": "Kopie von der Seite für die Integration von Warnmeldungen",
"pingCountDescription": "Anzahl der zu sendenden Pakete vor dem Anhalten",
"pingNumericDescription": "Wenn diese Option aktiviert ist, werden IP-Adressen anstelle von symbolischen Hostnamen ausgegeben",
"pingPerRequestTimeoutDescription": "Dies ist die maximale Wartezeit (in Sekunden), bevor ein einzelnes Ping-Paket als verloren gilt",
"smtpHelpText": "'SMTPS' testet, ob SMTP/TLS funktioniert; 'Ignoriere TLS' stellt eine Verbindung über Klartext her; 'STARTTLS' stellt eine Verbindung her, gibt einen STARTTLS-Befehl aus und überprüft das Serverzertifikat. Keiner dieser Befehle sendet eine E-Mail.",
"defaultFriendlyName": "Neuer Monitor",
"smseagleGroupV2": "Telefonbuchgruppen-ID(s)",
"smseagleContactV2": "Telefonbuch-Kontakt-ID(s)",
"smseagleMsgType": "Nachrichtentyp",
"smseagleMsgSms": "SMS-Nachricht (Standard)",
"smseagleMsgRing": "Ringruf",
"smseagleMsgTts": "Text-zu-Sprache-Anruf",
"smseagleMsgTtsAdvanced": "Text-zu-Sprache Erweiterter Anruf",
"smseagleDuration": "Dauer (in Sekunden)",
"smseagleTtsModel": "Text-zu-Sprache-Modell-ID",
"smseagleApiType": "API-Version",
"smseagleApiv1": "APIv1 (für bestehende Projekte und Abwärtskompatibilität)",
"smseagleDocs": "Dokumentation oder APIv2-Verfügbarkeit prüfen: {0}",
"smseagleComma": "Mehrere müssen durch Komma getrennt werden",
"pingCountLabel": "Maximale Pakete",
"pingNumericLabel": "Numerische Ausgabe",
"pingGlobalTimeoutLabel": "Globale Zeitüberschreitung",
"pingGlobalTimeoutDescription": "Gesamtzeit in Sekunden bis zum Ende des Pings, unabhängig von den gesendeten Paketen",
"pingPerRequestTimeoutLabel": "Zeitüberschreitung pro Ping",
"pingIntervalAdjustedInfo": "Das Intervall wird anhand der Paketanzahl, des globalen Timeouts und des Timeouts pro Ping angepasst",
"Custom URL": "Benutzerdefinierte URL",
"customUrlDescription": "Wird als klickbare URL anstelle der des Monitors verwendet.",
"OneChatAccessToken": "OneChat Access Token",
"OneChatUserIdOrGroupId": "OneChat Benutzer-ID oder Gruppen-ID",
"OneChatBotId": "OneChat Bot-ID",
"Disable URL in Notification": "URL in der Benachrichtigung deaktivieren",
"Ip Family": "IP-Familie",
"ipFamilyDescriptionAutoSelect": "Verwendet die {happyEyeballs} zur Bestimmung der IP-Familie.",
"Happy Eyeballs algorithm": "Happy Eyeballs Algorithmus",
"Add Another Tag": "Weiteres Tag hinzufügen",
"Manual": "Manuell",
"ntfyPriorityHelptextPriorityHigherThanDown": "Die reguläre Priorität sollte höher sein als die Priorität {0}. Priorität {1} ist höher als {0} Priorität {2}",
"ntfyPriorityDown": "Priorität für DOWN-Ereignisse",
"Add Tags": "Tags hinzufügen",
"tagAlreadyOnMonitor": "Dieses Tag (Name und Wert) befindet sich bereits auf dem Monitor oder muss noch hinzugefügt werden.",
"tagAlreadyStaged": "Dieses Tag (Name und Wert) wurde für dieses Batch bereits bereitgestellt.",
"tagNameExists": "Ein System-Tag mit diesem Namen existiert bereits. Wähle es aus der Liste aus oder verwende einen anderen Namen.",
"Clear Form": "Formular leeren",
"pause": "Pause",
"Staged Tags for Batch Add": "Bereitgestellte Tags für Batch-Hinzufügen",
"OAuth Audience": "OAuth Zielgruppe",
"Optional: The audience to request the JWT for": "Optional: Die Zielgruppe, für die das JWT angefordert werden soll"
}

View file

@ -695,8 +695,8 @@
"smseagleToken": "API-Zugriffstoken",
"smseagleUrl": "Ihre SMSEagle-Geräte-URL",
"Kook": "Kook",
"smseagleEncoding": "Als Unicode senden",
"smseaglePriority": "Nachrichtenpriorität (0-9, Standard = 0)",
"smseagleEncoding": "Als Unicode senden (Standard=GSM-7)",
"smseaglePriority": "Nachrichtenpriorität (0-9, höchste Priorität = 9)",
"Google Analytics ID": "Google Analytics ID",
"Edit Tag": "bearbeite Tag",
"Server Address": "Server Adresse",
@ -830,7 +830,7 @@
"nostrRelaysHelp": "Eine Relay-URL pro Zeile",
"nostrSender": "Privater Schlüssel des Absenders (nsec)",
"gamedigGuessPortDescription": "Der vom Valve Server Query Protocol verwendete Port kann sich vom Port des Clients unterscheiden. Versuche dies, wenn der Monitor keine Verbindung zum Server herstellen kann.",
"wayToGetFlashDutyKey": "Gehe zu Channel -> (Wähle einen Channel) -> Integrationen -> Neue Integration hinzufügen', füge ein 'Uptime Kuma' hinzu, um eine Push-Adresse zu erhalten, und kopiere den Integrationsschlüssel in die Adresse. Für weitere Informationen besuche",
"wayToGetFlashDutyKey": "Um Uptime Kuma mit Flashduty zu integrieren: Gehe zu Channels > Wähle einen Channel > Integrationen > Füge eine neue Integration hinzu, wähle Uptime Kuma und kopiere die Push URL.",
"timeoutAfter": "Zeitüberschreitung nach {0} Sekunden",
"styleElapsedTimeShowWithLine": "Anzeigen (mit Zeile)",
"styleElapsedTime": "Verstrichene Zeit unter der Prüfintervallleiste",
@ -944,7 +944,7 @@
"whapiRecipient": "Telefonnummer / Kontakt-ID / Gruppen-ID",
"API URL": "API URL",
"wayToWriteWhapiRecipient": "Die Rufnummer mit der internationalen Vorwahl, aber ohne das Pluszeichen am Anfang ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}).",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Gib entweder den Hostnamen des Servers ein, mit dem eine Verbindung hergestellt werden soll, oder {localhost}, wenn ein {local_mta} verwendet werden soll.",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Gib entweder den Hostnamen des Servers ein, mit dem eine Verbindung hergestellt werden soll, oder {localhost}, wenn ein {local_mta} verwendet werden soll",
"locally configured mail transfer agent": "Lokal konfigurierter Mail-Transfer-Agent",
"Mentioning": "Erwähnung",
"Don't mention people": "Keine Personen erwähnen",
@ -1118,5 +1118,62 @@
"wayToWriteWahaChatId": "Die Telefonnummer mit internationaler Vorwahl, ohne den anfänglichen Pluszeichen ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}). Die Benachrichtigungen werden an diese Chat-ID von der WAHA-Sitzung gesendet.",
"telegramServerUrlDescription": "Um die Telegram-Bot-API-Beschränkungen aufzuheben oder in gesperrten Gebieten (China, Iran usw.) Zugriff zu erhalten. Weitere Informationen findest du unter {0}. Standard: {1}",
"telegramServerUrl": "(Optional) Server URL",
"Font Twemoji by Twitter licensed under": "Schriftart Twemoji von Twitter lizenziert unter"
"Font Twemoji by Twitter licensed under": "Schriftart Twemoji von Twitter lizenziert unter",
"the smsplanet documentation": "die smsplanet Dokumentation",
"Phone numbers": "Telefonnummern",
"Sender name": "Absendername",
"smsplanetNeedToApproveName": "Muss im Kundenpanel genehmigt werden",
"smsplanetApiToken": "Token für die SMSPlanet API",
"smsplanetApiDocs": "Ausführliche Informationen zum Erhalt von API-Tokens findest du in {the_smsplanet_documentation}.",
"defaultFriendlyName": "Neuer Monitor",
"Use HTML for custom E-mail body": "HTML für benutzerdefinierten E-Mail-Text verwenden",
"smseagleApiv1": "APIv1 (für bestehende Projekte und Abwärtskompatibilität)",
"SpugPush Template Code": "Vorlage Code",
"FlashDuty Push URL": "Push-URL",
"pingCountLabel": "Maximale Pakete",
"pingNumericDescription": "Wenn diese Option aktiviert ist, werden IP-Adressen anstelle von symbolischen Hostnamen ausgegeben",
"pingPerRequestTimeoutDescription": "Dies ist die maximale Wartezeit (in Sekunden), bevor ein einzelnes Ping-Paket als verloren gilt",
"smtpHelpText": "'SMTPS' testet, ob SMTP/TLS funktioniert; 'Ignoriere TLS' stellt eine Verbindung über Klartext her; 'STARTTLS' stellt eine Verbindung her, gibt einen STARTTLS-Befehl aus und überprüft das Serverzertifikat. Keiner dieser Befehle sendet eine E-Mail.",
"OneChatUserIdOrGroupId": "OneChat Benutzer-ID oder Gruppen-ID",
"Disable URL in Notification": "URL in der Benachrichtigung deaktivieren",
"smseagleGroupV2": "Telefonbuchgruppen-ID(s)",
"smseagleContactV2": "Telefonbuch-Kontakt-ID(s)",
"smseagleMsgType": "Nachrichtentyp",
"smseagleMsgSms": "SMS-Nachricht (Standard)",
"smseagleMsgRing": "Ringruf",
"smseagleMsgTts": "Text-zu-Sprache-Anruf",
"smseagleMsgTtsAdvanced": "Text-zu-Sprache Erweiterter Anruf",
"smseagleDuration": "Dauer (in Sekunden)",
"smseagleTtsModel": "Text-zu-Sprache-Modell-ID",
"smseagleApiType": "API-Version",
"smseagleApiv2": "APIv2 (empfohlen für neue Integrationen)",
"smseagleDocs": "Dokumentation oder APIv2-Verfügbarkeit prüfen: {0}",
"smseagleComma": "Mehrere müssen durch Komma getrennt werden",
"FlashDuty Push URL Placeholder": "Kopie von der Seite für die Integration von Warnmeldungen",
"pingCountDescription": "Anzahl der zu sendenden Pakete vor dem Anhalten",
"pingNumericLabel": "Numerische Ausgabe",
"pingGlobalTimeoutLabel": "Globale Zeitüberschreitung",
"pingGlobalTimeoutDescription": "Gesamtzeit in Sekunden bis zum Ende des Pings, unabhängig von den gesendeten Paketen",
"pingPerRequestTimeoutLabel": "Zeitüberschreitung pro Ping",
"pingIntervalAdjustedInfo": "Das Intervall wird anhand der Paketanzahl, des globalen Timeouts und des Timeouts pro Ping angepasst",
"Custom URL": "Benutzerdefinierte URL",
"customUrlDescription": "Wird als klickbare URL anstelle der des Monitors verwendet.",
"OneChatAccessToken": "OneChat Access Token",
"OneChatBotId": "OneChat Bot-ID",
"Ip Family": "IP-Familie",
"Happy Eyeballs algorithm": "Happy Eyeballs Algorithmus",
"ipFamilyDescriptionAutoSelect": "Verwendet die {happyEyeballs} zur Bestimmung der IP-Familie.",
"Manual": "Manuell",
"ntfyPriorityHelptextPriorityHigherThanDown": "Die reguläre Priorität sollte höher sein als die Priorität {0}. Priorität {1} ist höher als {0} Priorität {2}",
"ntfyPriorityDown": "Priorität für DOWN-Ereignisse",
"Add Tags": "Tags hinzufügen",
"tagAlreadyOnMonitor": "Dieses Tag (Name und Wert) befindet sich bereits auf dem Monitor oder muss noch hinzugefügt werden.",
"tagAlreadyStaged": "Dieses Tag (Name und Wert) wurde für dieses Batch bereits bereitgestellt.",
"tagNameExists": "Ein System-Tag mit diesem Namen existiert bereits. Wähle es aus der Liste aus oder verwende einen anderen Namen.",
"Clear Form": "Formular leeren",
"Add Another Tag": "Weiteres Tag hinzufügen",
"Staged Tags for Batch Add": "Bereitgestellte Tags für Batch-Hinzufügen",
"pause": "Pause",
"OAuth Audience": "OAuth Zielgruppe",
"Optional: The audience to request the JWT for": "Optional: Die Zielgruppe, für die das JWT angefordert werden soll"
}

View file

@ -64,6 +64,7 @@
"Expected Value": "Expected Value",
"Json Query Expression": "Json Query Expression",
"Friendly Name": "Friendly Name",
"defaultFriendlyName": "New Monitor",
"URL": "URL",
"Hostname": "Hostname",
"Host URL": "Host URL",
@ -187,9 +188,13 @@
"Show URI": "Show URI",
"Tags": "Tags",
"Add New Tag": "Add New Tag",
"Add Tags": "Add Tags",
"Add New below or Select...": "Add New below or Select…",
"Tag with this name already exist.": "Tag with this name already exists.",
"Tag with this value already exist.": "Tag with this value already exists.",
"tagAlreadyOnMonitor": "This tag (name and value) is already on the monitor or pending addition.",
"tagAlreadyStaged": "This tag (name and value) is already staged for this batch.",
"tagNameExists": "A system tag with this name already exists. Select it from the list or use a different name.",
"color": "Color",
"value (optional)": "value (optional)",
"Gray": "Gray",
@ -840,6 +845,8 @@
"ntfyAuthenticationMethod": "Authentication Method",
"ntfyPriorityHelptextAllEvents": "All events are sent with the maximum priority",
"ntfyPriorityHelptextAllExceptDown": "All events are sent with this priority, except {0}-events, which have a priority of {1}",
"ntfyPriorityHelptextPriorityHigherThanDown": "Regular priority should be higher than {0} priority. Priority {1} is higher than {0} priority {2}",
"ntfyPriorityDown": "Priority for DOWN-events",
"ntfyUsernameAndPassword": "Username and Password",
"twilioAccountSID": "Account SID",
"twilioApiKey": "Api Key (optional)",
@ -894,8 +901,10 @@
"noGroupMonitorMsg": "Not Available. Create a Group Monitor First.",
"Close": "Close",
"Request Body": "Request Body",
"wayToGetFlashDutyKey": "You can go to Channel -> (Select a Channel) -> Integrations -> Add a new integration' page, add a 'Uptime Kuma' to get a push address, copy the Integration Key in the address. For more information, please visit",
"wayToGetFlashDutyKey": "To integrate Uptime Kuma with Flashduty: Go to Channels > Select a channel > Integrations > Add a new integration, choose Uptime Kuma, and copy the Push URL.",
"FlashDuty Severity": "Severity",
"FlashDuty Push URL": "Push URL",
"FlashDuty Push URL Placeholder": "Copy from the alerting integration page",
"nostrRelays": "Nostr relays",
"nostrRelaysHelp": "One relay URL per line",
"nostrSender": "Sender Private Key (nsec)",
@ -1013,6 +1022,8 @@
"Client ID": "Client ID",
"Client Secret": "Client Secret",
"OAuth Scope": "OAuth Scope",
"OAuth Audience": "OAuth Audience",
"Optional: The audience to request the JWT for": "Optional: The audience to request the JWT for",
"Optional: Space separated list of scopes": "Optional: Space separated list of scopes",
"Go back to home page.": "Go back to home page.",
"No tags found.": "No tags found.",
@ -1073,6 +1084,18 @@
"rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.",
"SendGrid API Key": "SendGrid API Key",
"Separate multiple email addresses with commas": "Separate multiple email addresses with commas",
"pingCountLabel": "Max Packets",
"pingCountDescription": "Number of packets to send before stopping",
"pingNumericLabel": "Numeric Output",
"pingNumericDescription": "If checked, IP addresses will be output instead of symbolic hostnames",
"pingGlobalTimeoutLabel": "Global Timeout",
"pingGlobalTimeoutDescription": "Total time in seconds before ping stops, regardless of packets sent",
"pingPerRequestTimeoutLabel": "Per-Ping Timeout",
"pingPerRequestTimeoutDescription": "This is the maximum waiting time (in seconds) before considering a single ping packet lost",
"pingIntervalAdjustedInfo": "Interval adjusted based on packet count, global timeout and per-ping timeout",
"smtpHelpText": "'SMTPS' tests that SMTP/TLS is working; 'Ignore TLS' connects over plaintext; 'STARTTLS' connects, issues a STARTTLS command and verifies the server certificate. None of these send an email.",
"Custom URL": "Custom URL",
"customUrlDescription": "Will be used as the clickable URL instead of the monitor's one.",
"OneChatAccessToken": "OneChat Access Token",
"OneChatUserIdOrGroupId": "OneChat User ID or Group ID",
"OneChatBotId": "OneChat Bot ID",
@ -1094,5 +1117,13 @@
"Phone numbers": "Phone numbers",
"Sender name": "Sender name",
"smsplanetNeedToApproveName": "Needs to be approved in the client panel",
"Disable URL in Notification": "Disable URL in Notification"
"Disable URL in Notification": "Disable URL in Notification",
"Ip Family": "IP Family",
"ipFamilyDescriptionAutoSelect": "Uses the {happyEyeballs} for determining the IP family.",
"Happy Eyeballs algorithm": "Happy Eyeballs algorithm",
"Add Another Tag": "Add Another Tag",
"Staged Tags for Batch Add": "Staged Tags for Batch Add",
"Clear Form": "Clear Form",
"pause": "Pause",
"Manual": "Manual"
}

View file

@ -503,8 +503,8 @@
"onebotMessageType": "Tipo de Mensaje OneBot",
"smseagleRecipientType": "Tipo de destinatario",
"smseagleRecipient": "Destinatario(s) (multiples deben separarse por comas)",
"smseagleEncoding": "Enviar como Unicode",
"smseaglePriority": "Prioridad del mensaje (0-9, predeterminado = 0)",
"smseagleEncoding": "Enviar como Unicode (por defecto=GSM-7)",
"smseaglePriority": "Prioridad del mensaje (0-9, mas alta prioridad = 9)",
"stackfield": "Stackfield",
"Leave blank to use a shared sender number.": "Dejar en blanco para usar un número de remitente compartido.",
"Octopush API Version": "Versión API Octopush",
@ -829,7 +829,7 @@
"Badge value (For Testing only.)": "Valor de la insignia (Solo para pruebas.)",
"Enable Kafka Producer Auto Topic Creation": "Habilitar la Creación Automática de Temas del Productor de Kafka",
"noGroupMonitorMsg": "No disponible. Cree primero un monitor de grupo.",
"wayToGetFlashDutyKey": "Puedes ir a Canal -> (Seleccionar un Canal) -> Integraciones -> Agregar una nueva integración, agregar un 'Uptime Kuma' para obtener una dirección de envío, copiar la Clave de Integración en la dirección. Para más información, por favor visita",
"wayToGetFlashDutyKey": "Para integrar Uptime Kuma con Flashduty: Ir a Canal -> (Seleccionar un Canal) -> Integraciones -> Agregar una nueva integración, elegir 'Uptime Kuma' y copia la URL Push.",
"gamedigGuessPort": "Gamedig: Adivinar el puerto",
"gamedigGuessPortDescription": "El puerto utilizado por Valve Server Query Protocol puede ser diferente del puerto del cliente. Pruebe esto si el monitor no puede conectarse a su servidor.",
"twilioApiKey": "Clave de la API (opcional)",
@ -1087,5 +1087,26 @@
"Custom sound to override default notification sound": "Sonidos personalizados prevalecen sobre los sonidos por defecto de las notificaciones",
"The phone number of the recipient in E.164 format.": "El número de teléfono del receptor en formato E.164.",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Las notificaciones sensibles en el tiempo se enviarán inmediatamente, incluso si el dispositivo está en modo no molestar.",
"Arcade": "Arcade"
"Arcade": "Arcade",
"defaultFriendlyName": "Nuevo Monitor",
"telegramUseTemplate": "Usar una plantilla de mensaje personalizado",
"telegramUseTemplateDescription": "Si esta habilitado, el mensaje sera enviado usando una plantilla personalizada.",
"telegramServerUrlDescription": "Para aumentar las limitaciones de la API de bots de Telegram o ganar acceso en áreas bloqueadas (China, Irán, etc). Para mas información click {0}. Por defecto: {1}",
"smseagleApiType": "Versión de la API",
"telegramServerUrl": "(Opcional) URL del servidor",
"tagAlreadyOnMonitor": "Esta etiqueta (nombre y valor) ya están en el monitor o están pendientes de ser agregadas.",
"tagAlreadyStaged": "Esta etiqueta (nombre y valor) ya esta montada para este lote.",
"Add Tags": "Agregar etiquetas",
"tagNameExists": "Una etiqueta de sistema con este nombre ya existe. Seleccionala de la lista o usa un nombre diferente.",
"telegramTemplateFormatDescription": "Telegram permite usar diferentes lenguajes de marcado para los mensajes, ver telegram {0} para detalles específicos.",
"Use HTML for custom E-mail body": "Usar HTML para un cuerpo de E-mail personalizado",
"smseagleMsgType": "Tipo de mensaje",
"smseagleMsgSms": "Mensaje por SMS (por defecto)",
"smseagleMsgTts": "Llamada Voz-a-texto",
"smseagleDuration": "Duración (en segundos)",
"smseagleApiv1": "APIv1 (para proyectos existentes y retrocompatibilidad)",
"smseagleApiv2": "APIv2 (recomendada para nuevas integraciones)",
"templateServiceName": "Nombre del servicio",
"templateHostnameOrURL": "Nombre del host o URL",
"templateStatus": "Estado"
}

View file

@ -683,7 +683,7 @@
"Affected Monitors": "Kaltetutako monitoreak",
"Pick Affected Monitors...": "Hautatu kaltetutako monitoreak…",
"deleteMaintenanceMsg": "Ziur zaude mantentze lan hau ezabatu nahi duzula?",
"smseagleEncoding": "Unicode gisa bidali",
"smseagleEncoding": "Unicode gisa bidali (default=GSM-7)",
"Add Another": "Gehitu beste bat",
"Google Analytics ID": "Google Analytics IDa",
"Edit Tag": "Editatu etiketa",
@ -695,5 +695,56 @@
"lunaseaUserID": "Erabiltzaile IDa",
"pagertreeDoNothing": "Ez egin ezer",
"Separate multiple email addresses with commas": "Banatu email helbideak koma ikurrekin",
"settingUpDatabaseMSG": "Datubasea ezartzen. Denbora pixka bat iraun dezake, pazientzia eduki."
"settingUpDatabaseMSG": "Datubasea ezartzen. Denbora pixka bat iraun dezake, pazientzia eduki.",
"pause": "Gelditu",
"Ip Family": "IP familia",
"Clear Form": "Garbitu formularioa",
"Manual": "Eskuzkoa",
"templateMsg": "Jakinarazpenaren mezua",
"infiniteRetention": "Ezarri 0 atxikipen infinitua lortzeko.",
"strategyManual": "Aktibatu/desaktibatu eskuz",
"Home Assistant URL": "Home Assistant URLa",
"lastDay4": "Hilabeteko azken 4. eguna",
"smseagleApiType": "API bertsioa",
"defaultFriendlyName": "Monitorizazio berria",
"threemaRecipientTypeIdentityFormat": "8 karaktere",
"threemaRecipientTypePhone": "Telefono zenbakia",
"Group ID": "Talde IDa",
"Custom URL": "URL pertsonalizatua",
"Plain Text": "Testu laua",
"telegramServerUrl": "(Hautazkoa) Zerbitzari Url-a",
"Notify Channel": "Jakinarazpen kanala",
"Add Tags": "Gehitu etiketak",
"Search monitored sites": "Bilatu monitorizatutako guneak",
"lastDay2": "Hilabeteko azken 2. eguna",
"lastDay3": "Hilabeteko azken 3. eguna",
"Server Timezone": "Zerbitzari ordu zona",
"chromeExecutable": "Chrome/Chromium exekutagarria",
"Clone Monitor": "Klonatu monitorizazioa",
"Add a domain": "Gehitu domeinu bat",
"smseagleDuration": "Iraupena (segundotan)",
"smseagleMsgType": "Mezu mota",
"smseagleMsgSms": "Sms mezua (defektuzkoa)",
"ntfyAuthenticationMethod": "Autentifikazio metodoa",
"lunaseaDeviceID": "Gailu IDa",
"threemaRecipientTypeEmail": "Email helbidea",
"New Group": "Talde berria",
"Group Name": "Talde izena",
"OAuth2: Client Credentials": "OAuth2: bezero kredentzialak",
"Authentication Method": "Autentifikazio metodoa",
"Client ID": "Bezero IDa",
"Client Secret": "Bezero gakoa",
"Go back to home page.": "Itzuli hasiera ordura.",
"No tags found.": "Ez da etiketarik aurkitu.",
"Disable URL in Notification": "Desgaitu URLa jakunarazpenean",
"Sender name": "Bidaltzaile izena",
"Phone numbers": "Telefono zenbakiak",
"Message Template": "Mezu txantiloia",
"Template Format": "Txantiloi formatua",
"Add Another Tag": "Gehitu beste etiketa",
"templateServiceName": "Zerbitzu izena",
"templateStatus": "egoera",
"webhookBodyCustomOption": "Pertsonalizatutako mezu gorputza",
"telegramSendSilentlyDescription": "Bidali mezuak isilik. Erabiltzaileak soinurik gabeko jakinarazpena jasoko du.",
"setupDatabaseMariaDB": "Konektatu kanpo MariaDB datu base batekin. Datu base konexioaren informazioa ezarri behar da."
}

View file

@ -236,7 +236,7 @@
"matrixDesc1": "با مراجعه به بخش پیشرفته تنظیمات اتاق در کلاینت Matrix خود می توانید آیدی داخلی اتاق را بیابید. باید شبیه \"!QMdRCpUIfLwsfjxye6:home.server\" باشد.",
"wayToGetPagerDutyKey": "با رفتن به Service -> Service Directory -> (Select a Service) -> Integrations -> Add integration می توانید این مورد را دریافت کنید. در اینجا می توانید \"Events API V2\" را جستجو کنید. اطلاعات بیشتر در {0}",
"smseagleRecipientType": "نوع گیرنده",
"smseagleEncoding": "ارسال به صورت یونیکد",
"smseagleEncoding": "ارسال به صورت یونیکد (پیشفرض=GSM-7)",
"Leave blank to use a shared sender number.": "برای استفاده از شماره فرستنده مشترک، آن را خالی بگذارید.",
"onebotSafetyTips": "برای امنیت، میبایستی توکن دسترسی اضافه کنید",
"Custom Monitor Type": "نوع مانیتور سفارشی",
@ -603,7 +603,7 @@
"smseagleContact": "نام(های) تماس دفترچه تلفن",
"smseagleRecipient": "گیرنده(های) (چند مورد باید با کاما از هم جدا شوند)",
"smseagleUrl": "URL دستگاه SMSEagle شما",
"smseaglePriority": "اولویت پیام (0-9، پیش فرض = 0)",
"smseaglePriority": "اولویت پیام (9-0, بالاترین اولویت = 9)",
"Recipient Number": "شماره گیرنده",
"From Name/Number": "از نام/شماره",
"Octopush API Version": "نسخه Octopush API",
@ -761,7 +761,7 @@
"Enter the list of brokers": "لیست بروکر هارا وارد کنید",
"Enable Kafka Producer Auto Topic Creation": "فعال سازی ایجاپ موضوع اتوماتیک تهیه کننده",
"Secret AccessKey": "کلید محرمانه AccessKey",
"wayToGetFlashDutyKey": "می توانید به کانال -> (انتخاب یک کانال) -> یکپارچه سازی -> یکپارچه سازی جدید بروید، یک \"Uptime Kuma\" را برای دریافت یک آدرس پوش اضافه کنید، کلید یکپارچه سازی را در آدرس کپی کنید. برای اطلاعات بیشتر لطفا مراجعه کنید به",
"wayToGetFlashDutyKey": "برای یکپارچه‌سازی Uptime Kuma با Flashduty: به بخش کانال‌ها بروید > یک کانال را انتخاب کنید > یکپارچه‌سازی‌ها > افزودن یک یکپارچه‌سازی جدید، گزینه Uptime Kuma را انتخاب کنید و آدرس Push را کپی کنید.",
"showCertificateExpiry": "نمایش زمان به پایان رسیدن اعتبار سرتیفیکیت",
"gamedigGuessPortDescription": "پورت مورد استفاده توسط پروتکل Query Valve Server ممکن است با پورت مشتری متفاوت باشد. اگر مانیتور نمی تواند به سرور شما متصل شود، این را امتحان کنید.",
"invertKeywordDescription": "دنبال کلمات کلیدی ناموجود باشید تا آنهایی که موجود است.",
@ -1037,13 +1037,13 @@
"Guitar": "گیتار",
"Pop": "پاپ",
"From": "از",
"telegramServerUrl": "(اختیاری) آدرس سرور",
"telegramServerUrl": "آدرس سرور (اختیاری)",
"telegramServerUrlDescription": "برای کاهش محدودیت‌های بات تلگرام یا دسترسی در مناطقی که تلگرام فیلتر شده است (مثل ایران یا چین و ...). برای اطلاعات بیشتر {0} را ببینید. مقدار پیشفرض: {1}",
"Alphanumerical string and hyphens only": "فقط حروف الفبا، اعداد و -",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "اعلان‌های حساس به زمان در لحظه ارسال خواهند شد، حتی اگر دستگاه در حالت بدون مزاحمت قرار داشته باشد.",
"rabbitmqNodesRequired": "لطفا گره‌های این پایش‌گر را تنظیم کنید.",
"rabbitmqNodesRequired": "لطفا node های این مانیتور را تنظیم کنید.",
"RabbitMQ Password": "گذرواژه RabbitMQ",
"RabbitMQ Nodes": "گره‌های مدیریت RabbitMQ",
"RabbitMQ Nodes": "node های مدیریت RabbitMQ",
"rabbitmqHelpText": "برای پایش، لازم است افزونه مدیریت (Management) در RabbitMQ را فعال کنید. برای اطلاعات بیشتر به {rabitmq_documentation} مراجعه کنید.",
"wayToWriteWahaChatId": "شماره موبایل در قالب بین‌المللی و بدون علامت مثبت ابتدایی ({0})، شناسه مخاطب ({1}) یا شناسه گروه ({2}). اعلان‌ها از نشست WAHA به این شناسه گفتگو ارسال خواهند شد.",
"wahaSession": "نشست",
@ -1055,7 +1055,7 @@
"Fail": "شکست",
"Custom sound to override default notification sound": "نوای دلخواه به جای نوای پیشفرض اعلان",
"Time Sensitive (iOS Only)": "حساس به زمان (فقط iOS)",
"Can be found on:": "در {0} یافت میشود",
"Can be found on:": "در {0} پیدا میشود",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "شناسه ارسال کننده متنی و در صورتی که میخواهید پاسخ‌ها را دریافت کنید، شماره موبایل در قالب E.164.",
"rabbitmqNodesDescription": "آدرس گره‌های مدیریت RabbitMQ را به همراه پروتکل و شماره پورت وارد کنید. مثال: {0}",
"RabbitMQ Username": "نام کاربری RabbitMQ",
@ -1068,5 +1068,70 @@
"templateStatus": "وضعیت",
"telegramUseTemplate": "استفاده از قالب پیام دلخواه",
"telegramUseTemplateDescription": "در صورت فعال‌سازی، پیام با قالب دلخواه ارسال خواهد شد.",
"telegramTemplateFormatDescription": "در تلگرام امکان استفاده از زبان‌های نشانه‌گذاری مختلفی وجود دارد، برای جزئیات بیشتر {0} را ببینید."
"telegramTemplateFormatDescription": "در تلگرام امکان استفاده از زبان‌های نشانه‌گذاری مختلفی وجود دارد، برای جزئیات بیشتر {0} را ببینید.",
"defaultFriendlyName": "مانیتور جدید",
"shrinkDatabaseDescriptionSqlite": "اجرای دستور {vacuum} برای پایگاه داده SQLite. گزینه {auto_vacuum} به‌صورت پیش‌فرض فعال است، اما مانند {vacuum} عمل یکپارچه‌سازی و بازآرایی صفحات پایگاه داده را انجام نمی‌دهد.",
"Use HTML for custom E-mail body": "استفاده از HTML برای محتوای سفارشی ایمیل",
"smseagleGroupV2": "شناسه(های) گروه دفترچه تلفن",
"smseagleTtsModel": "شناسه مدل تبدیل متن به گفتار",
"smseagleApiv2": "APIv2 (پیشنهاد شده برای یکپارچه‌سازی‌های جدید)",
"rabbitmqNodesInvalid": "لطفاً از یک آدرس URL کامل (که با 'http' شروع می‌شود) برای node های RabbitMQ استفاده کنید.",
"pingNumericDescription": "اگر فعال شود، آدرس‌های IP به‌جای نام‌های نمادین هاست نمایش داده خواهند شد",
"pingPerRequestTimeoutDescription": "این بیشترین زمان انتظار (بر حسب ثانیه) است پیش از آن‌که یک پکت پینگ به‌عنوان گم‌شده (packet lost) در نظر گرفته شود",
"smtpHelpText": "'SMTPS' بررسی می‌کند که SMTP/TLS به‌درستی کار می‌کند؛ 'Ignore TLS' اتصال را به‌صورت متن ساده (plaintext) برقرار می‌کند؛ 'STARTTLS' ابتدا اتصال برقرار کرده، سپس فرمان STARTTLS را ارسال می‌کند و سرتیفیکت سرور را بررسی می‌نماید. هیچ‌یک از این حالت‌ها ایمیلی ارسال نمی‌کنند.",
"smseagleContactV2": "شناسه(های) مخاطب دفترچه تلفن",
"smseagleMsgType": "نوع پیام",
"smseagleMsgSms": "پیامک (پیش‌فرض)",
"smseagleMsgRing": "شماره تماس",
"smseagleMsgTtsAdvanced": "تماس پیشرفته با تبدیل متن به گفتار",
"smseagleMsgTts": "تماس با تبدیل متن به گفتار",
"smseagleDuration": "مدت زمان (بر حسب ثانیه)",
"smseagleApiType": "نسخه API",
"smseagleApiv1": "APIv1 (برای پروژه‌های موجود و سازگاری با نسخه‌های قبلی)",
"smseagleDocs": "بررسی مستندات یا در دسترس بودن APIv2: {0}",
"smseagleComma": "چند مقدار باید با کاما (,) از هم جدا شوند",
"SpugPush Template Code": "کد قالب",
"FlashDuty Push URL": "آدرس Push",
"FlashDuty Push URL Placeholder": "از صفحه یکپارچه‌سازی هشدارها کپی کنید",
"Send rich messages": "ارسال rich messages",
"Arcade": "Arcade (آرکید)",
"Harp": "Harp (چنگ)",
"pingCountLabel": "حداکثر پکت ها",
"pingCountDescription": "تعداد پکت هایی که قبل از توقف باید ارسال شوند",
"pingNumericLabel": "خروجی عددی",
"pingGlobalTimeoutLabel": "تایم اوت کلی",
"pingGlobalTimeoutDescription": "زمان کلی به ثانیه قبل از توقف پینگ، صرف‌نظر از تعداد پکت های ارسال‌شده",
"pingPerRequestTimeoutLabel": "تایم اوت برای هر پینگ",
"pingIntervalAdjustedInfo": "فاصله زمانی بر اساس تعداد پکت ها، تایم اوت کلی و تایم اوت برای هر پینگ تنظیم می‌شود",
"Custom URL": "آدرس سفارشی",
"customUrlDescription": "به‌جای آدرس مانیتور، به‌عنوان لینک قابل کلیک استفاده خواهد شد.",
"OneChatAccessToken": "توکن دسترسی OneChat",
"OneChatUserIdOrGroupId": "شناسه کاربر یا شناسه گروه OneChat",
"OneChatBotId": "شناسه بات OneChat",
"YZJ Robot Token": "توکن ربات YZJ",
"Disable URL in Notification": "غیرفعال‌سازی URL در اعلان",
"Font Twemoji by Twitter licensed under": "فونت Twemoji ساخت Twitter تحت لایسنس زیر منتشر شده است",
"smsplanetApiDocs": "اطلاعات دقیق درباره دریافت توکن‌های API را می‌توانید در {the_smsplanet_documentation} بیابید.",
"the smsplanet documentation": "مستندات SMSPlanet",
"Phone numbers": "شماره های تماس",
"Sender name": "نام ارسال کننده",
"smsplanetNeedToApproveName": "نیاز به تأیید در پنل کاربری دارد",
"smsplanetApiToken": "توکن برای API سرویس SMSPlanet",
"wayToGetWahaApiKey": "کلید API همان مقدار environment variable WHATSAPP_API_KEY است که هنگام اجرای WAHA استفاده کرده‌اید.",
"wayToGetWahaApiUrl": "آدرس URL اینستنس WAHA شما.",
"SendGrid API Key": "کلید API سرویس SendGrid",
"Add Tags": "افزودن برچسب ها",
"tagAlreadyOnMonitor": "این برچسب (نام و مقدار) قبلاً روی مانیتور قرار دارد یا در حال اضافه شدن است.",
"tagNameExists": "یک برچسب سیستم با این نام قبلاً وجود دارد. آن را از فهرست انتخاب کنید یا از نام متفاوتی استفاده کنید.",
"Staged Tags for Batch Add": "برچسب‌های آماده برای افزودن به مجموعه",
"ntfyPriorityHelptextPriorityHigherThanDown": "اولویت عادی باید بالاتر از اولویت {0} باشد. اولویت {1} بالاتر از اولویت {0} اولویت {2} است",
"ntfyPriorityDown": "اولویت برای ایونت های DOWN",
"tagAlreadyStaged": "این برچسب (نام و مقدار) قبلاً برای این مجموعه آماده شده است.",
"Add Another Tag": "افزودن برچسب دیگر",
"Clear Form": "پاک کردن فرم",
"pause": "توقف",
"Happy Eyeballs algorithm": "الگوریتم Happy Eyeballs",
"Ip Family": "خانواده IP",
"ipFamilyDescriptionAutoSelect": "از {happyEyeballs} برای تعیین خانواده IP استفاده می‌کند.",
"Manual": "راهنما"
}

View file

@ -605,8 +605,8 @@
"smseagleRecipient": "Vastaanottaja(t) (jos useita, ne on erotettava pilkulla)",
"smseagleToken": "API-käyttöoikeustunnus",
"smseagleUrl": "SMSEagle-laitteesi URL-osoite",
"smseagleEncoding": "Lähetä Unicodena",
"smseaglePriority": "Viestin prioriteetti (0-9, oletus = 0)",
"smseagleEncoding": "Lähetä Unicodena (normaalisti=GSM-7)",
"smseaglePriority": "Viestin prioriteetti (0-9, korkein oletus = 0)",
"stackfield": "Stackfield",
"Recipient Number": "Vastaanottajan numero",
"From Name/Number": "Nimestä/numerosta",
@ -1104,7 +1104,7 @@
"wahaChatId": "Viesti ID (Puhelinnumero / Yhteystieto ID / Ryhmä ID)",
"Template Format": "Malli Muotoilu",
"wayToGetWahaApiUrl": "Sinun WAHA instanssin URL.",
"YZJ Webhook URL": "YZJ Webhook URL",
"YZJ Webhook URL": "YZJ Webhook URL-osoite",
"telegramServerUrl": "(Valinnainen) Palvelin Url",
"telegramServerUrlDescription": "Telegramin bot-api-rajoitusten poistamiseksi tai pääsyn saamiseksi estetyille alueille (Kiina, Iran jne.). Saat lisätietoja napsauttamalla {0}. Oletus: {1}",
"Message Template": "Viesti Malli",
@ -1116,5 +1116,38 @@
"templateStatus": "tila",
"telegramUseTemplate": "Käytä mukautettua viesti mallia",
"telegramUseTemplateDescription": "Jos aktivoitu, viesti lähetetään käyttämällä mukautettua mallia.",
"telegramTemplateFormatDescription": "Telegram sallii erilaisten merkintäkielien käytön viesteissä, katso Telegram {0} tarkempia tietoja."
"telegramTemplateFormatDescription": "Telegram sallii erilaisten merkintäkielien käytön viesteissä, katso Telegram {0} tarkempia tietoja.",
"smsplanetApiToken": "SMSPlanet API:n tunnus",
"smsplanetApiDocs": "Yksityiskohtaiset tiedot API-tunnusten hankkimisesta löytyvät osoitteesta {the_smsplanet_documentation}.",
"the smsplanet documentation": "smsplanetin dokumentaatio",
"Phone numbers": "Puhelinnumerot",
"Sender name": "Lähettäjän nimi",
"smsplanetNeedToApproveName": "On hyväksyttävä asiakaspaneelissa",
"Use HTML for custom E-mail body": "Käytä HTML:llää mukautettuihin sähköposteihin.",
"smseagleDocs": "Tarkista dokumentaatio APIv2 saatavuudelle: {0}",
"defaultFriendlyName": "Uusi monitori",
"smseagleMsgType": "Viestin tyyppi",
"smseagleMsgSms": "SMS viesti (oletus)",
"smseagleMsgRing": "Ring puhelu",
"smseagleMsgTts": "Teksti puheeksi puhelu",
"smseagleMsgTtsAdvanced": "Teksti puheeksi edistynyt puhelu",
"smseagleDuration": "Aika (sekuntteina)",
"smseagleTtsModel": "Teksti puheeksi mallin tunniste ID",
"pingCountLabel": "Suurin määrä paketeille",
"Add Tags": "Lisää tageja",
"tagAlreadyOnMonitor": "Tämä tagi (nimi ja arvo) on jo monitorissa tai odottamassa lisäystä.",
"tagAlreadyStaged": "Tämä tagi (nimi ja arvo) on jo lavastettu tälle erälle.",
"tagNameExists": "Systeemin tagi tällä nimellä on jo olemassa. Valitse se listasta tai käytä eri nimeä.",
"smseagleGroupV2": "Puhelinluettelon ryhmän tunnukset",
"smseagleContactV2": "Puhelinluettelon yhteydenotto ID:t",
"smseagleApiType": "API versio",
"smseagleApiv1": "APIv1 (olemassa oleville projekteille ja taaksepäin yhteensopivuudelle)",
"smseagleApiv2": "APIv2 (suositeltu uusimmille lisäosille)",
"smseagleComma": "Useat on erotettava toisistaan pilkulla",
"SpugPush Template Code": "Mallipohjan koodi",
"ntfyPriorityHelptextPriorityHigherThanDown": "Normaalin prioriteetin tulisi olla korkeampi kuin {0} prioriteetti. Prioriteetti {1} on korkeampi kuin {0} prioriteetti {2}",
"ntfyPriorityDown": "DOWN-tapahtumien prioriteetti",
"FlashDuty Push URL": "Pushaamiseen URL-osoite",
"FlashDuty Push URL Placeholder": "Kopioi hälytysintegraatiosivulta",
"pingCountDescription": "Numeroina määrä paketteja, jotka lähetetään ennen lopettamista."
}

View file

@ -397,8 +397,8 @@
"smseagleRecipient": "Destinataire(s) (les multiples doivent être séparés par une virgule)",
"smseagleToken": "Jeton d'accès à l'API",
"smseagleUrl": "L'URL de votre appareil SMSEagle",
"smseagleEncoding": "Envoyer en Unicode",
"smseaglePriority": "Priorité des messages (0-9, par défaut = 0)",
"smseagleEncoding": "Envoyer en Unicode (default=GSM-7)",
"smseaglePriority": "Priorité du message (0 à 9, la priorité la plus élevée = 9)",
"stackfield": "Stackfield",
"Customize": "Personnaliser",
"Custom Footer": "Pied de page personnalisé",
@ -592,7 +592,7 @@
"Domain": "Domaine",
"Workstation": "Poste de travail",
"disableCloudflaredNoAuthMsg": "Vous êtes en mode No Auth, un mot de passe n'est pas nécessaire.",
"trustProxyDescription": "Faire confiance aux en-têtes 'X-Forwarded-*'. Si vous souhaitez obtenir la bonne adresse IP client et que votre Uptime Kuma se situe derrière un proxy (comme nginx ou Apache) vous devez l'activer.",
"trustProxyDescription": "Faire confiance aux en-têtes 'X-Forwarded-*'. Si vous souhaitez obtenir la bonne adresse IP client et que votre Uptime Kuma se situe derrière un proxy (comme nginx ou Apache), vous devez l'activer.",
"wayToGetLineNotifyToken": "Vous pouvez obtenir un jeton d'accès auprès de {0}",
"Examples": "Exemples",
"Home Assistant URL": "URL vers Home Assistant",
@ -831,7 +831,7 @@
"noOrBadCertificate": "Pas/Mauvais certificat",
"pushDeerServerDescription": "Laissez le champ vide pour utiliser le serveur officiel",
"FlashDuty Severity": "Gravité",
"wayToGetFlashDutyKey": "Vous pouvez aller dans Canal -> (Sélectionner un canal) -> Intégrations -> Ajouter une nouvelle page d'intégration, ajouter un « Uptime Kuma » pour obtenir une adresse push, copier la clé d'intégration dans l'adresse. Pour plus d'informations, veuillez visiter",
"wayToGetFlashDutyKey": "Pour intégrer Uptime Kuma avec Flashduty : allez dans Canaux > sélectionnez un canal > Intégrations > Ajouter une nouvelle intégration, choisissez Uptime Kuma, puis copiez lURL Push.",
"Request Timeout": "Délai d'expiration de la demande",
"timeoutAfter": "Délai dépassé après {0} secondes",
"gamedigGuessPort": "Gamedig : Devinez le port",
@ -1118,5 +1118,62 @@
"wayToWriteWahaChatId": "Le numéro de téléphone avec le préfixe international, mais sans le signe plus ({0}), l'identifiant de contact ({1}) ni l'identifiant de groupe ({2}). Les notifications sont envoyées à cet identifiant de chat depuis la session WAHA.",
"telegramServerUrlDescription": "Pour lever les limitations de lAPI des bots Telegram ou accéder aux zones bloquées (Chine, Iran, etc.). Pour plus dinformations, cliquez sur {0}. Par défaut : {1}",
"telegramServerUrl": "(Facultatif) URL du serveur",
"Font Twemoji by Twitter licensed under": "La police Twemoji de Twitter est sous licence"
"Font Twemoji by Twitter licensed under": "La police Twemoji de Twitter est sous licence",
"the smsplanet documentation": "la documentation de smsplanet",
"Phone numbers": "Numéros de téléphone",
"Sender name": "Nom de l'expéditeur",
"smsplanetNeedToApproveName": "Doit être approuvé dans le panneau client",
"smsplanetApiToken": "Jeton pour l'API SMSPlanet",
"smsplanetApiDocs": "Des informations détaillées sur l'obtention de jetons API peuvent être trouvées dans {the_smsplanet_documentation}.",
"pingNumericDescription": "Si coché, les adresses IP seront affichées à la place des noms dhôtes symboliques",
"pingPerRequestTimeoutDescription": "Cest le temps dattente maximal (en secondes) avant de considérer un paquet ping comme perdu",
"smtpHelpText": "'SMTPS' teste que SMTP/TLS fonctionne ; 'Ignorer TLS' se connecte en clair ; 'STARTTLS' se connecte, envoie une commande STARTTLS et vérifie le certificat du serveur. Aucun de ces tests nenvoie de-mail.",
"Disable URL in Notification": "Désactiver lURL dans la notification",
"defaultFriendlyName": "Nouvelle sonde",
"Use HTML for custom E-mail body": "Utiliser du HTML pour le corps personnalisé de l'e-mail",
"smseagleGroupV2": "ID(s) de groupe du carnet dadresses",
"smseagleContactV2": "ID(s) de contact du carnet dadresses",
"smseagleMsgType": "Type de message",
"smseagleMsgSms": "Message SMS (par défaut)",
"smseagleMsgRing": "Appel sonore",
"smseagleMsgTts": "Appel avec synthèse vocale",
"smseagleMsgTtsAdvanced": "Appel texte vers parole (avancé)",
"smseagleDuration": "Durée (en secondes)",
"smseagleTtsModel": "ID du modèle de synthèse vocale",
"smseagleApiType": "Version de lAPI",
"smseagleApiv1": "API v1 (pour les projets existants et la compatibilité ascendante)",
"smseagleApiv2": "API v2 (recommandée pour les nouvelles intégrations)",
"smseagleDocs": "Vérifiez la documentation ou la disponibilité de lAPI v2 : {0}",
"smseagleComma": "Les multiples doivent être séparés par une virgule",
"SpugPush Template Code": "Code du modèle",
"FlashDuty Push URL": "URL de notification",
"FlashDuty Push URL Placeholder": "Copier depuis la page dintégration des alertes",
"pingCountLabel": "Nombre maximal de paquets",
"pingCountDescription": "Nombre de paquets à envoyer avant darrêter",
"pingNumericLabel": "Sortie numérique",
"pingGlobalTimeoutLabel": "Délai dattente global",
"pingGlobalTimeoutDescription": "Durée totale en secondes avant larrêt du ping, quel que soit le nombre de paquets envoyés",
"pingPerRequestTimeoutLabel": "Délai dattente par ping",
"pingIntervalAdjustedInfo": "Intervalle ajusté en fonction du nombre de paquets, du délai dattente global et du délai dattente par ping",
"Custom URL": "URL personnalisée",
"customUrlDescription": "Sera utilisée comme URL cliquable à la place de celle du moniteur.",
"OneChatAccessToken": "Jeton daccès OneChat",
"OneChatUserIdOrGroupId": "ID utilisateur ou ID de groupe OneChat",
"OneChatBotId": "ID du bot OneChat",
"ntfyPriorityHelptextPriorityHigherThanDown": "La priorité normale devrait être plus élevée que la priorité {0}. La priorité {1} est plus élevée que la priorité {0} {2}",
"ntfyPriorityDown": "Priorité pour les événements DOWN",
"Add Tags": "Ajouter des étiquettes",
"tagAlreadyOnMonitor": "Cette étiquette (nom et valeur) est déjà présente sur la sonde ou en attente d'ajout.",
"tagAlreadyStaged": "Cette étiquette (nom et valeur) est déjà préparée pour ce lot.",
"tagNameExists": "Une étiquette système portant ce nom existe déjà. Sélectionnez là dans la liste ou utilisez un autre nom.",
"Add Another Tag": "Ajouter une autre étiquette",
"Staged Tags for Batch Add": "Étiquettes préparées pour l'ajout en lot",
"Clear Form": "Réinitialiser le formulaire",
"pause": "Pause",
"Happy Eyeballs algorithm": "Algorithme Happy Eyeballs",
"Manual": "Manuel",
"Ip Family": "Famille d'adresses IP",
"ipFamilyDescriptionAutoSelect": "Utilise le {happyEyeballs} pour déterminer la famille d'adresses IP.",
"OAuth Audience": "Audience OAuth",
"Optional: The audience to request the JWT for": "Optionnel : Le public pour lequel demander le JWT"
}

View file

@ -704,8 +704,8 @@
"smseagleRecipientType": "Tip primatelja",
"smseagleToken": "Token za pristup API-ju",
"smseagleUrl": "URL Vašeg SMSEagle uređaja",
"smseagleEncoding": "Pošalji kao Unicode",
"smseaglePriority": "Prioritet poruke (0-9, zadana vrijednost je 0)",
"smseagleEncoding": "Pošalji kao Unicode (zadano=GSM-7)",
"smseaglePriority": "Prioritet poruke (0-9, gdje je 9 najveći prioritet)",
"Server URL should not contain the nfty topic": "URL poslužitelja ne smije sadržavati temu nfty",
"PushDeer Server": "PushDeer poslužitelj",
"Custom Monitor Type": "Prilagođeni tip Monitora",
@ -773,7 +773,7 @@
"styleElapsedTimeShowWithLine": "Pokaži (s linijom)",
"recurringInterval": "Periodično",
"Recurring": "Ponavljajući",
"strategyManual": "Ručno aktivan/neaktivan",
"strategyManual": "Ručno aktivno/neaktivno",
"warningTimezone": "Koristi vremensku zonu poslužitelja",
"weekdayShortMon": "Pon",
"weekdayShortSun": "Ned",
@ -831,7 +831,7 @@
"noGroupMonitorMsg": "Nije dostupno. Prvo kreirajte grupu Monitora.",
"Close": "Zatvori",
"Request Body": "Tijelo zahtjeva",
"wayToGetFlashDutyKey": "Možete otići na 'Channel' -> (Odaberite kanal) -> 'Integrations' -> 'Add a new integration' i odaberite 'Uptime Kuma' da biste dobili push adresu. Zatim kopirajte integracijski ključ u adresu. Za više informacija posjetite",
"wayToGetFlashDutyKey": "Za integraciju Uptime Kume s Flashdutyjem: Idite na Channels > Select a channel > Integrations > Add a new integration, odaberite Uptime Kuma i kopirajte push adresu.",
"FlashDuty Severity": "Stupanj ozbiljnosti",
"nostrRelays": "Nostr releji",
"nostrRelaysHelp": "Jedan URL releja po liniji",
@ -1111,5 +1111,63 @@
"wayToGetWahaSession": "Iz ove sjednice WAHA šalje obavijesti na identifikator razgovora. Može se pronaći na WAHA nadzornoj ploči.",
"wayToWriteWahaChatId": "Telefonski broj s međunarodnim prefiksom, ali bez znaka plus na početku ({0}), identifikator kontakta ({1}) ili identifikator grupe ({2}). Obavijesti se šalju na ovaj identifikator chata iz WAHA sesije.",
"telegramServerUrl": "(Neobvezno) URL Poslužitelja",
"telegramServerUrlDescription": "Za ukidanje ograničenja API-ja za botove Telegrama ili dobivanje pristupa u blokiranim područjima (Kina, Iran, itd.). Za više informacija kliknite {0}. Zadano: {1}"
"telegramServerUrlDescription": "Za ukidanje ograničenja API-ja za botove Telegrama ili dobivanje pristupa u blokiranim područjima (Kina, Iran, itd.). Za više informacija kliknite {0}. Zadano: {1}",
"Font Twemoji by Twitter licensed under": "Font Twemoji tvrtke X je pod licencom",
"the smsplanet documentation": "dokumentaciji usluge SMSPLANET",
"Phone numbers": "Brojevi telefona",
"Sender name": "Naziv pošiljatelja",
"smsplanetNeedToApproveName": "Potrebno je odobrenje u klijentskoj nadzornoj ploči",
"smsplanetApiToken": "Token za pristup SMSPLANET API-ju",
"smsplanetApiDocs": "Detaljne informacije o dobivanju tokena za API možete pronaći u {the_smsplanet_documentation}.",
"Happy Eyeballs algorithm": "algoritam Happy Eyeballs",
"Add Another Tag": "Dodaj novu oznaku",
"Staged Tags for Batch Add": "Pripremljene oznake za skupno dodavanje",
"pause": "Pauziraj",
"Use HTML for custom E-mail body": "Koristiti HTML za prilagođeno tijelo e-maila",
"smseagleGroupV2": "Identifikator(i) grupe telefonskog imenika",
"smseagleMsgType": "Tip poruke",
"smseagleMsgTtsAdvanced": "Napredni poziv s pretvaranjem teksta u govor",
"smseagleApiv1": "APIv1 (za postojeće projekte i povratnu kompatibilnost)",
"pingGlobalTimeoutDescription": "Ukupno vrijeme trajanja slanja (u sekundama), bez obzira na broj paketa",
"smtpHelpText": "'SMTPS' opcija provjerava radi li SMTP/TLS; 'Ignore TLS' dovodi do povezivanja koristeći običan tekst; 'STARTTLS' dovodi do izdavanja naredbe STARTTLS i provjere certifikata poslužitelja. Ništa od ovoga ne šalje e-poštu.",
"Ip Family": "IP verzija",
"ipFamilyDescriptionAutoSelect": "Koristi {happyEyeballs} za određivanje IP verzija.",
"Clear Form": "Očisti formu",
"defaultFriendlyName": "Novi Monitor",
"ntfyPriorityHelptextPriorityHigherThanDown": "Regularni prioritet treba biti veći od prioriteta {0}. Prioritet {1} je veći od prioriteta {0} {2}",
"ntfyPriorityDown": "Prioritet za incidente ispada",
"pingPerRequestTimeoutDescription": "Maksimalno vrijeme čekanja (u sekundama) prije nego što se pojedini paket smatra izgubljenim",
"Add Tags": "Dodaj oznake",
"tagAlreadyOnMonitor": "Ova oznaka (naziv i vrijednost) već je dodana na Monitor ili je njeno dodavanje na čekanju.",
"tagNameExists": "Već postoji sistemska oznaka s ovim nazivom. Odaberite ju iz popisa ili koristite drugi naziv.",
"tagAlreadyStaged": "Ova oznaka (naziv i vrijednost) već je pripremljena za ovaj skup Monitora.",
"smseagleContactV2": "Identifikator(i) kontakta telefonskog imenika",
"smseagleMsgSms": "SMS poruka (zadano)",
"smseagleMsgRing": "Poziv sa zvonom",
"smseagleMsgTts": "Poziv s pretvaranjem teksta u govor",
"smseagleDuration": "Trajanje (u sekundama)",
"smseagleTtsModel": "Identifikator modela za pretvaranje teksta u govor",
"smseagleApiType": "Inačica API-ja",
"smseagleApiv2": "APIv2 (preporučeno za nove integracije)",
"smseagleDocs": "Provjeriti dokumentaciju i dostupnost verzije APIv2: {0}",
"smseagleComma": "Višestruki izbori moraju se odvojiti zarezom",
"SpugPush Template Code": "Kôd predloška",
"FlashDuty Push URL": "Push adresa",
"FlashDuty Push URL Placeholder": "Kopirati sa stranice za integraciju",
"pingCountLabel": "Maks. paketa",
"pingCountDescription": "Ukupan broj paketa koji će se poslati",
"pingNumericLabel": "Brojčani ispis",
"pingNumericDescription": "Ako je odabrano, IP adrese bit će ispisane umjesto naziva domaćina",
"pingGlobalTimeoutLabel": "Globalno vremensko ograničenje",
"pingPerRequestTimeoutLabel": "Vremensko ograničenje jednog paketa",
"pingIntervalAdjustedInfo": "Interval koji se prilagođava broju paketa, globalnom vremenskom ograničenju i vremenskom ograničenju jednog paketa",
"Custom URL": "Prilagođena adresa",
"customUrlDescription": "Koristit će se kao adresa na koju monitor vodi, umjesto adrese Monitora.",
"OneChatAccessToken": "OneChat pristupni token",
"OneChatUserIdOrGroupId": "OneChat identifikator korisnika ili grupe",
"OneChatBotId": "OneChat identifikator bota",
"Disable URL in Notification": "Onemogući URL u obavijesti",
"Manual": "Ručno",
"OAuth Audience": "OAuth publika",
"Optional: The audience to request the JWT for": "Neobavezno: Publika za koju se traži JWT"
}

View file

@ -308,7 +308,7 @@
"clearDataOlderThan": "Mantieni lo storico per {0} giorni.",
"PasswordsDoNotMatch": "Le password non corrispondono.",
"records": "records",
"One record": "One record",
"One record": "Un record",
"steamApiKeyDescription": "Per monitorare un server di gioco Steam è necessaria una Web-API Key di Steam. È possibile registrarne una qui: ",
"Current User": "Utente corrente",
"recent": "Recenti",
@ -450,7 +450,7 @@
"Domain Name Expiry Notification": "Notifica di scadenza del nome di dominio",
"Date Created": "Data di creazione",
"Slug": "Slug",
"Show Powered By": "Mostra Alimentato da",
"Show Powered By": "Mostra \"Poweerd By\"",
"Domain Names": "Nomi di dominio",
"signedInDispDisabled": "Autenticazione disabilitata.",
"RadiusSecret": "Radius Segreto",
@ -742,5 +742,42 @@
"templateHostnameOrURL": "nome host o URL",
"templateStatus": "stato",
"templateServiceName": "nome del servizio",
"locally configured mail transfer agent": "agente mail configurato localmente"
"locally configured mail transfer agent": "agente mail configurato localmente",
"shrinkDatabaseDescriptionSqlite": "Un record",
"pushoversounds cashregister": "Registratore di cassa",
"Strategy": "Strategia",
"Add a domain": "Aggiungi un dominio",
"telegramServerUrl": "(Facoltativo) URL del Server",
"pushoversounds magic": "Magico",
"pushoversounds mechanical": "Meccanico",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Sirena",
"pushoversounds spacealarm": "Allarme spaziale",
"pushoversounds alien": "Allarme Alieno (lungo)",
"Remove domain": "Rimuovi il dominio '{0}'",
"Edit Tag": "Modifica il Tag",
"Server Address": "Indirizzo del Server",
"Expiry": "Scadenza",
"telegramUseTemplateDescription": "Se abilitato, il messaggio sarà spedito usando il template personalizzato.",
"high": "alto",
"jsonQueryDescription": "Analizza ed estrai dati specifici dalla risposta JSON del server utilizzando una query JSON oppure usa \"$\" per la risposta grezza, se non ti aspetti JSON. Il risultato viene quindi confrontato con il valore previsto, sotto forma di stringhe. Consulta {0} per la documentazione e usa {1} per sperimentare con le query.",
"Free Mobile User Identifier": "Identificatore utente mobile gratuito",
"telegramServerUrlDescription": "Per rimuovere le limitazioni dell'API bot di Telegram o ottenere l'accesso in aree bloccate (Cina, Iran, ecc.), clicca su {0} per maggiori informazioni. Predefinito: {1}",
"octopushLogin": "“Accedi” dalle credenziali API HTTP nel pannello di controllo",
"promosmsLogin": "Nome di accesso API",
"pushoversounds bike": "Bicicletta",
"pushoversounds bugle": "Bugle",
"pushoversounds classical": "Classico",
"pushoversounds cosmic": "Cosmico",
"pushoversounds incoming": "In arrivo",
"pushoversounds intermission": "Intervallo",
"pushoversounds tugboat": "Rimorchiatore",
"pushoversounds climb": "Salita (lunga)",
"pushoversounds persistent": "Persistente (lungo)",
"pushoversounds vibrate": "Solo vibrazione",
"wayToGetKookGuildID": "Attivare la “Modalità sviluppatore” nelle impostazioni di Kook e fare clic con il pulsante destro del mouse sulla gilda per ottenere il suo ID.",
"Guild ID": "Guild ID",
"Free Mobile API Key": "Chiave API mobile gratuita",
"telegramUseTemplate": "Utilizza un template di messaggio personalizzato",
"telegramTemplateFormatDescription": "Telegram permette l'utilizzo di diversi linguaggi di markup, vedi Telegram {0} per maggiori dettagli."
}

View file

@ -29,7 +29,7 @@
"Add New Monitor": "監視の追加",
"Quick Stats": "統計",
"Up": "正常",
"Down": "停止",
"Down": "異常",
"Pending": "待機中",
"Unknown": "不明",
"Pause": "一時停止",
@ -1085,5 +1085,11 @@
"telegramUseTemplate": "カスタムメッセージテンプレートを使用",
"telegramUseTemplateDescription": "有効にすると、メッセージはカスタムテンプレートを使って送信されます。",
"telegramTemplateFormatDescription": "Telegramではメッセージに異なるマークアップ言語を使用することができます。詳細はTelegram {0} を参照してください。",
"Font Twemoji by Twitter licensed under": "TwemojiフォントはTwitterライセンス下でライセンスされています"
"Font Twemoji by Twitter licensed under": "TwemojiフォントはTwitterライセンス下でライセンスされています",
"the smsplanet documentation": "smsplanetドキュメント",
"Phone numbers": "携帯電話番号",
"Sender name": "送信者名",
"smsplanetNeedToApproveName": "クライアントパネルでの承認が必要",
"smsplanetApiToken": "SMSPlanet APIのトークン",
"smsplanetApiDocs": "APIトークンの取得に関する詳細な情報は、{the_smsplanet_documentation}にあります。"
}

View file

@ -18,5 +18,26 @@
"Add": "დამატება",
"Add New Monitor": "ახალი მონიტორის დამატება",
"Down": "დაბლა",
"setupDatabaseChooseDatabase": "რომელი მონაცემთა ბაზის გამოყენება გსურთ?"
"setupDatabaseChooseDatabase": "რომელი მონაცემთა ბაზის გამოყენება გსურთ?",
"here": "აქ",
"Required": "აუცილებელი",
"high": "მაღალი",
"greater than": "მეტი",
"less than or equal to": "ნაკლები ან ტოლი",
"greater than or equal to": "მეტი ან ტოლი",
"record": "ჩანაწერი",
"Notification Channel": "შეტყობინებების არხი",
"Sound": "ხმა",
"less than": "ნაკლები",
"-hour": "-საათი",
"Friendly Name": "სახელი",
"hour": "საათი",
"-year": "-წელი",
"Response": "პასუხი",
"Ping": "პინგი",
"Monitor Type": "მონიტორის ტიპი",
"Keyword": "საკვანძო სიტყვა",
"Unknown": "უცნობი",
"dbName": "მონაცემთა ბაზის სახელი",
"Home": "მთავარი"
}

View file

@ -1,26 +1,26 @@
{
"languageName": "한국어",
"checkEverySecond": "{0}초마다 확인해요",
"retryCheckEverySecond": "{0}초마다 다시 확인해요",
"retriesDescription": "서비스가 중단된 후 알림을 보내기 전 최대 재시도 횟수",
"ignoreTLSError": "HTTPS 웹사이트에서 TLS/SSL 오류 무시하기",
"upsideDownModeDescription": "서버 상태를 반대로 표시해요. 서버가 작동하면 오프라인으로 표시할 거예요.",
"maxRedirectDescription": "최대 리다이렉트 횟수예요. 0을 입력하면 리다이렉트를 꺼요.",
"checkEverySecond": "{0}초마다 확인",
"retryCheckEverySecond": "{0}초마다 재시도",
"retriesDescription": "서비스가 다운된 것으로 간주하고 알림을 보내기 전까지의 최대 재시도 횟수",
"ignoreTLSError": "HTTPS 웹사이트에서 TLS/SSL 오류 무시",
"upsideDownModeDescription": "상태를 반대로 표시합니다. 서비스에 연결 가능하면 '다운'으로 간주됩니다.",
"maxRedirectDescription": "최대 리디렉션 허용 횟수. 0으로 설정하면 리디렉션을 사용하지 않습니다.",
"acceptedStatusCodesDescription": "응답 성공으로 간주할 상태 코드를 정해요.",
"passwordNotMatchMsg": "비밀번호 재입력이 일치하지 않아요.",
"notificationDescription": "모니터링에 알림을 설정할 수 있어요.",
"notificationDescription": "알림이 동작하려면 기존 모니터에 할당되어야 합니다.",
"keywordDescription": "HTML 이나 JSON에서 대소문자를 구분해 키워드를 검색해요.",
"pauseDashboardHome": "일시 정지",
"deleteMonitorMsg": "정말 이 모니터링을 삭제할까요?",
"deleteNotificationMsg": "정말 이 알림을 모든 모니터링에서 삭제할까요?",
"pauseDashboardHome": "정지",
"deleteMonitorMsg": "이 모니터를 삭제하시겠습니까?",
"deleteNotificationMsg": "이 알림을 모든 모니터에서 삭제하시겠습니까?",
"resolverserverDescription": "Cloudflare가 기본 서버예요, 원한다면 언제나 다른 Resolver 서버로 변경할 수 있어요.",
"rrtypeDescription": "모니터링할 RR-Type을 선택해요",
"pauseMonitorMsg": "정말 이 모니터링을 일시 정지할까요?",
"enableDefaultNotificationDescription": "새로 추가하는 모든 모니터링에 이 알림을 기본적으로 활성화해요. 각 모니터에 대해 별도로 알림을 비활성화할 수 있어요.",
"clearEventsMsg": "정말 이 모니터링에 대한 모든 이벤트를 삭제할까요?",
"clearHeartbeatsMsg": "정말 이 모니터링에 대한 모든 하트비트를 삭제할까요?",
"rrtypeDescription": "모니터링할 RR Type을 선택하세요.",
"pauseMonitorMsg": "이 모니터를 일시 정지하시겠습니까?",
"enableDefaultNotificationDescription": "새 모니터에 이 알림을 기본적으로 활성화합니다. 개별 모니터에 대해 알림을 비활성화할 수 있습니다.",
"clearEventsMsg": "이 모니터의 모든 이벤트를 삭제하시겠습니까?",
"clearHeartbeatsMsg": "이 모니터의 모든 하트비트를 삭제하시겠습니까?",
"confirmClearStatisticsMsg": "정말 모든 통계를 삭제할까요?",
"importHandleDescription": "이름이 같은 모든 모니터링이나 알림을 건너뛰려면 '기존값 건너뛰기'를 선택해주세요. '덮어쓰기'는 기존의 모든 모니터링과 알림을 삭제해요.",
"importHandleDescription": "이름이 같은 모니터나 알림을 건너뛰려면 '기존 항목 건너뛰기'를 선택하세요. '덮어쓰기'를 선택한 경우 존재하는 기존 모니터와 알림을 모두 삭제합니다.",
"confirmImportMsg": "정말 백업을 가져올까요? 가져오기 옵션을 제대로 설정했는지 다시 확인해주세요.",
"twoFAVerifyLabel": "토큰을 입력해 2단계 인증이 작동하는지 확인해주세요",
"tokenValidSettingsMsg": "토큰이 유효해요! 이제 2단계 인증 설정을 저장할 수 있어요.",
@ -28,17 +28,17 @@
"confirmDisableTwoFAMsg": "정말 2단계 인증을 비활성화할까요?",
"Settings": "설정",
"Dashboard": "대시보드",
"New Update": "새로운 업데이트",
"New Update": "새 업데이트",
"Language": "언어",
"Appearance": "디스플레이",
"Appearance": "모양",
"Theme": "테마",
"General": "일반",
"Version": "버전",
"Check Update On GitHub": "깃허브에서 업데이트 확인",
"Check Update On GitHub": "Github에서 업데이트 확인",
"List": "목록",
"Add": "추가",
"Add New Monitor": "새로운 모니터 추가하기",
"Quick Stats": "간단한 정보",
"Add New Monitor": "새 모니터 추가",
"Quick Stats": "요약",
"Up": "온라인",
"Down": "오프라인",
"Pending": "대기 중",
@ -50,22 +50,22 @@
"Message": "메시지",
"No important events": "중요 이벤트 없음",
"Resume": "재개",
"Edit": "수정",
"Edit": "편집",
"Delete": "삭제",
"Current": "현재",
"Uptime": "업타임",
"Cert Exp.": "인증서 만료.",
"Cert Exp.": "인증서 만료",
"day": "일",
"-day": "-일",
"-day": "일",
"hour": "시간",
"-hour": "-시간",
"-hour": "시간",
"Response": "응답",
"Ping": "핑",
"Monitor Type": "모니터링 종류",
"Monitor Type": "모니터 타입",
"Keyword": "키워드",
"Friendly Name": "이름",
"Friendly Name": "별명",
"URL": "URL",
"Hostname": "호스트네임",
"Hostname": "호스트",
"Port": "포트",
"Heartbeat Interval": "하트비트 주기",
"Retries": "재시도",
@ -73,46 +73,46 @@
"Advanced": "고급",
"Upside Down Mode": "상태 반전 모드",
"Max. Redirects": "최대 리다이렉트",
"Accepted Status Codes": "응답 성공 상태 코드",
"Accepted Status Codes": "허용된 상태 코드",
"Save": "저장",
"Notifications": "알림",
"Not available, please setup.": "존재하지 않아요. 새로운 거 하나 만드는 건 어때요?",
"Not available, please setup.": "아직 사용할 수 없습니다. 설정이 필요합니다.",
"Setup Notification": "알림 설정",
"Light": "이트",
"Light": "이트",
"Dark": "다크",
"Auto": "자동",
"Theme - Heartbeat Bar": "테마 - 하트비트 바",
"Normal": "기본값",
"Bottom": "가운데",
"Bottom": "하단",
"None": "없음",
"Timezone": "시간대",
"Search Engine Visibility": "검색 엔진 활성화",
"Search Engine Visibility": "검색 엔진 노출",
"Allow indexing": "인덱싱 허용",
"Discourage search engines from indexing site": "검색 엔진 인덱싱 거부",
"Discourage search engines from indexing site": "검색 엔진의 인덱싱을 허용하지 않음",
"Change Password": "비밀번호 변경",
"Current Password": "기존 비밀번호",
"Current Password": "현재 비밀번호",
"New Password": "새 비밀번호",
"Repeat New Password": "새로운 비밀번호 재입력",
"Repeat New Password": "새 비밀번호 확인",
"Update Password": "비밀번호 변경",
"Disable Auth": "인증 비활성화",
"Enable Auth": "인증 활성화",
"disableauth.message1": "정말로 {disableAuth}?",
"disable authentication": "인증 기능을 끌까요",
"disableauth.message2": "이 기능은 {intendThirdPartyAuth}을 Uptime Kuma 앞에 둔 사용자를 위한 기능이에요.",
"where you intend to implement third-party authentication": "Cloudflare Access와 같은 서드파티 인증",
"disableauth.message1": "{disableAuth}하시겠습니까?",
"disable authentication": "인증을 비활성화",
"disableauth.message2": "이 기능은 Uptime Kuma 앞단에 Cloudflare Access, Authelia 등의 {intendThirdPartyAuth}을 위해 설계되었습니다.",
"where you intend to implement third-party authentication": "서드 파티 인증을 구현하는 상황",
"Please use this option carefully!": "신중하게 사용하세요!",
"Logout": "로그아웃",
"Leave": "나가기",
"I understand, please disable": "기능에 대해 이해했으니 꺼주세요.",
"Leave": "취소",
"I understand, please disable": "이해했습니다. 비활성화합니다.",
"Confirm": "확인",
"Yes": "확인",
"No": "취소",
"Username": "이름",
"Yes": "",
"No": "아니요",
"Username": "사용자명",
"Password": "비밀번호",
"Remember me": "비밀번호 기억하기",
"Remember me": "로그인 상태 유지",
"Login": "로그인",
"No Monitors, please": "모니터링이 현재 없어요,",
"add one": "한번 추가해보실래요?",
"No Monitors, please": "등록된 모니터가 없습니다.",
"add one": "추가하기",
"Notification Type": "알림 종류",
"Email": "이메일",
"Test": "테스트",
@ -120,33 +120,33 @@
"Resolver Server": "Resolver 서버",
"Resource Record Type": "리소스 레코드 유형",
"Last Result": "최근 결과",
"Create your admin account": "관리자 계정 만들기",
"Repeat Password": "비밀번호 재입력",
"Create your admin account": "관리자 계정 생성",
"Repeat Password": "비밀번호 확인",
"Import Backup": "백업 가져오기",
"Export Backup": "백업 내보내기",
"Export": "내보내기",
"Import": "가져오기",
"respTime": "응답 시간 (ms)",
"notAvailableShort": "N/A",
"Default enabled": "기본 알림으로 설정",
"Apply on all existing monitors": "기존 모니터에 모두 적용하기",
"Create": "생성하기",
"Default enabled": "기본적으로 활성화",
"Apply on all existing monitors": "기존 모니터에 모두 적용",
"Create": "생성",
"Clear Data": "데이터 삭제",
"Events": "이벤트",
"Heartbeats": "하트비트",
"Auto Get": "자동 Get",
"backupDescription": "모든 모니터링과 알림을 JSON 파일 형식에 저장할 수 있어요.",
"Auto Get": "Auto Get",
"backupDescription": "모든 모니터와 알림을 JSON 파일에 백업할 수 있습니다.",
"backupDescription2": "히스토리와 이벤트 데이터는 포함되어 있지 않아요.",
"backupDescription3": "알림 토큰과 같은 보안 데이터가 내보내기 파일에 포함되어 있으므로 관리에 주의해주세요.",
"alertNoFile": "가져오기를 하기 위해 파일을 선택해주세요.",
"alertWrongFileType": "JSON 파일을 선택해주세요.",
"Clear all statistics": "모든 통계 삭제",
"Skip existing": "기존 건너뛰기",
"alertNoFile": "가져올 파일을 선택하세요.",
"alertWrongFileType": "JSON 파일을 선택세요.",
"Clear all statistics": "모든 통계 삭제",
"Skip existing": "기존 항목 건너뛰기",
"Overwrite": "덮어쓰기",
"Options": "옵션",
"Keep both": "두개 모두 보존",
"Keep both": "모두 보존",
"Verify Token": "토큰 검증",
"Setup 2FA": "2단계 인증 설정하기",
"Setup 2FA": "2단계 인증 설정",
"Enable 2FA": "2단계 인증 활성화",
"Disable 2FA": "2단계 인증 비활성화",
"2FA Settings": "2단계 인증 설정",
@ -154,34 +154,34 @@
"Active": "활성화",
"Inactive": "비활성화",
"Token": "토큰",
"Show URI": "URI 보기",
"Show URI": "URI 표시",
"Tags": "태그",
"Add New below or Select...": "아래 새롭게 추가 또는 선택…",
"Tag with this name already exist.": "같은 태그 이름이 이미 존재해요.",
"Tag with this value already exist.": "같은 값을 가진 태그가 이미 존재해요.",
"Add New below or Select...": "아래에서 선택하거나 추가…",
"Tag with this name already exist.": "동일한 이름의 태그가 이미 존재합니다.",
"Tag with this value already exist.": "동일한 값의 태그가 이미 존재합니다.",
"color": "색상",
"value (optional)": "값 (선택)",
"Gray": "회색",
"Red": "빨간색",
"Orange": "주황",
"Green": "초록",
"Blue": "파란색",
"Indigo": "남색",
"Purple": "보라",
"Pink": "핑크",
"Red": "빨",
"Orange": "주황",
"Green": "초록",
"Blue": "파",
"Indigo": "인디고",
"Purple": "보라",
"Pink": "핑크",
"Search...": "검색…",
"Avg. Ping": "평균 핑",
"Avg. Response": "평균 응답",
"Entry Page": "첫 페이지",
"statusPageNothing": "아무것도 없어요. 새로운 그룹 또는 모니터링을 추가해주세요.",
"statusPageNothing": "아무것도 없습니다. 새 그룹이나 모니터를 추가하세요.",
"No Services": "서비스 없음",
"All Systems Operational": "모든 시스템 정상",
"Partially Degraded Service": "일부 시스템 비정상",
"Degraded Service": "모든 시스템 비정상",
"All Systems Operational": "모든 시스템 작동 중",
"Partially Degraded Service": "일부 서비스 불안정",
"Degraded Service": "서비스 불안정",
"Add Group": "그룹 추가",
"Add a monitor": "모니터 추가",
"Edit Status Page": "상태 페이지 수정",
"Go to Dashboard": "대시보드로 가기",
"Add a monitor": "모니터 추가",
"Edit Status Page": "상태 페이지 편집",
"Go to Dashboard": "대시보드로",
"Status Page": "상태 페이지",
"Status Pages": "상태 페이지",
"defaultNotificationName": "내 {notification} 알림 ({number})",
@ -198,8 +198,8 @@
"webhook": "Webhook",
"Post URL": "Post URL",
"Content Type": "Content Type",
"webhookJsonDesc": "{0}은 Express.js와 같은 최신 HTTP 서버에 적합해요",
"webhookFormDataDesc": "{multipart}은 PHP에 적합해요. {decodeFunction}를 기준으로 JSON을 디코딩하면 되어요",
"webhookJsonDesc": "{0}은(는) Express.js와 같은 모던 HTTP 서버에 적합합니다.",
"webhookFormDataDesc": "{multipart}는 PHP에 적합합니다. JSON은 {decodeFunction}을 사용해 파싱해야 합니다.",
"smtp": "Email (SMTP)",
"secureOptionNone": "없음 / STARTTLS (25, 587)",
"secureOptionTLS": "TLS (465)",
@ -215,26 +215,26 @@
"Prefix Custom Message": "접두사 메시지",
"Hello @everyone is...": "{'@'}everyone 서버 상태 알림이에요…",
"teams": "Microsoft Teams",
"Webhook URL": "웹훅 URL",
"Webhook URL": "Webhook URL",
"wayToGetTeamsURL": "{0}에서 Webhook을 어떻게 만드는지 알아보세요.",
"signal": "Signal",
"Number": "숫자",
"Recipients": "받는 사람",
"needSignalAPI": "REST API를 사용하는 Signal 클라이언트가 있어야 해요.",
"wayToCheckSignalURL": "밑에 URL을 확인해 URL 설정 방법을 볼 수 있어요:",
"signalImportant": "경고: 받는 사람의 그룹과 숫자는 섞을 수 없어요!",
"signalImportant": "중요: 수신자 그룹과 숫자는 섞을 수 없습니다!",
"gotify": "Gotify",
"Application Token": "애플리케이션 토큰",
"Server URL": "서버 URL",
"Priority": "우선 순위",
"slack": "Slack",
"Icon Emoji": "아이콘 이모지",
"Channel Name": "채널 이름",
"Channel Name": "채널",
"Uptime Kuma URL": "Uptime Kuma URL",
"aboutWebhooks": "Webhook에 대한 설명: {0}",
"aboutChannelName": "Webhook 채널을 무시하려면 {0} 채널 이름칸에 채널 이름을 입력해주세요. 예: #기타-채널",
"aboutKumaURL": "Uptime Kuma URL칸을 공백으로 두면 기본적으로 Github Project 페이지로 설정해요.",
"emojiCheatSheet": "이모지 목록 시트: {0}",
"aboutWebhooks": "Webhook에 대한 자세한 내용: {0}",
"aboutChannelName": "Webhook 채널을 바이패스하려면 {0}에 채널 이름을 입력하세요. 예: #기타-채널",
"aboutKumaURL": "Uptime Kuma URL 필드를 공백으로 두면 기본적으로 Github Project 페이지로 설정합니다,",
"emojiCheatSheet": "이모지 목록: {0}",
"rocket.chat": "Rocket.chat",
"pushover": "Pushover",
"pushy": "Pushy",
@ -263,9 +263,9 @@
"Example:": "예: {0}",
"Read more:": "더 보기: {0}",
"Status:": "상태: {0}",
"Read more": "더 보기",
"appriseInstalled": "Apprise가 설치되어있어요.",
"appriseNotInstalled": "Apprise가 설치되어있지 않아요. {0}",
"Read more": "더보기",
"appriseInstalled": "Apprise가 설치되어 있습니다.",
"appriseNotInstalled": "Apprise가 설치되지 않았습니다. {0}",
"Access Token": "액세스 토큰",
"Channel access token": "채널 액세스 토큰",
"Line Developers Console": "Line 개발자 콘솔",
@ -284,10 +284,10 @@
"promosmsTypeSpeed": "SMS SPEED - 시스템에서 가장 높은 우선순위예요. 매우 빠르고 신뢰할 수 있지만 비용이 많이 들어요 (SMS 전체 가격의 약 두 배).",
"promosmsPhoneNumber": "전화 번호 (폴란드 수신자라면 지역번호를 적지 않아도 되어요.)",
"promosmsSMSSender": "SMS 보내는 사람 이름 : 미리 등록된 이름 혹은 기본값 중 하나예요: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
"Primary Base URL": "기본 URL",
"Primary Base URL": "주 베이스 URL",
"Push URL": "Push URL",
"needPushEvery": "이 URL을 {0} 초 마다 호출할 수 있어요.",
"pushOptionalParams": "선택적 파라미터: {0}",
"needPushEvery": "이 URL을 {0}초 마다 호출할 수 있습니다.",
"pushOptionalParams": "추가 파라미터: {0}",
"emailCustomSubject": "커스텀 주제",
"clicksendsms": "ClickSend SMS",
"checkPrice": "{0} 가격 확인:",
@ -297,110 +297,110 @@
"matrixHomeserverURL": "Homeserver URL (http(s):// 와 함께 적어주세요. 그리고 포트 번호는 선택적 입니다.)",
"Internal Room Id": "내부 방 ID",
"matrixDesc1": "Matrix 클라이언트 방 설정의 고급 섹션에서 내부 방 ID를 찾을 수 있어요. 내부 방 ID는 이렇게 생겼답니다: !QMdRCpUIfLwsfjxye6:home.server.",
"matrixDesc2": "사용자의 모든 방에 대한 엑세스가 허용될 수 있어서 새로운 사용자를 만들고 원하는 방에만 초대한 후 엑세스 토큰을 사용하는 것이 좋아요. {0} 이 명령어를 통해 엑세스 토큰을 얻을 수 있어요",
"Method": "메서드",
"matrixDesc2": "개인 Matrix 사용자 계정의 액세스 토큰을 사용하는 것은 계정 전체와 참여 중인 모든 방에 완전한 접근 권한을 부여하게 되므로 권장되지 않습니다. 대신 새로운 사용자를 생성한 후 알림을 받을 방에만 초대하는 것을 권장합니다. 액세스 토큰은 {0} 명령어를 실행하여 얻을 수 있습니다.",
"Method": "Method",
"Body": "Body",
"Headers": "헤더",
"PushUrl": "Push URL",
"HeadersInvalidFormat": "요청 Headers의 JSON 형식이 올바르지 않아요: ",
"BodyInvalidFormat": "요청 Body의 JSON 형식이 올바르지 않아요: ",
"Monitor History": "모니터 기록",
"clearDataOlderThan": "모니터링 기록을 {0}일 동안 저장해요.",
"PasswordsDoNotMatch": "비밀번호가 일치하지 않아요.",
"records": "records",
"HeadersInvalidFormat": "요청 헤더의 JSON 형식이 올바르지 않음: ",
"BodyInvalidFormat": "요청 본문의 JSON 형식이 올바르지 않음: ",
"Monitor History": "모니터 기록",
"clearDataOlderThan": "모니터 기록을 {0}일간 저장합니다.",
"PasswordsDoNotMatch": "비밀번호가 일치하지 않습니다.",
"records": "레코드",
"One record": "One record",
"steamApiKeyDescription": "스팀 게임 서버를 모니터링하려면 Steam Web API 키가 필요해요. API 키는 하단 웹사이트에서 등록할 수 있어요: ",
"steamApiKeyDescription": "Steam 게임 서버를 모니터링하려면 Steam Web-API 키가 필요합니다. 여기서 API 키를 등록하세요: ",
"Current User": "현재 사용자",
"recent": "최근",
"Done": "완료",
"Info": "정보",
"Security": "보안",
"Steam API Key": "스팀 API 키",
"Steam API Key": "Steam API 키",
"Shrink Database": "데이터베이스 축소",
"Pick a RR-Type...": "RR-Type을 골라주세요…",
"Pick Accepted Status Codes...": "상태 코드를 골라주세요…",
"Pick a RR-Type...": "RR-Type 선택…",
"Pick Accepted Status Codes...": "성공 상태 코드 선택…",
"Default": "기본",
"HTTP Options": "HTTP 옵션",
"Create Incident": "인시던트 만들기",
"Create Incident": "인시던트 생성",
"Title": "제목",
"Content": "내용",
"Style": "스타일",
"info": "정보",
"warning": "주의",
"danger": "경고",
"warning": "경고",
"danger": "위험",
"primary": "기본",
"light": "이트",
"light": "이트",
"dark": "다크",
"Post": "게시",
"Please input title and content": "제목과 내용을 작성해주세요",
"Created": "생성 날짜",
"Last Updated": "마지막 업데이트",
"Please input title and content": "제목 및 내용을 입력하세요.",
"Created": "생성",
"Last Updated": "최근 수정",
"Unpin": "제거",
"Switch to Light Theme": "이트 테마로 전환",
"Switch to Light Theme": "이트 테마로 전환",
"Switch to Dark Theme": "다크 테마로 전환",
"Show Tags": "태그 보기",
"Show Tags": "태그 보기",
"Hide Tags": "태그 숨기기",
"Description": "설명",
"No monitors available.": "모니터링이 없어요.",
"No monitors available.": "사용 가능한 모니터가 없습니다.",
"Add one": "추가하기",
"No Monitors": "모니터 없음",
"Untitled Group": "이름없는 그룹",
"No Monitors": "모니터 없음",
"Untitled Group": "제목 없는 그룹",
"Services": "서비스",
"Discard": "취소",
"Cancel": "취소",
"Cancel": "닫기",
"Powered by": "Powered by",
"serwersms": "SerwerSMS.pl",
"serwersmsAPIUser": "API Usename (webapi_ 접두사 포함)",
"serwersmsAPIUser": "API 사용자명 (webapi_ 접두사 포함)",
"serwersmsAPIPassword": "API 비밀번호",
"serwersmsPhoneNumber": "휴대전화 번호",
"serwersmsSenderName": "보내는 사람 이름 (customer portal를 통해 가입된 정보)",
"serwersmsPhoneNumber": "휴대 번호",
"serwersmsSenderName": "SMS 발신자명 (customer portal로 가입된 정보)",
"stackfield": "Stackfield",
"dnsPortDescription": "DNS 서버 포트, 기본값은 53 이에요. 포트는 언제나 변경할 수 있어요.",
"PushByTechulus": "Push by Techulus",
"GoogleChat": "Google Chat (Google Workspace only)",
"topic": "Topic",
"topicExplanation": "모니터링할 MQTT Topic",
"topicExplanation": "모니터링할 MQTT 토픽",
"successMessage": "성공 메시지",
"successMessageExplanation": "성공으로 간주되는 MQTT 메시지",
"error": "오류",
"critical": "크리티컬",
"Customize": "커스터마이즈",
"Custom Footer": "커스텀 Footer",
"Custom CSS": "커스텀 CSS",
"critical": "중대",
"Customize": "사용자화",
"Custom Footer": "사용자 지정 푸터",
"Custom CSS": "사용자 지정 CSS",
"smtpDkimSettings": "DKIM 설정",
"smtpDkimDesc": "사용 방법은 DKIM {0}를 참조하세요.",
"smtpDkimDesc": "사용 방법은 Nodemailer DKIM {0}을(를) 참조하세요.",
"documentation": "문서",
"smtpDkimDomain": "도메인 이름",
"smtpDkimKeySelector": "Key Selector",
"smtpDkimPrivateKey": "Private Key",
"smtpDkimPrivateKey": "비밀 키",
"smtpDkimHashAlgo": "해시 알고리즘 (선택)",
"smtpDkimheaderFieldNames": "서명할 헤더 키 (선택)",
"smtpDkimskipFields": "서명하지 않을 헤더 키 (선택)",
"wayToGetPagerDutyKey": "Service -> Service Directory -> (서비스 선택) -> Integrations -> Add integration. 에서 찾을 수 있어요. 자세히 알아보려면 {0}에서 \"Events API V2\"를 검색해봐요",
"wayToGetPagerDutyKey": "\"Events API V2\"는 Service -> Service Directory -> (서비스 선택) -> Integrations -> Add integration. 에서 찾을 수 있습니다. 자세한 내용 {0}",
"Integration Key": "Integration 키",
"Integration URL": "Integration URL",
"Auto resolve or acknowledged": "자동 해결 혹은 승인",
"Auto resolve or acknowledged": "자동 해결 또는 승인",
"do nothing": "아무것도 하지 않기",
"auto acknowledged": "자동 승인 (acknowledged)",
"auto resolve": "자동 해결 (resolve)",
"gorush": "Gorush",
"alerta": "Alerta",
"alertaApiEndpoint": "API Endpoint",
"alertaEnvironment": "환경변수",
"alertaApiEndpoint": "API 엔드포인트",
"alertaEnvironment": "환경",
"alertaApiKey": "API 키",
"alertaAlertState": "경고 상태",
"alertaRecoverState": "해결된 상태",
"deleteStatusPageMsg": "정말 이 상태 페이지를 삭제할까요?",
"alertaAlertState": "알림 상태",
"alertaRecoverState": "복구 상태",
"deleteStatusPageMsg": "이 상태 페이지를 삭제하시겠습니까?",
"Proxies": "프록시",
"default": "Default",
"default": "기본",
"enabled": "활성화",
"setAsDefault": "기본 프록시로 설정",
"deleteProxyMsg": "정말 이 프록시를 모든 모니터링에서 삭제할까요?",
"proxyDescription": "프록시가 작동하려면 모니터에 할당되어야 해요.",
"enableProxyDescription": "이 프록시는 활성화될 때까지 영향을 미치지 않아요. 활성화 상태에 따라 모든 모니터에서 프록시를 일시정지할 수 있어요.",
"setAsDefaultProxyDescription": "새로 추가하는 모든 모니터링에 이 프록시를 기본적으로 활성화해요. 각 모니터에 대해 별도로 프록시를 비활성화할 수 있어요.",
"setAsDefault": "기본로 설정",
"deleteProxyMsg": "이 프록시를 모든 모니터에서 삭제하시겠습니까?",
"proxyDescription": "프록시가 작동하려면 모니터에 할당되어야 합니다.",
"enableProxyDescription": "이 프록시는 활성화될 때까지 모니터의 요청에 미치지 않습니다. 활성화 상태를 통해 모든 모니터에서 프록시를 일시 정지할 수 있습니다.",
"setAsDefaultProxyDescription": "새 모니터에 이 프록시를 기본적으로 활성화합니다. 개별 모니터에 대해 프록시를 비활성화할 수 있습니다.",
"Certificate Chain": "인증서 체인",
"Valid": "유효",
"Valid": "유효",
"Invalid": "유효하지 않음",
"AccessKeyId": "AccessKey ID",
"SecretAccessKey": "AccessKey Secret",
@ -425,17 +425,17 @@
"Proxy server has authentication": "프록시 서버에 인증 절차가 있음",
"User": "사용자",
"Installed": "설치됨",
"Not installed": "설치되어 있지 않음",
"Not installed": "설치되지 않음",
"Running": "작동 중",
"Not running": "작동하고 있지 않음",
"Remove Token": "토큰 제",
"Not running": "작동 중이 아님",
"Remove Token": "토큰 ",
"Start": "시작",
"Stop": "정지",
"Uptime Kuma": "Uptime Kuma",
"Add New Status Page": "새로운 상태 페이지 만들기",
"Slug": "주소",
"Add New Status Page": "새 상태 페이지 추가",
"Slug": "Slug",
"Accept characters:": "허용되는 문자열:",
"startOrEndWithOnly": "{0} 로 시작하거나 끝나야 해요",
"startOrEndWithOnly": "{0}로 시작하거나 끝나야 합니다.",
"No consecutive dashes": "연속되는 대시는 허용되지 않아요",
"Next": "다음",
"The slug is already taken. Please choose another slug.": "이미 존재하는 주소에요. 다른 주소를 사용해 주세요.",
@ -469,7 +469,7 @@
"onebotGroupMessage": "그룹 메시지",
"onebotPrivateMessage": "개인 메시지",
"onebotUserOrGroupId": "그룹/사용자 ID",
"onebotSafetyTips": "을 위해 Access 토큰을 설정하세요",
"onebotSafetyTips": "안을 위해 Access 토큰을 설정하세요.",
"PushDeer Key": "PushDeer 키",
"Footer Text": "Footer 문구",
"Show Powered By": "Powered By 문구 표시하기",
@ -479,9 +479,9 @@
"Certificate Expiry Notification": "인증서 만료 알림",
"API Username": "API 사용자 이름",
"API Key": "API 키",
"Recipient Number": "받는 사람 번호",
"Recipient Number": "수신자 번호",
"From Name/Number": "발신자 이름/번호",
"Leave blank to use a shared sender number.": "공유 발신 번호를 사용하려면 공백으로 두세요.",
"Leave blank to use a shared sender number.": "공유 발신 번호를 사용하려면 공백으로 두세요.",
"Octopush API Version": "Octopush API 버전",
"Legacy Octopush-DM": "레거시 Octopush-DM",
"endpoint": "endpoint",
@ -526,9 +526,9 @@
"Retype the address.": "주소 다시 입력하기.",
"Go back to the previous page.": "이전 페이지로 돌아가기.",
"Coming Soon": "Coming Soon",
"wayToGetClickSendSMSToken": "{0}에서 API 사용자 이름과 키를 얻을 수 있어요.",
"Custom Monitor Type": "커스텀 모니터",
"deleteDockerHostMsg": "정말 이 도커 호스트를 모든 모니터링에서 삭제할까요?",
"wayToGetClickSendSMSToken": "{0}에서 API 사용자명과 키를 얻을 수 있습니다.",
"Custom Monitor Type": "커스텀 모니터",
"deleteDockerHostMsg": "이 Docker 호스트를 모든 모니터에서 삭제하시겠습니까?",
"trustProxyDescription": "'X-Forwarded-*' 헤더를 신뢰해요. 올바른 클라이언트 IP를 얻어야하고Uptime Kuma가 Nginx나 Apache 같은 프록시 뒤에 있다면 이 기능을 활성화해야 해요.",
"Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "프로필 이름(왼쪽 아래)을 클릭하고 아래로 스크롤한 다음 토큰 만들기를 클릭하여 장기 액세스 토큰을 만들 수 있어요. ",
"Then choose an action, for example switch the scene to where an RGB light is red.": "그런 다음 동작을 선택해요, 예를 들어 장면을 RGB 조명이 빨간색인 곳으로 전환해요.",
@ -541,12 +541,12 @@
"You can divide numbers with": "다음과 같이 숫자를 구분할 수 있어요:",
"goAlertInfo": "GoAlert는 온콜 스케줄링, 자동 에스컬레이션 및 알림(SMS 또는 음성 통화와 같은)을 위한 오픈 소스 응용 프로그램이에요. 올바른 사람, 올바른 방법, 적절한 시간에 자동으로 참여하세요! {0}",
"smseagle": "SMSEagle",
"smseagleTo": "휴대전화 번호",
"smseagleRecipient": "받는 사람 (쉼표로 구분)",
"smseagleTo": "휴대 번호",
"smseagleRecipient": "수신자 (여러 명인 경우 쉼표로 구분)",
"Maintenance": "점검",
"statusMaintenance": "점검 중",
"resendEveryXTimes": "{0}번마다 다시 보내요",
"resendDisabled": "다시 보내지 않아요",
"resendEveryXTimes": "{0}번마다 재전송",
"resendDisabled": "재전송하지 않음",
"loadingError": "데이터를 가져올 수 없어요, 나중에 다시 시도하세요.",
"plugin": "플러그인",
"install": "설치",
@ -575,14 +575,14 @@
"Bark Group": "Bark 그룹",
"Bark Sound": "Bark 소리",
"promosmsAllowLongSMS": "긴 SMS 허용",
"smseagleGroup": "전화번호부 그룹 이름",
"smseagleContact": "전화번호부 연락처 이름",
"smseagleRecipientType": "받는 사람 종류",
"smseagleToken": "API 세스 토큰",
"smseagleGroup": "연락처 그룹명 목록",
"smseagleContact": "연락처 이름 목록",
"smseagleRecipientType": "수신자 종류",
"smseagleToken": "API 세스 토큰",
"smseagleUrl": "SMSEagle 기기 URL",
"smseagleEncoding": "유니코드로 보내기",
"smseagleEncoding": "유니코드로 전송 (기본값 = GSM-7)",
"smseaglePriority": "메시지 우선 순위 (0-9, 기본값= 0)",
"ntfy Topic": "ntfy 주제",
"ntfy Topic": "ntfy 토픽",
"HomeAssistant": "홈 어시스턴트",
"RadiusSecretDescription": "클라이언트와 서버 간의 비밀 키",
"RadiusSecret": "Radius 비밀 키",
@ -594,7 +594,7 @@
"Request Timeout": "요청 타임아웃",
"Query": "쿼리",
"settingsCertificateExpiry": "TLS 인증서 만료",
"certificationExpiryDescription": "HTTPS 모니터링 TLS 인증서가 만료되면 알림을 활성화해요:",
"certificationExpiryDescription": "TLS 인증서가 설정된 기간 내에 만료될 경우, HTTPS 모니터가 알림을 전송합니다:",
"Setup Docker Host": "도커 호스트 설정",
"Docker Daemon": "도커 데몬",
"socket": "소켓",
@ -623,7 +623,7 @@
"Event data:": "이벤트 데이터:",
"Frontend Version": "프론트엔드 버전",
"Frontend Version do not match backend version!": "프론트엔드 버전이 백엔드 버전과 일치하지 않아요!",
"confirmDeleteTagMsg": "정말 이 태그를 삭제할까요? 이 태그와 연결된 모니터링은 삭제되지 않아요.",
"confirmDeleteTagMsg": "이 태그를 삭제하시겠습니까? 이 태그와 연결된 모니터는 삭제되지 않습니다.",
"infiniteRetention": "무한히 저장하려면 0으로 설정하세요.",
"backupRecommend": "대신 볼륨 또는 데이터 폴더 (./data/) 를 직접 백업하세요.",
"Optional": "선택",
@ -670,64 +670,64 @@
"grpcMethodDescription": "메서드 이름은 sayHello, check와 같은 카멜 케이스로 변환되어요.",
"deleteMaintenanceMsg": "정말 이 점검을 삭제할까요?",
"recurringIntervalMessage": "매일 한 번 실행 | {0}일마다 한 번 실행",
"affectedMonitorsDescription": "현재 점검에 영향을 받는 모니터링 선택하기",
"affectedMonitorsDescription": "현재 유지보수에 영향을 받는 모니터를 선택하세요.",
"affectedStatusPages": "점검 메시지를 표시할 상태 페이지 선택하기",
"Kook": "Kook",
"atLeastOneMonitor": "최소 1개의 모니터링을 선택하세요",
"atLeastOneMonitor": "적어도 1개 이상의 모니터를 선택하세요.",
"wayToGetKookBotToken": "{0} 에서 애플리케이션을 만들고 봇 토큰을 얻어요",
"Help": "도움말",
"Game": "게임",
"General Monitor Type": "일반 모니터",
"Passive Monitor Type": "수동 모니터",
"Specific Monitor Type": "특정 모니터",
"General Monitor Type": "일반 모니터 유형",
"Passive Monitor Type": "수동 모니터 유형",
"Specific Monitor Type": "특정 모니터 유형",
"Monitor": "모니터",
"Resend Notification if Down X times consecutively": "X번 중단될 경우 알림 다시 보내기",
"Schedule maintenance": "점검 예약하기",
"Affected Monitors": "영향을 받는 모니터",
"Pick Affected Monitors...": "영향을 받는 모니터 선택하기…",
"Resend Notification if Down X times consecutively": "연속적인 다운으로 판단해 알림을 재전송할 기준 횟수",
"Schedule maintenance": "유지보수 예약",
"Affected Monitors": "영향을 받는 모니터",
"Pick Affected Monitors...": "영향을 받는 모니터 선택…",
"Start of maintenance": "점검 시작",
"All Status Pages": "모든 상태 페이지",
"Select status pages...": "상태 페이지 선택하기…",
"Custom": "커스텀",
"Select status pages...": "상태 페이지 선택…",
"Custom": "사용자 지정",
"webhookAdditionalHeadersTitle": "추가 헤더",
"webhookAdditionalHeadersDesc": "웹훅과 함께 전송될 추가 헤더를 설정해요. 각각의 헤더는 JSON 키/값으로 구성되어야 해요.",
"webhookAdditionalHeadersDesc": "Webhook과 함께 전송되는 추가 헤더를 설정합니다. 각각의 헤더는 JSON 키/값으로 이루어져야 합니다.",
"HTTP Headers": "HTTP 헤더",
"Trust Proxy": "프록시 신뢰",
"API Keys": "API 키",
"markdownSupported": "Markdown 문법이 지원됨",
"markdownSupported": "마크다운 문법 사용 가능",
"telegramMessageThreadID": "(선택) 메시지 스레드 ID",
"Clone": "복제",
"cloneOf": "{0}의 복제본",
"Clone Monitor": "모니터 복제",
"Clone Monitor": "모니터 복제",
"telegramProtectContent": "포워딩/저장 보호",
"telegramProtectContentDescription": "활성화 할경우 텔레그램 봇 메시지는 포워딩 및 저장으로부터 보호됩니다.",
"telegramSendSilentlyDescription": "조용히 메시지를 보냅니다. 사용자들은 무음으로 알림을 받습니다.",
"telegramSendSilently": "무음 알림",
"Add New Tag": "태그 추가",
"Edit Tag": "태그 수정",
"Add New Tag": "태그 추가",
"Edit Tag": "태그 편집",
"Server Address": "서버 주소",
"Learn More": "자세히 알아보기",
"Continue": "계속",
"Key Added": "키 추가됨",
"No API Keys": "API 키 없음",
"disableAPIKeyMsg": "이 API키를 정말로 비활성화하시겠습니까?",
"deleteAPIKeyMsg": "이 API키를 정말로 삭제하시겠습니까?",
"disableAPIKeyMsg": "이 API 키를 비활성화하시겠습니까?",
"deleteAPIKeyMsg": "이 API 키를 삭제하시겠습니까?",
"Generate": "생성",
"Body Encoding": "Body 인코딩",
"Body Encoding": "본문(Body) 인코딩",
"Expiry": "만료",
"Expiry date": "만료 날짜",
"Expiry date": "만료",
"Don't expire": "만료되지 않음",
"notificationRegional": "지역별",
"Google Analytics ID": "Google Analytics ID",
"Google Analytics ID": "Google 애널리틱스 ID",
"Add API Key": "API 키 추가",
"apiKeyAddedMsg": "API 키가 추가되었습니다. 다시 표시되지 않을 것이므로 메모해 두세요.",
"apiKeyAddedMsg": "API 키가 추가되었습니다. 다시 표시되지 않므로 메모해 두세요.",
"pagertreeCritical": "긴급",
"apiKey-active": "사용 가능",
"apiKey-active": "활성",
"lunaseaUserID": "사용자 ID",
"apiKey-expired": "만료됨",
"Expires": "만료",
"Expires": "만료",
"twilioAuthToken": "인증 토큰 / API 키 시크릿",
"twilioFromNumber": "번호에서",
"twilioFromNumber": "발신 번호",
"twilioToNumber": "번호에서",
"twilioAccountSID": "계정 SID",
"pagertreeUrgency": "긴급",
@ -739,75 +739,75 @@
"invalidCronExpression": "알수없는 Cron 값입니다: {0}",
"Add Another": "다른 항목 추가",
"apiKey-inactive": "비활성화",
"pagertreeIntegrationUrl": "Integration 링크",
"pagertreeIntegrationUrl": "Integration URL",
"pagertreeLow": "낮음",
"pagertreeMedium": "중간",
"pagertreeHigh": "높음",
"pagertreeResolve": "자동으로 해결하기",
"pagertreeResolve": "자동으로 해결",
"pagertreeDoNothing": "아무것도 하지 않음",
"wayToGetPagerTreeIntegrationURL": "PagerTree에서 Uptime Kuma 통합을 생성한 후 Endpoint를 복사합니다. 전체 세부 정보 보기 {0}",
"wayToGetPagerTreeIntegrationURL": "PagerTree에서 Uptime Kuma 통합을 생성한 후 엔드포인트를 복사합니다. 세부 정보 보기 {0}",
"lunaseaTarget": "대상",
"lunaseaDeviceID": "기기 ID",
"lunaseaDeviceID": "디바이스 ID",
"statusPageRefreshIn": "{0} 후 새로고침",
"telegramMessageThreadIDDescription": "포럼의 대상 메시지 쓰레드(주제)에 대한 선택적 고유 식별인, 포럼 관리자 그룹에만 해당",
"pagertreeSilent": "없음",
"setupDatabaseChooseDatabase": "어떤 데이터베이스를 사용하시겠습니까?",
"setupDatabaseEmbeddedMariaDB": "추가 설정은 필요 없습니다. 이 도커 이미지에는 MariaDB가 내장되어 구성되어 있습니다. Uptime Kuma는 Unix Socket을 통해 데이터베이스에 연결합니다.",
"setupDatabaseEmbeddedMariaDB": "추가 설정이 필요하지 않습니다. 이 도커 이미지에는 MariaDB가 자동으로 포함 및 구성되어 있으며, Uptime Kuma는 유닉스 소켓을 통해 데이터베이스에 연결합니다.",
"setupDatabaseMariaDB": "외부 MariaDB 데이터베이스에 연결합니다. 데이터베이스 연결 정보를 설정해야 합니다.",
"setupDatabaseSQLite": "소규모 배포에 권장되는 간단한 데이터베이스 파일입니다. v2.0.0 이전에는 Uptime Kuma가 SQLite를 기본 데이터베이스로 사용했습니다.",
"setupDatabaseSQLite": "소규모 배포에 권장되는 간단한 데이터베이스 파일입니다. Uptime Kuma는 v2.0.0 이전까지 SQLite를 기본 데이터베이스로 사용했습니다.",
"dbName": "데이터베이스 이름",
"filterActive": "활성",
"filterActivePaused": "일시지",
"filterActive": "활성",
"filterActivePaused": "일시지",
"Home": "홈",
"Cannot connect to the socket server": "소켓 서버에 연결 할 수 없습니다",
"Reconnecting...": "재 연결중...",
"Cannot connect to the socket server": "소켓 서버에 연결할 수 없습니다.",
"Reconnecting...": "다시 연결하는 중...",
"Json Query": "JSON 쿼리",
"settingUpDatabaseMSG": "데이터베이스를 설정하는 중입니다. 시간이 걸릴 수 있으니 기다려 주세요.",
"settingUpDatabaseMSG": "데이터베이스를 설정하는 중입니다. 시간이 걸릴 수 있으니 잠시만 기다려 주세요.",
"enableNSCD": "모든 DNS 요청을 캐싱하기 위해 NSCD (Name Service Cache Daemon) 활성화",
"pushOthers": "기타",
"programmingLanguages": "프로그래밍 언어",
"Select": "선택",
"Edit Maintenance": "점검 수정하기",
"styleElapsedTime": "하트비트 바 밑의 지난 시간 표시",
"styleElapsedTimeShowNoLine": "보이기 (선 없음)",
"styleElapsedTimeShowWithLine": "보이기 (선 있음)",
"styleElapsedTime": "하트비트 바 아래 표시되는 경과 시간",
"styleElapsedTimeShowNoLine": "표시 (선 없음)",
"styleElapsedTimeShowWithLine": "표시 (선 있음)",
"chromeExecutable": "Chrome/Chromium 실행 파일",
"chromeExecutableAutoDetect": "자동 감지",
"Invert Keyword": "키워드 반전",
"Expected Value": "기값",
"Expected Value": "기값",
"Add a domain": "도메인 추가",
"Remove domain": "도메인 '{0}' 제거",
"Monitor Group": "모니터 그룹",
"Monitor Group": "모니터 그룹",
"Monitor Setting": "{0}의 모니터 설정",
"now": "지금",
"time ago": "{0} 전",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "연결하려는 서버의 호스트 이름을 입력하거나 {local_mta}를 사용하려는 경우 {localhost}를 입력합니다",
"-year": "-연도",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "연결하려는 서버의 호스트 이름을 입력하거나, {local_mta}를 사용하려는 경우 {localhost}를 입력합니다.",
"-year": "",
"Json Query Expression": "Json 쿼리 표현식",
"Host URL": "호스트 URL",
"locally configured mail transfer agent": "로컬 구성된 메일 전송 에이전트",
"ignoreTLSErrorGeneral": "연결에 TLS/SSL 오류 무시하기",
"locally configured mail transfer agent": "로컬 구성된 메일 전송 에이전트",
"ignoreTLSErrorGeneral": "연결 중 TLS/SSL 오류 무시",
"ignoredTLSError": "TLS/SSL 오류가 무시되었습니다",
"liquidIntroduction": "템플릿 생성은 Liquid 템플릿 언어를 통해 이루어집니다. 사용 지침은 {0}을 참조하세요. 사용 가능한 변수는 다음과 같습니다:",
"liquidIntroduction": "템플릿은 Liquid 템플릿 언어를 통해 생성됩니다. 사용법은 {0}을 참조하세요. 사용 가능한 변수는 다음과 같습니다:",
"templateMsg": "알림 메시지",
"templateLimitedToUpDownCertNotifications": "업/다운/인증서 만료 알림에만 사용 가능",
"templateLimitedToUpDownNotifications": "UP/DOWN 알림에만 사용 가능",
"webhookBodyPresetOption": "프리셋 - {0}",
"templateLimitedToUpDownCertNotifications": "온라인/오프라인/인증서 만료 알림에만 사용 가능",
"templateLimitedToUpDownNotifications": "온라인/오프라인 알림에만 사용 가능",
"webhookBodyPresetOption": "사전 설정 - {0}",
"successKeyword": "성공 키워드",
"successKeywordExplanation": "성공으로 간주되는 MQTT 키워드",
"successKeywordExplanation": "성공으로 간주 MQTT 키워드",
"Reset Token": "토큰 초기화",
"Check/Uncheck": "체크/체크 해제",
"pushViewCode": "푸시 모니터는 어떻게 사용하나요? (코드 보기)",
"Search monitored sites": "모니터링중인 사이트 검색",
"templateHeartbeatJSON": "heartbeat를 설명하는 오브젝트",
"shrinkDatabaseDescriptionSqlite": "SQLite 데이터베이스에서 {vacuum} 명령을 실행해요. {auto_vacuum}이 이미 활성화되어 있지만, {auto_vacuum}은 {vacuum}이 하는 것처럼 데이터베이스를 조각 모음 하거나 페이지를 다시 압축하지는 않아요.",
"pushViewCode": "푸시 모니터는 어떻게 사용하나요? (코드 보기)",
"Search monitored sites": "모니터링 중인 사이트 검색",
"templateHeartbeatJSON": "하트비트를 설명하는 오브젝트",
"shrinkDatabaseDescriptionSqlite": "SQLite 데이터베이스에 대해 {vacuum}을(를) 트리거합니다. {auto_vacuum}이 이미 활성화되어 있지만, 이는 데이터베이스를 조각 모음하거나 {vacuum} 명령어처럼 개별 데이터베이스 페이지를 다시 정리하지는 않습니다.",
"statusPageSpecialSlugDesc": "특별한 주소 {0}: 아무런 주소도 입력되지 않으면 이 페이지가 보여요",
"Add a new expiry notification day": "새 만료 알림 날짜 추가",
"Refresh Interval Description": "이 상태 페이지는 {0}초마다 완전 새로고침(F5) 돼요",
"telegramServerUrlDescription": "텔레그램 봇 API의 제한을 해제하거나, 차단된 지역(중국, 이란 등)에서 액세스하려면 {0}을 클릭하세요. 기본값: {1}",
"chromeExecutableDescription": "Docker 사용자의 경우, Chromium이 아직 설치되지 않았다면 이를 설치하고 테스트 결과를 표시하는 데 몇 분이 걸릴 수 있어요. 1GB의 디스크 공간을 사용해요.",
"templateMonitorJSON": "monitor를 설명하는 오브젝트",
"webhookBodyCustomOption": "커스텀 Body",
"templateMonitorJSON": "모니터를 설명하는 오브젝트",
"webhookBodyCustomOption": "사용자 지정 본문 (Body)",
"telegramServerUrl": "(선택) 서버 URL",
"and": "그리고",
"emailCustomisableContent": "사용자 지정 가능한 콘텐츠",
@ -815,15 +815,15 @@
"leave blank for default subject": "기본값을 사용하려면 비워두세요",
"emailCustomBody": "커스텀 Body",
"leave blank for default body": "기본값을 사용하려면 비워두세요",
"templateServiceName": "서비스 이름",
"templateServiceName": "서비스",
"templateHostnameOrURL": "호스트명 또는 URL",
"templateStatus": "상태",
"selectedMonitorCount": "선택됨: {0}",
"Remove the expiry notification": "만료 알림 날짜 제거",
"Refresh Interval": "새로고침 주기",
"noDockerHostMsg": "사용할 수 없습니다. 먼저 도커 호스트를 설정하세요.",
"DockerHostRequired": "이 모니터링을 위한 도커 호스트를 설정해 주세요.",
"tailscalePingWarning": "Tailscale Ping 모니터링을 사용하려면 Docker 없이 Uptime Kuma를 설치하고 서버에 Tailscale 클라이언트도 설치해야 합니다.",
"DockerHostRequired": "이 모니터를 위한 Docker 호스트를 설정해 주세요.",
"tailscalePingWarning": "Tailscale Ping 모니터를 사용하려면 Docker를 사용하지 않고 Uptime Kuma를 설치해야 하며, 서버에 Tailscale 클라이언트도 설치해야 합니다.",
"telegramUseTemplate": "커스텀 메시지 템플릿 사용",
"telegramUseTemplateDescription": "활성화하면 메시지를 보낼 때 커스텀 템플릿을 사용해요.",
"telegramTemplateFormatDescription": "텔레그램은 메시지에 다양한 마크업 언어를 사용할 수 있어요. 자세한 내용은 텔레그램 {0}을 참조하세요.",
@ -834,5 +834,62 @@
"Select message type": "메시지 유형 선택",
"Send to channel": "채널로 전송",
"Create new forum post": "새 포럼 게시물 만들기",
"Your User ID": "사용자 ID"
"Your User ID": "사용자 ID",
"emailTemplateMonitorJSON": "모니터를 설명하는 객체",
"postToExistingThread": "기존 스레드/포럼 게시물에 게시",
"forumPostName": "포럼 게시물 이름",
"threadForumPostID": "스레드 / 포럼 게시물 ID",
"e.g. {discordThreadID}": "예: {discordThreadID}",
"whatHappensAtForumPost": "새 포럼 게시물을 만드세요. 기존 게시물에는 메시지가 게시되지 않습니다. 기존 게시물에 게시하려면 \"{option}\"을 사용하세요",
"wayToGetDiscordThreadId": "스레드/포럼 게시물 ID를 얻는 것은 채널 ID를 얻는 것과 비슷합니다. ID를 얻는 방법에 대해 자세히 알아보세요. {0}",
"Channel access token (Long-lived)": "채널 액세스 토큰(장기)",
"invertKeywordDescription": "키워드가 존재하지 않는지 살펴보세요.",
"emailTemplateLimitedToUpDownNotification": "UP/DOWN 하트비트에만 사용 가능, 그렇지 않으면 null",
"tagAlreadyOnMonitor": "이 태그 (이름 및 값)은 이미 모니터에 있거나 추가 보류 중입니다.",
"tagNameExists": "이 이름을 가진 시스템 태그는 이미 존재합니다. 목록에서 선택하거나 다른 이름을 사용하세요.",
"Use HTML for custom E-mail body": "맞춤형 이메일 본문에 HTML을 사용하세요",
"jsonQueryDescription": "서버의 JSON 응답에서 JSON 쿼리를 사용하거나, JSON이 아닐 경우 원시 응답을 위해 \"$\"를 사용하여 특정 데이터를 파싱하고 추출합니다. 추출된 결과는 문자열로 기대값과 비교됩니다. 문서는 {0}을 참조하고, 쿼리 실험은 {1}을 사용하세요.",
"Don't mention people": "사람들을 멘션하지 마세요",
"Notify Channel": "알림 채널",
"aboutNotifyChannel": "채널 알림은 해당 채널의 모든 구성원에게 데스크톱 또는 모바일 알림을 전송하며, 이들은 상태가 활성으로 설정되어 있든 자리 비움으로 설정되어 있든 관계없이 알림을 받습니다.",
"smseagleMsgTtsAdvanced": "고급 텍스트 음성 변환 통화",
"smseagleApiv2": "APIv2 (새로운 연동에 권장됨)",
"smspartnerApiurl": "{0}에 있는 대시보드에서 API 키를 확인할 수 있습니다",
"Server URL should not contain the nfty topic": "서버 URL에 nfty 토픽을 포함해서는 안 됨",
"defaultFriendlyName": "새로운 모니터",
"smspartnerPhoneNumber": "전화번호(들)",
"Add Tags": "태그 추가",
"tagAlreadyStaged": "이 태그 (이름과 값)은 이미 이 배치에 대해 단계적으로 설정되어 있습니다.",
"emailTemplateHeartbeatJSON": "하트 비트를 설명하는 객체",
"pushoverMessageTtl": "메시지 TTL(초)",
"Bark API Version": "Bark API 버전",
"Mentioning": "멘토링",
"Mention group": "{group}을(를) 멘션",
"setup a new monitor group": "새 모니터 그룹 설정",
"openModalTo": "{0}을(를) 위한 모달 열기",
"aboutSlackUsername": "메시지 발신자의 표시 이름을 변경합니다. 누군가를 언급하려면, 친숙한 이름(friendly name)에 포함하세요.",
"smseagleGroupV2": "전화번호부 그룹 ID(들)",
"smseagleContactV2": "전화번호부 연락처 ID(들)",
"smseagleMsgType": "메시지 타입",
"smseagleMsgSms": "SMS 메시지 (기본값)",
"smseagleMsgRing": "통화 울리기",
"smseagleMsgTts": "텍스트 음성 변환 통화",
"smseagleDuration": "지속 시간 (초)",
"smseagleTtsModel": "텍스트 음성 변환 모델 ID",
"smseagleApiType": "API 버전",
"smseagleApiv1": "APIv1 (기존 프로젝트 및 하위 호환용)",
"smseagleDocs": "문서 또는 APIv2 사용 가능 여부를 확인하세요: {0}",
"smseagleComma": "여러 개를 입력할 경우 쉼표로 구분해야 합니다",
"smspartnerPhoneNumberHelptext": "번호는 국제 형식 {0}, {1}이어야 하며 여러 번호는 {2}로 구분해야 합니다",
"smspartnerSenderName": "SMS 발신자 이름",
"smspartnerSenderNameInfo": "3자 이상 11자 이하의 일반 문자여야 함",
"PushDeer Server": "PushDeer 서버",
"pushDeerServerDescription": "비워 두면 공식 서버를 사용함",
"SpugPush Template Code": "템플릿 코드",
"ntfyAuthenticationMethod": "인증 방법",
"ntfyPriorityHelptextAllEvents": "모든 이벤트는 최대 우선순위로 전송됨",
"twilioApiKey": "Api 키 (선택)",
"ntfyPriorityHelptextAllExceptDown": "우선 순위가 {1}인 {0}-이벤트를 제외한 모든 이벤트가 이 우선순위로 전송됩니다",
"ntfyUsernameAndPassword": "사용자명과 비밀번호",
"Show Clickable Link": "클릭 가능한 링크 표시"
}

View file

@ -1108,5 +1108,12 @@
"templateStatus": "status",
"telegramUseTemplate": "Gebruik aangepaste bericht sjabloon",
"telegramTemplateFormatDescription": "Telegram staat het gebruik van verschillende opmaaktalen voor berichten toe, zie Telegram {0} voor specifieke details.",
"telegramUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een aangepaste sjabloon."
"telegramUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een aangepaste sjabloon.",
"Font Twemoji by Twitter licensed under": "Lettertype Twemoji van Twitter gelicentieerd onder",
"the smsplanet documentation": "de smsplanet documentatie",
"Phone numbers": "Telefoonnummers",
"Sender name": "Naam afzender",
"smsplanetNeedToApproveName": "Moet worden goedgekeurd in het clientpaneel",
"smsplanetApiToken": "Token voor de SMSPlanet API",
"smsplanetApiDocs": "Gedetailleerde informatie over het verkrijgen van API-tokens vindt u op {the_smsplanet_documentation}."
}

View file

@ -1117,5 +1117,12 @@
"templateStatus": "status",
"telegramUseTemplate": "Użyj niestandardowego szablonu wiadomości",
"telegramUseTemplateDescription": "Jeśli opcja ta jest włączona, wiadomość zostanie wysłana przy użyciu niestandardowego szablonu.",
"telegramTemplateFormatDescription": "Telegram pozwala na używanie różnych języków znaczników dla wiadomości, zobacz Telegram {0}, aby uzyskać szczegółowe informacje."
"telegramTemplateFormatDescription": "Telegram pozwala na używanie różnych języków znaczników dla wiadomości, zobacz Telegram {0}, aby uzyskać szczegółowe informacje.",
"Font Twemoji by Twitter licensed under": "Czcionka Twemoji autorstwa Twitter na licencji",
"smsplanetApiToken": "Token dla API SMSPlanet",
"smsplanetApiDocs": "Szczegółowe informacje na temat uzyskiwania tokenów API można znaleźć w {the_smsplanet_documentation}.",
"the smsplanet documentation": "dokumentacja smsplanet",
"Phone numbers": "Numery telefonów",
"Sender name": "Nazwa nadawcy",
"smsplanetNeedToApproveName": "Wymaga zatwierdzenia w panelu klienta"
}

View file

@ -355,7 +355,7 @@
"smtpDkimskipFields": "Chaves Do Cabeçalho para não assinar (Opcional)",
"alertaEnvironment": "Ambiente",
"alertaRecoverState": "Estado De Recuperação",
"smseagleEncoding": "Enviar como Unicode",
"smseagleEncoding": "Enviar como Unicode (padrão=GSM-7)",
"onebotGroupMessage": "Grupo",
"onebotPrivateMessage": "Privado",
"onebotUserOrGroupId": "ID do Grupo/Usuário",
@ -526,7 +526,7 @@
"promosmsAllowLongSMS": "Permitir SMS grandes",
"Huawei": "Huawei",
"smseagleTo": "Números Dos Telefones",
"smseaglePriority": "Prioridade da mensagem (0-9, padrão=0)",
"smseaglePriority": "Prioridade da mensagem (0-9, prioridade mais alta = 9)",
"dataRetentionTimeError": "O período de retenção tem que ser maior ou igual a 0",
"User Key": "Chave Do Usuário",
"Device": "Dispositivo",
@ -820,7 +820,7 @@
"emailTemplateHeartbeatJSON": "objeto que descreve o batimento cardíaco",
"emailTemplateMsg": "mensagem da notificação",
"emailTemplateLimitedToUpDownNotification": "disponível apenas para pulsações UP/DOWN, caso contrário, nulo",
"wayToGetFlashDutyKey": "Você pode ir em Canal -> (Selecionar um Canal) -> Integrações -> Adicionar uma nova integração, adicione um 'Uptime Kuma' para obter um endereço de push, copie a Chave de Integração no endereço. Para mais informações, visite",
"wayToGetFlashDutyKey": "Para integrar o Uptime Kuma com o Flashduty: Acesse Canais > Selecionar um canal > Integrações > Adicionar uma nova integração, escolha Uptime Kuma e copie o URL de push.",
"FlashDuty Severity": "Gravidade",
"templateMsg": "mensagem da notificação",
"templateHeartbeatJSON": "objeto que descreve o batimento cardíaco",
@ -978,7 +978,7 @@
"Add Remote Browser": "Adicionar Navegador Remoto",
"New Group": "Novo Grupo",
"Group Name": "Nome do Grupo",
"OAuth2: Client Credentials": "OAuth2: Client Credentials",
"OAuth2: Client Credentials": "OAuth2: Credenciais do Cliente",
"Authentication Method": "Método de Autenticação",
"Authorization Header": "Header de Autorização",
"ignoredTLSError": "Erros TLS/SSL foram ignorados",
@ -1082,5 +1082,62 @@
"telegramServerUrl": "(Opcional) URL do Servidor",
"Message Template": "Modelo de Mensagem",
"Template Format": "Formato do Modelo",
"Font Twemoji by Twitter licensed under": "Fonte Twemoji do Twitter licenciada sob"
"Font Twemoji by Twitter licensed under": "Fonte Twemoji do Twitter licenciada sob",
"the smsplanet documentation": "a documentação do smsplanet",
"Phone numbers": "Números de telefone",
"Sender name": "Nome do remetente",
"smsplanetNeedToApproveName": "Precisa ser aprovado no painel do cliente",
"smsplanetApiToken": "Token para a API SMSPlanet",
"smsplanetApiDocs": "Informações detalhadas sobre a obtenção de tokens de API podem ser encontradas em {the_smsplanet_documentation}.",
"defaultFriendlyName": "Novo Monitor",
"Use HTML for custom E-mail body": "Use HTML para corpo de e-mail personalizado",
"smseagleGroupV2": "ID(s) do grupo da lista telefônica",
"smseagleTtsModel": "ID do modelo de conversão de texto em fala",
"smseagleDocs": "Verifique a documentação ou a disponibilidade da APIv2: {0}",
"pingCountDescription": "Número de pacotes a enviar antes de parar",
"pingGlobalTimeoutLabel": "Tempo limite global",
"pingGlobalTimeoutDescription": "Tempo total em segundos antes que o ping pare, independentemente dos pacotes enviados",
"pingIntervalAdjustedInfo": "Intervalo ajustado com base na contagem de pacotes, tempo limite global e tempo limite por ping",
"smtpHelpText": "'SMTPS' testa se o SMTP/TLS está funcionando; 'Ignorar TLS' conecta por texto simples; 'STARTTLS' conecta, emite um comando STARTTLS e verifica o certificado do servidor. Nenhum desses métodos envia um e-mail.",
"smseagleContactV2": "ID(s) de contato da lista telefônica",
"smseagleMsgType": "Tipo de mensagem",
"smseagleMsgSms": "Mensagem SMS (padrão)",
"smseagleMsgRing": "Chamada de toque",
"smseagleMsgTts": "Chamada de texto para fala",
"smseagleMsgTtsAdvanced": "Chamada avançada de conversão de texto em fala",
"smseagleDuration": "Duração (em segundos)",
"smseagleApiType": "Versão da API",
"smseagleApiv1": "APIv1 (para projetos existentes e compatibilidade com versões anteriores)",
"smseagleApiv2": "APIv2 (recomendado para novas integrações)",
"smseagleComma": "Múltiplos devem ser separados por vírgula",
"SpugPush Template Code": "Código do modelo",
"FlashDuty Push URL": "URL de envio",
"FlashDuty Push URL Placeholder": "Copiar da página de integração de alertas",
"pingCountLabel": "Máximo de Pacotes",
"pingNumericLabel": "Saída Numérica",
"pingNumericDescription": "Se marcado, os endereços IP serão emitidos em vez de nomes de host simbólicos",
"pingPerRequestTimeoutLabel": "Tempo limite por ping",
"pingPerRequestTimeoutDescription": "Este é o tempo máximo de espera (em segundos) antes de considerar um único pacote de ping perdido",
"Custom URL": "URL personalizada",
"customUrlDescription": "Será usado como URL clicável em vez do monitor.",
"OneChatAccessToken": "Token de acesso OneChat",
"OneChatUserIdOrGroupId": "ID de usuário ou ID de grupo do OneChat",
"OneChatBotId": "ID do bot OneChat",
"Disable URL in Notification": "Desativar URL na notificação",
"ntfyPriorityHelptextPriorityHigherThanDown": "A prioridade regular deve ser maior que {0} prioridade. Prioridade {1} é maior que {0} prioridade {2}",
"ntfyPriorityDown": "Prioridade para eventos DOWN",
"tagAlreadyOnMonitor": "Esta tag (nome e valor) já está no monitor ou na adição pendente.",
"tagNameExists": "Uma tag de sistema com este nome já existe. Selecione-o na lista ou use um nome diferente.",
"Add Tags": "Adicionar Tags",
"tagAlreadyStaged": "Esta tag (nome e valor) já está adicionada para este lote.",
"Add Another Tag": "Adicione Outra Tag",
"Staged Tags for Batch Add": "Tags preparadas para adição em lote",
"Clear Form": "Limpar formulário",
"pause": "Pausar",
"Happy Eyeballs algorithm": "Algoritmo Happy Eyeballs",
"Manual": "Manual",
"Ip Family": "Família IP",
"ipFamilyDescriptionAutoSelect": "Usa {happyEyeballs} para determinar a família IP.",
"Optional: The audience to request the JWT for": "Opcional: O público deve solicitar o JWT para",
"OAuth Audience": "Público OAuth"
}

View file

@ -202,5 +202,20 @@
"Confirm": "Confirmar",
"pushOthers": "Outros",
"time ago": "{0} atrás",
"Dark": "Escuro"
"Dark": "Escuro",
"defaultFriendlyName": "Novo Monitor",
"Normal": "Normal",
"Allow indexing": "Permitir indexação",
"Discourage search engines from indexing site": "Desencorajar os mecanismos de busca de indexar o site",
"One record": "Um registro",
"topic": "Tópico",
"successKeyword": "Palavra-chave de sucesso",
"Timezone": "Fuso horário",
"PasswordsDoNotMatch": "As senhas não coincidem.",
"records": "registros",
"Current User": "Usuário Atual",
"recent": "Recente",
"Add New below or Select...": "Adicionar ou selecione um novo abaixo…",
"Bottom": "inferior",
"Default enabled": "Padrão ativado"
}

View file

@ -1,10 +1,10 @@
{
"languageName": "Русский",
"languageName": "Английский",
"checkEverySecond": "Проверка каждые {0} секунд",
"retriesDescription": "Максимальное количество попыток перед пометкой сервиса, как недоступного, и отправкой уведомления",
"ignoreTLSError": "Игнорировать ошибки TLS/SSL для HTTPS сайтов",
"upsideDownModeDescription": "Инверсия статуса. Если сервис доступен, он будет отмечен как недоступный.",
"maxRedirectDescription": "Максимальное количество перенаправлений. Поставьте 0, чтобы отключить перенаправления.",
"retriesDescription": "Максимальное число попыток перед тем, как сервис будет помечен как неработающий и будет отправлено уведомление",
"ignoreTLSError": "Игнорировать ошибки TLS/SSL для сайтов с HTTPS",
"upsideDownModeDescription": "Инвертировать статус. Если сервис доступен — он считается НЕРАБОТАЮЩИМ.",
"maxRedirectDescription": "Максимальное число перенаправлений. Установите 0, чтобы отключить перенаправления.",
"acceptedStatusCodesDescription": "Выберите коды статусов для определения доступности сервиса.",
"passwordNotMatchMsg": "Введённые пароли не совпадают.",
"notificationDescription": "Необходимо привязать уведомления к монитору чтобы они функционировали.",
@ -26,8 +26,8 @@
"Check Update On GitHub": "Проверить наличие обновления в GitHub",
"List": "Список",
"Add": "Добавить",
"Add New Monitor": "Добавить Новый Сенсор",
"Quick Stats": "Статистика",
"Add New Monitor": "Новый монитор",
"Quick Stats": "Сводка",
"Up": "Доступен",
"Down": "Не доступен",
"Pending": "В ожидании",
@ -43,7 +43,7 @@
"Delete": "Удалить",
"Current": "Текущий",
"Uptime": "Время безотказной работы",
"Cert Exp.": "Сертификат ист.",
"Cert Exp.": "Срок SSL",
"day": "день | дней",
"-day": "-дней",
"hour": "час",
@ -56,7 +56,7 @@
"URL": "URL-ссылка",
"Hostname": "Имя хоста",
"Port": "Порт",
"Heartbeat Interval": "Частота опроса",
"Heartbeat Interval": "Интервал опроса",
"Retries": "Попыток",
"Advanced": "Дополнительно",
"Upside Down Mode": "Режим инверсии статуса",
@ -208,10 +208,10 @@
"pushbullet": "Pushbullet",
"line": "Line Messenger",
"mattermost": "Mattermost",
"Primary Base URL": "Основной URL",
"Primary Base URL": "Основной URL, по которому доступен Uptime Kuma",
"Push URL": "URL-ссылка push уведомлений",
"needPushEvery": "К этому URL необходимо обращаться каждые {0} секунд.",
"pushOptionalParams": "Опциональные параметры: {0}",
"pushOptionalParams": "Необязательные параметры: {0}",
"defaultNotificationName": "Уведомления {notification} ({number})",
"here": "здесь",
"Required": "Обязательно",
@ -689,7 +689,7 @@
"Guild ID": "Идентификатор гильдии",
"Kook": "Kook",
"wayToGetKookBotToken": "Создайте приложение и получите токен бота по адресу {0}",
"Resend Notification if Down X times consecutively": "Повторная отправка уведомления при неудачном запросе X раз",
"Resend Notification if Down X times consecutively": "Повторно отправлять уведомление, если сбой произошёл X раз подряд",
"telegramProtectContent": "Запретить пересылку/сохранение",
"telegramProtectContentDescription": "Если включено, сообщения бота в Telegram будут запрещены для пересылки и сохранения.",
"telegramSendSilently": "Отправить без звука",
@ -786,7 +786,7 @@
"Badge Label Suffix": "Суффикс надписи для значка",
"Edit Maintenance": "Редактировать техобслуживание",
"Reconnecting...": "Переподключение...",
"Cannot connect to the socket server": "Невозможно подключиться к серверу",
"Cannot connect to the socket server": "Не удаётся подключиться к сокет-серверу",
"Badge Warn Color": "Цвет значка для предупреждения",
"Badge Warn Days": "Значок для \"дней предупреждения\"",
"Badge Down Days": "Значок для \"дней недоступности\"",
@ -829,7 +829,7 @@
"PushDeer Server": "Сервер PushDeer",
"pushDeerServerDescription": "Оставьте пустым для использования официального сервера",
"showCertificateExpiry": "Показывать истекающий сертификат",
"Request Timeout": "Тайм-Аут запроса",
"Request Timeout": "Таймаут ожидания",
"timeoutAfter": "Тайм-Аут через {0} секунд",
"Select": "Выбрать",
"selectedMonitorCount": "Выбрано: {0}",
@ -850,12 +850,12 @@
"nostrRelaysHelp": "Один URL-адрес ретрансляции в каждой строке",
"enableNSCD": "Включить NSCD (Name Service Cache Daemon) для кэширования всех DNS-запросов",
"Saved.": "Сохранено.",
"setupDatabaseChooseDatabase": "Какую базу данных Вы бы хотели использовать?",
"setupDatabaseEmbeddedMariaDB": "Вам не нужно ничего настраивать. В этот докер-образ автоматически встроена и настроена MariaDB. Uptime Kuma будет подключаться к этой базе данных через unix-сокет.",
"setupDatabaseSQLite": "Простой файл базы данных, рекомендуемый для небольших развертываний. До версии 2.0.0 Uptime Kuma использовал SQLite в качестве базы данных по умолчанию.",
"setupDatabaseMariaDB": "Подключитесь к внешней базе данных MariaDB. Необходимо задать информацию о подключении к базе данных.",
"setupDatabaseChooseDatabase": "Какую базу данных вы хотите использовать?",
"setupDatabaseEmbeddedMariaDB": "Ничего настраивать не нужно. Этот образ Docker уже содержит встроенную и настроенную MariaDB. Uptime Kuma будет подключаться к базе данных через Unix-сокет.",
"setupDatabaseSQLite": "Простой файл базы данных, рекомендуется для небольших установок. До версии 2.0.0 Uptime Kuma использовал SQLite в качестве базы данных по умолчанию.",
"setupDatabaseMariaDB": "Подключение к внешней базе данных MariaDB. Необходимо указать информацию для подключения.",
"dbName": "Имя базы данных",
"pushViewCode": "Как использовать монитор Push? (Посмотреть код)",
"pushViewCode": "Как настроить Push-монитор? (Показать код)",
"programmingLanguages": "Языки программирования",
"Bark API Version": "Версия Bark API",
"monitorToastMessagesDescription": "Уведомления для мониторов исчезают через заданное время в секундах. Значение -1 отключает тайм-аут. Значение 0 отключает уведомления.",
@ -914,7 +914,7 @@
"Add a Remote Browser": "Добавить удаленный браузер",
"Remote Browser not found!": "Удаленный браузер не найден!",
"remoteBrowsersDescription": "Удаленные браузеры — альтернатива локальному запуску Chromium. Установите такой сервис, как browserless.io, или подключитесь к своему собственному",
"settingUpDatabaseMSG": "Настраиваем базу данных. Это может занять некоторое время, пожалуйста подождите.",
"settingUpDatabaseMSG": "Настройка базы данных. Это может занять некоторое время, пожалуйста, подождите.",
"setup a new monitor group": "настроить новую группу мониторов",
"openModalTo": "открыть модальное окно {0}",
"Add a domain": "Добавить домен",
@ -929,8 +929,8 @@
"Mention group": "Упомянуть {group}",
"Your User ID": "Ваш идентификатор пользователя",
"Host URL": "URL Хоста",
"locally configured mail transfer agent": "Настроенный локально агент передачи почты",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Введите {Hostname} сервера, к которому вы хотите подключиться, либо {localhost}, если вы собираетесь использовать {local_mta}",
"locally configured mail transfer agent": "локальный почтовый агент",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Укажите имя хоста сервера, к которому хотите подключиться, или {localhost}, если планируете использовать {local_mta}",
"wayToGetHeiiOnCallDetails": "Как получить ID триггера и ключи API , рассказывается в {documentation}",
"gtxMessagingApiKeyHint": "Вы можете найти свой ключ API на странице: Мои учетные записи маршрутизации > Показать информацию об учетной записи > Учетные данные API > REST API (v2.x)",
"From Phone Number / Transmission Path Originating Address (TPOA)": "Номер телефона / Адрес источника пути передачи (АИПП)",
@ -978,7 +978,7 @@
"Refresh Interval Description": "Страница статуса будет полностью обновлена каждые {0} секунд",
"and": "и",
"e.g. {discordThreadID}": "например {discordThreadID}",
"ignoredTLSError": "Ошибки TLS/SSL проигнорированы",
"ignoredTLSError": "TLS/SSL ошибки не учитываются",
"Debug": "Отладка",
"Copy": "Скопировать",
"CopyToClipboardError": "Не удалось скопировать: {error}",
@ -1110,20 +1110,27 @@
"templateServiceName": "имя сервиса",
"templateHostnameOrURL": "hostname или URL",
"templateStatus": "статус",
"telegramServerUrlDescription": "Чтобы поднять ограничения API API Telegram или получить доступ к заблокированным районам (Китай, Иран и т.д.). Для получения дополнительной информации нажмите {0}. По умолчанию: {1}",
"wayToGetWahaApiKey": "Ключ API - это значение переменной среды WHATSAPP_API_KEY, которое вы использовали для запуска WAHA.",
"telegramServerUrlDescription": "Чтобы обойти ограничения API бота Telegram или получить доступ в заблокированных регионах (например, в Китае или Иране), нажмите {0} для получения подробной информации. Значение по умолчанию: {1}",
"wayToGetWahaApiKey": "Ключ API — это значение переменной окружения WHATSAPP_API_KEY, которое вы использовали для запуска WAHA.",
"wayToGetWahaSession": "Из этой сессии WAHA отправляет уведомления на удостоверение личности чата. Вы можете найти его на приборной панели Waha.",
"wayToWriteWahaChatId": "Номер телефона с международным префиксом, но без знака плюс в начале ({0}), идентификатор контакта ({1}) или идентификатора группы ({2}). Уведомления отправляются на этот идентификатор чата от сеанса Waha.",
"wahaSession": "Сессия",
"wahaChatId": "Идентификатор чата (номер телефона / идентификатор контакта / идентификатор группы)",
"wayToGetWahaApiUrl": "Ваш экземпляр WAHA URL.",
"YZJ Webhook URL": "YZJ Вебхук URL",
"YZJ Webhook URL": "URL вебхука YZJ",
"YZJ Robot Token": "YZJ Токен Робота",
"telegramServerUrl": "(Необязательно) URL Сервера",
"telegramUseTemplate": "Используйте пользовательский шаблон сообщения",
"telegramUseTemplateDescription": "Если включено, сообщение будет отправлено с помощью пользовательского шаблона.",
"telegramTemplateFormatDescription": "Telegram позволяет использовать различные языки разметки для сообщений, см. Telegram {0} для конкретных деталей.",
"telegramTemplateFormatDescription": "Telegram позволяет использовать различные языки разметки в сообщениях. Подробности смотрите в документации Telegram — {0}.",
"Plain Text": "Простой текст",
"Message Template": "Шаблон сообщения",
"Template Format": "Формат шаблона"
"Template Format": "Формат шаблона",
"Font Twemoji by Twitter licensed under": "Шрифт Twemoji от Twitter лицензирован на условиях",
"smsplanetApiToken": "Токен для API SMSPlanet",
"smsplanetApiDocs": "Подробную информацию о получении API-токенов можно найти в {the_smsplanet_documentation}.",
"the smsplanet documentation": "документация SMSPlanet",
"Phone numbers": "Номера телефонов",
"Sender name": "Имя отправителя",
"smsplanetNeedToApproveName": "Требуется одобрение в панели клиента"
}

View file

@ -582,5 +582,171 @@
"Json Query Expression": "Výraz dotazu JSON",
"ignoredTLSError": "Chyby TLS/SSL boli ignorované",
"Add a new expiry notification day": "Pridať nové oznámenie o vypršaní platnosti",
"chromeExecutable": "Spustitelný súbor Chrome/Chromium"
"chromeExecutable": "Spustitelný súbor Chrome/Chromium",
"templateHostnameOrURL": "názov hostiteľa alebo URL",
"templateStatus": "stav",
"templateServiceName": "názov služby",
"telegramTemplateFormatDescription": "Telegram umožňuje používať rôzne značkovacie jazyky pre správy, podrobnosti nájdete v článku Telegram {0}.",
"recurringIntervalMessage": "Spustiť raz denne | Spustiť raz každých {0} dní",
"affectedMonitorsDescription": "Vyberte monitoringy, ktorých sa týka aktuálna údržba",
"keywordDescription": "Vyhľadajte kľúčové slovo v obyčajnej HTML alebo JSON odpovedi. Vyhľadávanie rozlišuje veľké a malé písmená.",
"backupDescription": "Všetky monitory a upozornenia si môžete zálohovať do súboru JSON.",
"backupDescription2": "Poznámka: história a údaje o udalostiach nie sú zahrnuté.",
"pushoversounds climb": "Stúpanie (dlhé)",
"pushoversounds persistent": "Trvalé (dlhé)",
"pushoversounds echo": "Pushover Echo (dlhé)",
"pushoversounds updown": "Hore dole (dlhé)",
"pushoversounds vibrate": "Iba vibrovanie",
"pushoversounds none": "Žiadne (tiché)",
"pushoversounds alien": "Mimozemský alarm (dlhý)",
"Apprise URL": "URL adresa oznámenia",
"Example:": "Príklad: {0}",
"Read more:": "Čítajte viac: {0}",
"LunaSea Device ID": "LunaSea ID zariadenia",
"Proxy Protocol": "Proxy protokol",
"Setup Proxy": "Nastavenie proxy servera",
"telegramUseTemplate": "Použiť vlastnú šablónu správy",
"telegramUseTemplateDescription": "Ak je povolená, správa sa odošle pomocou vlastnej šablóny.",
"Use HTML for custom E-mail body": "Použitie HTML v tele e-mailu",
"twoFAVerifyLabel": "Zadajte svoj token na overenie 2FA:",
"confirmEnableTwoFAMsg": "Naozaj chcete povoliť 2FA?",
"confirmDisableTwoFAMsg": "Naozaj chcete vypnúť 2FA?",
"affectedStatusPages": "Zobraziť túto správu o údržbe na vybraných stavových stránkach",
"notificationDescription": "Aby upozornenia fungovali, musia byť priradené k monitoringu.",
"invertKeywordDescription": "Hľadajte, či kľúčové slovo chýba, skôr ako to, že je prítomné.",
"backupDescription3": "Citlivé údaje, ako napríklad tokeny oznámení, sú zahrnuté v exportovanom súbore; export si, prosím, bezpečne uložte.",
"pushyAPIKey": "Tajný kľúč API",
"wayToGetKookBotToken": "Vytvorte si aplikáciu a získajte token bota na adrese {0}",
"wayToGetKookGuildID": "V nastavení Kook zapnite „Režim vývojára“ a kliknite pravým tlačidlom myši na guild, čím získate jeho ID",
"pushoverDesc1": "Priorita núdze (2) má predvolený 30-sekundový časový limit medzi opakovanými pokusmi a jeho platnosť vyprší po 1 hodine.",
"octopushLegacyHint": "Používate staršiu verziu Octopush (2011 2020) alebo novú verziu?",
"octopushSMSSender": "Meno odosielateľa SMS: 3 11 alfanumerických znakov a medzera (a z, A Z, 0 9)",
"You can divide numbers with": "Čísla môžete oddeliť pomocou",
"goAlertInfo": "GoAlert je aplikácia s otvoreným zdrojovým kódom na plánovanie pohotovostí, automatizované eskalácie a upozornenia (ako sú SMS alebo hlasové hovory). Automaticky oslovte správnu osobu správnym spôsobom a v správnom čase! {0}",
"For safety, must use secret key": "Z bezpečnostných dôvodov je potrebné použiť tajný kľúč",
"promosmsTypeFlash": "SMS FLASH Správa sa automaticky zobrazí na zariadení príjemcu. Obmedzené len na poľských príjemcov.",
"promosmsPhoneNumber": "Telefónne číslo (pre poľského príjemcu, môžete vynechať smerové čísla)",
"matrixDesc1": "Interné ID miestnosti nájdete v rozšírenej sekcii nastavení miestnosti vo vašom klientovi Matrix. Malo by vyzerať takto: !QMdRCpUIfLwsfjxye6:home.server.",
"matrixDesc2": "Dôrazne sa odporúča vytvoriť nového používateľa a nepoužívať vlastný prístupový token používateľa Matrix, pretože vám to umožní plný prístup k vášmu účtu a všetkým miestnostiam, ku ktorým ste sa pripojili. Namiesto toho vytvorte nového používateľa a pozvite ho iba do miestnosti, v ktorej chcete dostávať upozornenia. Prístupový token získate spustením príkazu {0}",
"aboutKumaURL": "Ak necháte pole URL adresy Uptime Kuma prázdne, predvolene sa zobrazí stránka projektu GitHub.",
"PhoneNumbers": "Telefónne čísla",
"telegramServerUrl": "(Voliteľné) URL adresa servera",
"telegramServerUrlDescription": "Ak chcete zrušiť obmedzenia rozhrania API botov Telegramu alebo získať prístup v blokovaných oblastiach (Čína, Irán atď.), kliknite na {0}. Predvolené: {1}",
"Device": "Zariadenie",
"aboutSlackUsername": "Zmení zobrazované meno odosielateľa správy. Ak chcete niekoho spomenúť, uveďte ho v poli obecný názov.",
"smtpDkimSettings": "Nastavenia DKIM",
"defaultFriendlyName": "Nový Monitor",
"promosmsPassword": "Heslo API",
"WebHookUrl": "URL webhooku",
"Add Tags": "Pridať značky",
"tagAlreadyStaged": "Táto značka (názov a hodnota) je už pripravená pre tento batch.",
"tagAlreadyOnMonitor": "Táto značka (názov a hodota) je už použitá na inom monitoringu alebo čaká na pridanie.",
"tagNameExists": "Systémová značka s týmto názvom už existuje. Vyberte ju zo zoznamu alebo použite iný názov.",
"octopushAPIKey": "„Kľúč API“ z prihlasovacích údajov HTTP API v ovládacom paneli",
"octopushLogin": "„Prihlásenie“ z prihlasovacích údajov HTTP API v ovládacom paneli",
"pushoversounds pushover": "Pushover (predvolené)",
"pushoversounds bike": "Bicykel",
"pushoversounds bugle": "Trúbka",
"pushoversounds cashregister": "Pokladňa",
"pushoversounds classical": "Klasická",
"pushoversounds cosmic": "Kozmický",
"pushoversounds falling": "Padajúce",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Prichádzajúce",
"pushoversounds intermission": "Prestávka",
"pushoversounds magic": "Kúzlo",
"pushoversounds mechanical": "Mechanické",
"pushoversounds pianobar": "Klávesa klavíra",
"pushoversounds siren": "Siréna",
"pushoversounds spacealarm": "Vesmírny alarm",
"pushoversounds tugboat": "Remorkér",
"pushyToken": "Token zariadenia",
"apprise": "Apprise (Podpora viac ako 50 notifikačných služieb)",
"GoogleChat": "Google Chat (iba Google Workspace)",
"Guild ID": "Guild ID",
"User Key": "Používateľský kľúč",
"Message Title": "Názov správy",
"Notification Sound": "Zvuk upozornenia",
"More info on:": "Viac informácií o: {0}",
"pushoverMessageTtl": "Správa TTL (sekundy)",
"pushoverDesc2": "Ak chcete posielať upozornenia na rôzne zariadenia, vyplňte pole Zariadenia.",
"SMS Type": "Typ SMS",
"octopushTypeLowCost": "Nízka cena (Pomalé - niekedy blokované operátorom)",
"octopushTypePremium": "Prémiový (Rýchle odporúča sa pre upozornenia)",
"checkPrice": "Skontrolujte ceny {0}:",
"apiCredentials": "Prihlasovacie údaje API",
"Check octopush prices": "Skontrolujte ceny Octopush {0}.",
"octopushPhoneNumber": "Telefónne číslo (medzinárodný formát, napr.: +33612345678) ",
"Status:": "Stav: {0}",
"Strategy": "Stratégia",
"Free Mobile User Identifier": "Bezplatný identifikátor mobilného používateľa",
"Free Mobile API Key": "Bezplatný kľúč mobilného API",
"Enable TLS": "Povoliť TLS",
"Proto Service Name": "Proto názov služby",
"Proto Method": "Proto metóda",
"Proto Content": "Proto obsah",
"Economy": "Úsporná",
"Lowcost": "Nízkonákladový",
"high": "vysoká",
"SendKey": "Kľúč k odoslaniu",
"SMSManager API Docs": "Dokumentácia k rozhraniu SMSManager API ",
"Gateway Type": "Typ brány",
"Base URL": "Základná URL adresa",
"goAlertIntegrationKeyInfo": "Získajte generický integračný kľúč API pre službu v tomto formáte „aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee“, zvyčajne hodnota parametra tokenu skopírovanej URL adresy.",
"AccessKeyId": "ID prístupového kľúča",
"SecretAccessKey": "Tajný prístupový kľúč",
"TemplateCode": "Kód šablóny",
"SignName": "Názov podpisu",
"Sms template must contain parameters: ": "Šablóna SMS musí obsahovať parametre: ",
"Bark API Version": "Verzia Bark API",
"Bark Endpoint": "Bark Endpoint",
"Bark Group": "Bark skupina",
"Bark Sound": "Bark zvuk",
"SecretKey": "Tajný Kľúč",
"Mentioning": "Zmienky",
"Don't mention people": "Nespomínajte ľudí",
"Mention group": "Spomeňte {skupinu}",
"Device Token": "Token zariadenia",
"Platform": "Platforma",
"Huawei": "Huawei",
"High": "Vysoký",
"Retry": "Opakovať",
"Topic": "Téma",
"WeCom Bot Key": "Kľúč WeCom Bota",
"Proxy Server": "Proxy server",
"Proxy server has authentication": "Proxy server má overenie",
"promosmsTypeEco": "SMS ECO lacné, ale pomalé a často preťažené. Obmedzené len na poľských príjemcov.",
"promosmsTypeFull": "SMS PLNÁ Prémiová úroveň SMS, môžete použiť meno odosielateľa (najprv si musíte meno zaregistrovať). Spoľahlivé pre upozornenia.",
"promosmsTypeSpeed": "RÝCHLA SMS - Najvyššia priorita v systéme. Veľmi rýchle a spoľahlivé, ale drahé (približne dvojnásobok ceny PLNEJ SMS).",
"promosmsSMSSender": "Meno odosielateľa SMS: Predregistrované meno alebo jedno z predvolených: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
"promosmsAllowLongSMS": "Povoliť dlhé SMS",
"Feishu WebHookUrl": "URL adresa webhooku Feishu",
"matrixHomeserverURL": "URL adresa domáceho servera (s http(s://) a voliteľným portom)",
"Internal Room Id": "Vnútorné ID miestnosti",
"Channel Name": "Názov kanála",
"Notify Channel": "Upozorniť kanál",
"aboutNotifyChannel": "Upozornenie kanála spustí upozornenie na ploche alebo mobilnom zariadení pre všetkých členov kanála, bez ohľadu na to, či sú aktívni alebo neprítomní.",
"Uptime Kuma URL": "URL adresa Uptime Kuma",
"setup a new monitor group": "nastaviť novú skupinu pre monitoring",
"openModalTo": "otvoriť modálne okno do {0}",
"Add a domain": "Pridať doménu",
"Remove domain": "Odstrániť doménu „{0}“",
"Icon Emoji": "Ikona emotikonu",
"signalImportant": "DÔLEŽITÉ: V zozname príjemcov nemôžete kombinovať skupiny a čísla!",
"aboutWebhooks": "Viac informácií o webhookoch na: {0}",
"aboutChannelName": "Ak chcete obísť kanál Webhook, zadajte názov kanála do poľa Názov kanála {0}. Napr.: #iny-kanal",
"smtpDkimDesc": "Použitie nájdete v dokumentácii Nodemailer DKIM {0}.",
"documentation": "dokumentácia",
"smtpDkimDomain": "Názov domény",
"smtpDkimKeySelector": "Výber kľúča",
"smtpDkimPrivateKey": "Súkromný kľúč",
"smtpDkimHashAlgo": "Hašovací algoritmus (voliteľné)",
"importHandleDescription": "Ak chcete preskočiť všetky monitory alebo upozornenia s rovnakým názvom, vyberte možnosť „Preskočiť existujúce“. Možnosť „Prepísať“ vymaže všetky existujúce monitory a upozornenia.",
"confirmImportMsg": "Naozaj chcete importovať zálohu? Skontrolujte, či ste vybrali správnu možnosť importu.",
"atLeastOneMonitor": "Vyberte aspoň jeden monitor, ktorého sa to týka",
"passwordNotMatchMsg": "Opakované heslo sa nezhoduje.",
"jsonQueryDescription": "Analyzujte a extrahujte konkrétne údaje z JSON odpovede servera pomocou JSON dotazu alebo použite „$“ pre surovú odpoveď, ak sa neočakáva JSON. Výsledok sa potom porovná s očakávanou hodnotou ako reťazec. Dokumentáciu nájdete v {0} na experimentovanie s dotazmi {1}.",
"promosmsLogin": "Prihlasovacie meno API",
"tokenValidSettingsMsg": "Token je platný! Teraz môžete uložiť nastavenia 2FA.",
"endpoint": "koncový bod (endpoint)"
}

View file

@ -28,20 +28,20 @@
"confirmDisableTwoFAMsg": "คุณแน่ใจหรือไม่ที่จะปิดใช้งาน 2FA?",
"Settings": "การตั้งค่า",
"Dashboard": "แผงควบคุม",
"New Update": "อัพเดทใหม่",
"New Update": "อัปเดตใหม่",
"Language": "ภาษา",
"Appearance": "หน้าตา",
"Appearance": "ลักษณะการแสดงผล",
"Theme": "ธีม",
"General": "ทั่วไป",
"Primary Base URL": "URL หลัก",
"Version": "เวอร์ชัน",
"Version": "เวอร์ชัน",
"Check Update On GitHub": "ตรวจสอบการอัปเดตบน GitHub",
"List": "รายการ",
"Add": "เพิ่ม",
"Add New Monitor": "เพิ่มมอนิเตอร์ใหม่",
"Quick Stats": "สถิติอย่างย่อ",
"Up": "ใช้งานได้",
"Down": "ไม่สามารถใช้งานได้",
"Quick Stats": "สรุปสถานะ",
"Up": "ทำงานปกติ",
"Down": "ทำงานล้มเหลว",
"Pending": "รอดำเนินการ",
"Unknown": "ไม่ทราบ",
"Pause": "หยุดชั่วคราว",
@ -98,12 +98,12 @@
"Current Password": "รหัสผ่านปัจจุบัน",
"New Password": "รหัสผ่านใหม่",
"Repeat New Password": "ยืนยันรหัสผ่านใหม่",
"Update Password": "อัพเดทรหัสผ่าน",
"Update Password": "อัปเดตรหัสผ่าน",
"Disable Auth": "ปิดใช้งานการตรวจสอบสิทธิ์",
"Enable Auth": "เปิดใช้งานการตรวจสอบสิทธิ์",
"disableauth.message1": "คุณต้องการที่จะ {disableAuth}?",
"disable authentication": "ปิดใช้งานระบบรับรองความถูกต้องใช่หรือไม่",
"disableauth.message2": "ระบบนี้ถูกออกแบบมาเพื่อการใช้งานกับระบบรับรองความถูกต้องของบุคคลที่สามเช่น Cloudflare Access, Authelia หรือวิธีการอื่นๆ",
"disableauth.message2": "ถูกออกแบบมาสำหรับกรณีที่มี {intendThirdPartyAuth} อยู่หน้าระบบ Uptime Kuma เช่น Cloudflare Access, Authelia หรือกลไกการตรวจสอบสิทธิ์อื่น ๆ",
"Please use this option carefully!": "โปรดใช้ความระมัดระวังในการเลือกใช้งานระบบนี้ !",
"Logout": "ออกจากระบบ",
"Leave": "ออก",
@ -174,7 +174,7 @@
"Purple": "ม่วง",
"Pink": "ชมพู",
"Search...": "ค้นหา…",
"Avg. Ping": "ค่า Ping เฉลี่ย",
"Avg. Ping": "ค่าปิงเฉลี่ย",
"Avg. Response": "ค่า Response เฉลี่ย",
"Entry Page": "หน้าต้อนรับ",
"statusPageNothing": "ไม่มีอะไรตรงนี้ !, กรุณาเพิ่มกลุ่มหรือมอนิเตอร์",
@ -300,7 +300,7 @@
"Internal Room Id": "รหัสห้องภายใน",
"matrixDesc1": "คุณค้นหารหัสห้องภายในได้โดยดูในส่วนขั้นสูงของการตั้งค่าห้องในไคลเอ็นต์ Matrix มันควรจะมีลักษณะเช่น !PMdRCpsIfLwsfjIye6:kiznick.server.",
"matrixDesc2": "ขอแนะนำเป็นอย่างยิ่งให้คุณสร้างผู้ใช้ใหม่และอย่าใช้โทเค็นการเข้าถึงของผู้ใช้ Matrix ของคุณเอง เนื่องจากจะทำให้สามารถเข้าถึงบัญชีของคุณและห้องทั้งหมดที่คุณเข้าร่วม ให้สร้างผู้ใช้ใหม่และเชิญเฉพาะห้องที่คุณต้องการรับการแจ้งเตือนแทน คุณสามารถรับโทเค็นเพื่อการเข้าถึงได้โดยเรียกใช้ {0}",
"Method": "วิธี",
"Method": "เมทอด",
"Body": "เนื้อหา",
"Headers": "ส่วนหัว",
"PushUrl": "Push URL",
@ -340,7 +340,7 @@
"Post": "โพสต์",
"Please input title and content": "กรุณาใส่ชื่อและเนื้อหา",
"Created": "สร้าง",
"Last Updated": "อัพเดทล่าสุด",
"Last Updated": "อัปเดตล่าสุด",
"Unpin": "เลิกตรึง",
"Switch to Light Theme": "เปลี่ยนเป็นแบบสว่าง",
"Switch to Dark Theme": "เปลี่ยนเป็นแบบมืด",
@ -380,7 +380,7 @@
"alertaApiKey": "กุญแจ API",
"alertaAlertState": "แจ้งเตือนสถานะ",
"alertaRecoverState": "กู้คืนสถานะ",
"deleteStatusPageMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้",
"deleteStatusPageMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้?",
"Proxies": "พร็อกซี",
"default": "ค่าเริ่มต้น",
"enabled": "เปิดใช้งานแล้ว",
@ -462,12 +462,12 @@
"PushDeer Key": "กุญแจ PushDeer",
"Footer Text": "ข้อความส่วนท้าย",
"Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"",
"Domain Names": "Domain Names",
"Domain Names": "ชื่อโดเมน",
"signedInDisp": "เข้าใช้งานในฐานะ {0}",
"signedInDispDisabled": "ปิดการยืนยันตัวตน",
"Certificate Expiry Notification": "แจ้งเตือนใบรับรองหมดอายุ",
"API Username": "API Username",
"API Key": "API Key",
"API Username": "ชื่อผู้ใช้ของ API",
"API Key": "API คีย์",
"Recipient Number": "หมายเลขผู้รับ",
"From Name/Number": "จาก ชื่อ / หมายเลข",
"Leave blank to use a shared sender number.": "ไม่ต้องกรอกเพื่อใช้ชื่อผู้ส่งร่วมกัน",
@ -477,32 +477,32 @@
"octopushAPIKey": "\"API key\" จากข้อมูลยืนยันตัวตน HTTP API ในแผงควบคุม",
"octopushLogin": "\"Login\" จากข้อมูลยืนยันตัวตน HTTP API ในแผงควบคุม",
"promosmsLogin": "API Login Name",
"promosmsPassword": "API Password",
"promosmsPassword": "รหัสผ่าน API",
"pushoversounds pushover": "Pushover (default)",
"pushoversounds bike": "Bike",
"pushoversounds bugle": "Bugle",
"pushoversounds cashregister": "Cash Register",
"pushoversounds bike": "จักรยาน",
"pushoversounds bugle": "บักเกิล",
"pushoversounds cashregister": "เครื่องคิดเงิน",
"pushoversounds classical": "Classical",
"pushoversounds cosmic": "Cosmic",
"pushoversounds falling": "Falling",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Incoming",
"pushoversounds intermission": "Intermission",
"pushoversounds magic": "Magic",
"pushoversounds mechanical": "Mechanical",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Siren",
"pushoversounds spacealarm": "Space Alarm",
"pushoversounds tugboat": "Tug Boat",
"pushoversounds alien": "Alien Alarm (long)",
"pushoversounds climb": "Climb (long)",
"pushoversounds cosmic": "คอสมิก",
"pushoversounds falling": "ตก",
"pushoversounds gamelan": "ระนาด",
"pushoversounds incoming": "กำลังมา",
"pushoversounds intermission": "ช่วงพัก",
"pushoversounds magic": "แมจิก",
"pushoversounds mechanical": "เครื่องกล",
"pushoversounds pianobar": "เปียโนบาร์",
"pushoversounds siren": "ไซเรน",
"pushoversounds spacealarm": "สัญญาณเตือนอวกาศ",
"pushoversounds tugboat": "เรือโยง",
"pushoversounds alien": "แจ้งเตือน เอเลี่ยน (ยาว)",
"pushoversounds climb": "ไต่เขา (ยาว)",
"pushoversounds persistent": "Persistent (long)",
"pushoversounds echo": "Pushover Echo (long)",
"pushoversounds updown": "Up Down (long)",
"pushoversounds vibrate": "Vibrate Only",
"pushoversounds none": "None (silent)",
"pushoversounds updown": "ขึ้นลง (ยาว)",
"pushoversounds vibrate": "สั่นอย่างเดียว",
"pushoversounds none": "ไม่มี (เงียบ)",
"pushyAPIKey": "Secret API Key",
"pushyToken": "Device token",
"pushyToken": "โทเคน ของอุปกรณ์",
"Show update if available": "แสดงการอัปเดตถ้ามี",
"Also check beta release": "ตรวจสอบรุ่นเบต้า",
"Using a Reverse Proxy?": "ใช้ Reverse Proxy อยู่ใช่มั้ย?",
@ -534,7 +534,7 @@
"Bark Sound": "เสียงประกาศ",
"Authentication": "การตรวจสอบสิทธิ์",
"HTTP Headers": "HTTP Headers",
"Trust Proxy": "Trust Proxy",
"Trust Proxy": "เชื่อถือพร็อกซี",
"HomeAssistant": "Home Assistant",
"RadiusSecret": "Radius Secret",
"RadiusSecretDescription": "แบ่งปันคีย์ลับระหว่างผู้ใช้งานและเซิร์ฟเวอร์",
@ -553,9 +553,9 @@
"socket": "Socket",
"tcp": "TCP / HTTP",
"Docker Container": "Docker Container",
"Container Name / ID": "Container Name / ID",
"Docker Host": "Docker Host",
"Docker Hosts": "Docker Hosts",
"Container Name / ID": "ชื่อ / ไอดี ของคอนเทนเนอร์",
"Docker Host": "โฮสต์ของ Docker",
"Docker Hosts": "โฮสต์ของ Docker",
"ntfy Topic": "หัวข้อ ntfy",
"Domain": "โดเมน",
"Workstation": "Workstation",
@ -576,7 +576,7 @@
"Frontend Version": "เวอร์ชั่น Frontend",
"Frontend Version do not match backend version!": "เวอร์ชั่น Frontend ไม่ตรงกับ Backend !",
"webhookAdditionalHeadersTitle": "Header เพิ่มเติม",
"webhookAdditionalHeadersDesc": "กำหนด Header ที่จะส่งไปหร้อมกับ Webhook",
"webhookAdditionalHeadersDesc": "กำหนด Header ที่จะส่งไปหร้อมกับ Webhook โดยแต่ละ header ควรระบุในรูปแบบ key/value แบบ JSON",
"Start of maintenance": "เริ่มการซ่อมบำรุง",
"All Status Pages": "หน้าสถานะทั้งหมด",
"Custom": "กำหนดเอง",
@ -592,8 +592,8 @@
"Pick Affected Monitors...": "เลือกมอนิเตอร์ที่ได้รับผลกระทบ…",
"Packet Size": "ขนาดของ Packet",
"ZohoCliq": "ZohoCliq",
"backupOutdatedWarning": "ไม่ได้รับการพัฒนาแล้ว : ไม่สามารถสร้างหรือกูข้อมูลสำรองได้สมบูรณ์ เนื่องจากมีฟีเจอร์ใหม่เพิ่มขึ้นมากและการแบ็คอัพไม่ได้ถูกพัฒนา",
"backupRecommend": "กรุณาแบ็คอัพข้อมูลทั้งหมดหรือโฟลเดอร์ Data (./data/) โดยตรงแทน",
"backupOutdatedWarning": "ไม่ได้รับการพัฒนาแล้ว : ไม่สามารถสร้างหรือกูข้อมูลสำรองได้สมบูรณ์ เนื่องจากมีฟีเจอร์ใหม่เพิ่มขึ้นมากและการแบ็กอัปไม่ได้ถูกพัฒนา",
"backupRecommend": "กรุณาแบ็กอัปข้อมูลทั้งหมดหรือโฟลเดอร์ Data (./data/) โดยตรงแทน",
"Optional": "ไม่จำเป็น",
"squadcast": "Squadcast",
"or": "หรือ",
@ -638,7 +638,7 @@
"lastDay3": "วันที่ 3 สุดท้ายของเดือน",
"lastDay4": "วันที่ 4 สุดท้ายของเดือน",
"No Maintenance": "ไม่มีการบำรุงรักษา",
"pauseMaintenanceMsg": "แน่ใจไหมว่าต้องการหยุดชั่วคราว",
"pauseMaintenanceMsg": "แน่ใจไหมว่าต้องการหยุดชั่วคราว?",
"Display Timezone": "แสดงเขตเวลา",
"statusPageMaintenanceEndDate": "จบ",
"Server Timezone": "เขตเวลาเซิร์ฟเวอร์",
@ -647,7 +647,7 @@
"telegramProtectContentDescription": "หากเปิดใช้งาน ข้อความบอทใน Telegram จะได้รับการปกป้องจากการส่งต่อและการบันทึก",
"dnsCacheDescription": "อาจจะทำงานไม่ได้กับ IPv6, ปิดใช้งานถ้าเจอปัญหา",
"IconUrl": "URL ไอคอน",
"Enable DNS Cache": "เปิดใช้งาน DNS Cache",
"Enable DNS Cache": "(เลิกใช้แล้ว) เปิดใช้งานแคช DNS สำหรับตัวตรวจสอบ HTTP(s)",
"Enable": "เปิดใช้งาน",
"Disable": "ปิดใช้งาน",
"Single Maintenance Window": "หน้าการปรับปรุงเดี่ยว",
@ -676,12 +676,12 @@
"timeoutAfter": "หมดเวลาหลังจาก {0} วินาที",
"Select": "เลือก",
"Expected Value": "ค่าที่คาดหวัง",
"setupDatabaseChooseDatabase": "ฐานข้อมูลไหนที่ต้องการใช้งาน?",
"setupDatabaseChooseDatabase": "คุณต้องการใช้ฐานข้อมูลใด?",
"setupDatabaseEmbeddedMariaDB": "คุณไม่จำเป็นต้องทำอะไร Docker image จะสร้างและตั่งค่า MariaDB ให้โดยอัตโนมัติ Uptime Kuma จะเชื่อมต่อกับฐานข้อมูลนี้ด้วย unix socket",
"setupDatabaseMariaDB": "เชื่อมต่อไปยัง MariaDB ภายนอก คุณจำเป็นจะต้องตั่งค่าการเชื่อมต่อฐานข้อมูล",
"setupDatabaseSQLite": "ไฟล์ฐานข้อมูลอย่างง่าย แนะนำสำหรับการปรับใช้ขนาดเล็ก ก่อนเวอร์ชัน 2.0.0 Uptime Kuma ใช้ SQLite เป็นฐานข้อมูลเริ่มต้น",
"dbName": "ชื่อฐานข้อมูล",
"Passive Monitor Type": "ชนิดมอนิเตอร์แบบพาสซีฟ",
"Passive Monitor Type": "ประเภทมอนิเตอร์แบบพาสซีฟ",
"documentationOf": "{0} คู่มือ",
"successDeleted": "ลบสำเร็จ.",
"Command": "คำสั่ง",
@ -703,12 +703,293 @@
"ignoreTLSErrorGeneral": "ละเว้นข้อผิดพลาด TLS/SSL สำหรับการเชื่อมต่อ",
"programmingLanguages": "ภาษาโปรแกรมมิ่ง",
"Invert Keyword": "คำสำคัญ",
"settingUpDatabaseMSG": "กำลังตั้งค่าฐานข้อมูลอาจใช้เวลาสักครู่ โปรดอดทนรอ",
"settingUpDatabaseMSG": "การตั้งค่าฐานข้อมูล อาจต้องใช้เวลาสักระยะหนึ่ง โปรดอดใจรอ",
"time ago": "{0} ที่ผ่านมา",
"-year": "-ปี",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "ป้อนชื่อโฮสต์ของเซิร์ฟเวอร์ที่คุณต้องการเชื่อมต่อหรือ {localhost} หากคุณต้องการใช้ {local_mta}",
"Request Timeout": "หมดเวลาการเชื่อมต่อ",
"ignoredTLSError": "ข้อผิดพลาด TLS/SSL ถูกละเว้น",
"pushOthers": "อื่น ๆ",
"pushViewCode": "วิธีใช้งาน Push monitor (ดูโค้ด)"
"pushViewCode": "วิธีใช้งาน Push monitor (ดูโค้ด)",
"templateServiceName": "ชื่อบริการ",
"templateHostnameOrURL": "ชื่อโฮสต์หรือ URL",
"templateStatus": "สถานะ",
"webhookBodyCustomOption": "เนื้อหากำหนดเอง",
"Reset Token": "รีเซ็ตโทเคน",
"apiKeyAddedMsg": "คีย์ API ของคุณถูกเพิ่มเรียบร้อยแล้ว โปรดจดบันทึกไว้ เนื่องจากจะไม่แสดงอีกครั้ง",
"wayToGetSevenIOApiKey": "ไปที่แดชบอร์ดที่ app.seven.io > develope > api key > ปุ่มเพิ่มสีเขียว",
"filterActivePaused": "หยุดชั่วคราว",
"Search monitored sites": "ค้นหาเว็บไซต์ที่ตรวจสอบ",
"liquidIntroduction": "การใช้เทมเพลตสามารถทำได้ผ่านภาษาการสร้างเทมเพลต Liquid โปรดดูที่ {0} สำหรับคำแนะนำในการใช้งาน ตัวแปรที่ใช้ได้มีดังนี้:",
"templateLimitedToUpDownCertNotifications": "ใช้ได้เฉพาะสำหรับการแจ้งเตือนสถานะ UP/DOWN/การหมดอายุของใบรับรอง",
"selectedMonitorCount": "ที่เลือกไว้: {0}",
"statusPageSpecialSlugDesc": "Slug พิเศษ {0}: หน้านี้จะแสดงเมื่อไม่มีการระบุ slug",
"Add a new expiry notification day": "เพิ่มวันแจ้งเตือนการหมดอายุใหม่",
"templateMonitorJSON": "ออบเจ็กต์ที่อธิบายเกี่ยวกับตัวตรวจสอบ",
"templateLimitedToUpDownNotifications": "ใช้ได้เฉพาะสำหรับการแจ้งเตือนสถานะ UP/DOWN",
"webhookBodyPresetOption": "ค่าที่ตั้งไว้ล่วงหน้า - {0}",
"Check/Uncheck": "เลือก/ไม่เลือก",
"Learn More": "เรียนรู้เพิ่มเติม",
"Add API Key": "เพิ่มคีย์ API",
"templateMsg": "ข้อความการแจ้งเตือน",
"Json Query Expression": "นิพจน์สำหรับดึงข้อมูล JSON",
"locally configured mail transfer agent": "ตัวส่งอีเมลในเครื่อง",
"filterActive": "กำลังทำงาน",
"successKeyword": "คำสำเร็จ (Success Keyword)",
"smseagleContact": "ชื่อผู้ติดต่อในสมุดโทรศัพท์",
"smspartnerApiurl": "คุณสามารถหาคีย์ API ของคุณได้ในแดชบอร์ดที่ {0}",
"smspartnerPhoneNumber": "หมายเลขโทรศัพท์",
"smspartnerSenderName": "ชื่อผู้ส่ง SMS",
"Remove the expiry notification": "ลบวันแจ้งเตือนการหมดอายุ",
"Refresh Interval": "ช่วงเวลารีเฟรช",
"Refresh Interval Description": "หน้าสถานะจะทำการรีเฟรชเว็บไซต์ทั้งหมดทุก ๆ {0} วินาที",
"noDockerHostMsg": "ไม่พร้อมใช้งาน กรุณาตั้งค่า Docker Host ก่อน",
"tailscalePingWarning": "เพื่อที่จะใช้ตัวตรวจสอบ Tailscale Ping คุณต้องติดตั้ง Uptime Kuma โดยไม่ใช้ Docker และติดตั้ง Tailscale client บนเซิร์ฟเวอร์ของคุณด้วย",
"telegramUseTemplate": "ใช้เทมเพลต ข้อความที่กำหนดเอง",
"telegramUseTemplateDescription": "หากเปิดใช้งาน ข้อความจะถูกส่งโดยใช้เทมเพลตที่กำหนดเอง",
"telegramTemplateFormatDescription": "Telegram อนุญาตให้ใช้ภาษามาร์กอัปต่าง ๆ กับข้อความ ดูรายละเอียดเพิ่มเติมได้ที่ Telegram {0}",
"telegramServerUrl": "(ไม่บังคับ) URL ของเซิร์ฟเวอร์",
"telegramServerUrlDescription": "เพื่อยกระดับข้อจำกัดของ API ของ Telegram หรือให้เข้าถึงพื้นที่ที่ถูกบล็อก (จีน, อิหร่าน, เป็นต้น) สำหรับข้อมูลเพิ่มเติมคลิก {0}. ค่าเริ่มต้น: {1}",
"enableNSCD": "เปิดใช้งาน NSCD (Name Service Cache Daemon) สำหรับการแคชคำขอ DNS ทั้งหมด",
"emailCustomisableContent": "เนื้อหาที่ปรับแต่งได้",
"smtpLiquidIntroduction": "สองฟิลด์ต่อไปนี้สามารถใช้เทมเพลตผ่านภาษาการสร้างเทมเพลต Liquid โปรดดูที่ {0} สำหรับคำแนะนำในการใช้งาน ตัวแปรที่ใช้ได้มีดังนี้:",
"emailTemplateMsg": "ข้อความของการแจ้งเตือน",
"postToExistingThread": "โพสต์ไปยังเธรด / โพสต์ฟอรัมที่มีอยู่แล้ว",
"whatHappensAtForumPost": "สร้างโพสต์ในฟอรัมใหม่ จะไม่โพสต์ข้อความในโพสต์เดิม หากต้องการโพสต์ในโพสต์เดิมให้ใช้ “{option}”",
"wayToGetDiscordThreadId": "การรับค่า ID ของเธรดหรือโพสต์ในฟอรัมจะคล้ายกับการรับ Channel ID อ่านเพิ่มเติมเกี่ยวกับวิธีการรับ ID ได้ที่ {0}",
"infiniteRetention": "ตั้งค่าเป็น 0 เพื่อการเก็บข้อมูลตลอดไป",
"confirmDeleteTagMsg": "คุณแน่ใจหรือว่าต้องการลบแท็กนี้? มอนิเตอร์ที่ใช้กับแท็กนี้จะไม่ได้ถูกลบ",
"affectedMonitorsDescription": "เลือกมอนิเตอร์ที่ได้รับผลกระทบจากการซ่อมบำรุงปัจจุบัน",
"affectedStatusPages": "แสดงข้อความการซ่อมบำรุงนี้บนหน้าสถานะที่เลือก",
"wayToGetKookBotToken": "สร้างแอปพลิเคชันและรับโทเค็นบอตของคุณที่ {0}",
"wayToGetKookGuildID": "เปิดโหมด Developer ในการตั้งค่าของ Kook แล้วคลิกขวาที่กิลด์เพื่อรับ ID ของมัน",
"Strategy": "กลยุทธ์",
"Economy": "เศรษฐกิจ",
"You can divide numbers with": "คุณสามารถหารตัวเลขได้ด้วย",
"Notify Channel": "ช่องทางการแจ้งเตือน",
"setup a new monitor group": "ตั้งค่ากลุ่มการมอนิเตอร์หม่",
"smseagleGroup": "ชื่อกลุ่มสมุดโทรศัพท์",
"smseagleEncoding": "ส่งเป็น Unicode",
"smseaglePriority": "ลำดับความสำคัญของข้อความ (0-9, ค่าเริ่มต้น = 0)",
"smspartnerPhoneNumberHelptext": "หมายเลขต้องอยู่ในรูปแบบสากล {0}, {1} และหากมีหลายหมายเลขต้องคั่นด้วย {2}",
"smspartnerSenderNameInfo": "ต้องอยู่ระหว่าง 3 ถึง 11 ตัวอักษรปกติ",
"Custom Monitor Type": "ประเภทการมอนิเตอร์ แบบกำหนดเอง",
"Add Another": "เพิ่มอีกหนึ่ง",
"Expires": "หมดอายุ",
"disableAPIKeyMsg": "คุณแน่ใจหรือไม่ว่าจะปิดการใช้งาน API คีย์นี้?",
"ntfyAuthenticationMethod": "วิธีการยืนยันตัวตน",
"ntfyPriorityHelptextAllEvents": "ทุกกิจกรรมจะถูกส่งด้วยลำดับความสำคัญสูงสุด",
"ntfyPriorityHelptextAllExceptDown": "เหตุการณ์ทั้งหมดจะถูกส่งด้วยลำดับความสำคัญนี้ ยกเว้นเหตุการณ์ {0} ซึ่งมีลำดับความสำคัญ {1}",
"Show Clickable Link Description": "หากทำเครื่องหมายไว้ ทุกคนที่มีสิทธิ์เข้าถึงหน้าสถานะนี้จะสามารถเข้าถึง URL ของมอนิเตอร์ได้",
"monitorToastMessagesDescription": "การแจ้งเตือนแบบ Toast สำหรับการตรวจสอบจะหายไปหลังจากเวลาที่กำหนด (เป็นวินาที) หากตั้งค่าเป็น -1 ระบบจะไม่จำกัดเวลาแสดงผล หากตั้งค่าเป็น 0 จะปิดการแสดงการแจ้งเตือนแบบ Toast ทั้งหมด",
"wayToGetFlashDutyKey": "คุณสามารถไปที่ Channel -> (เลือก Channel) -> Integrations -> Add a new integration' page, add 'Uptime Kuma' to get push address, copy the Integration Key in the address. สำหรับข้อมูลเพิ่มเติม โปรดไปที่",
"cacheBusterParamDescription": "พารามิเตอร์ที่สร้างขึ้นแบบสุ่มเพื่อหลีกเลี่ยงการใช้แคช",
"gamedigGuessPortDescription": "พอร์ตที่ใช้โดย Valve Server Query Protocol อาจแตกต่างจากพอร์ตไคลเอนต์ ลองใช้วิธีนี้หากมอนิเตอร์ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ของคุณได้",
"bitrix24SupportUserID": "กรอกรหัสผู้ใช้ของคุณลงใน Bitrix24 คุณสามารถค้นหารหัสได้จากลิงก์โดยไปที่โปรไฟล์ของผู้ใช้",
"successBackupRestored": "กู้คืนข้อมูลสำรองสำเร็จแล้ว",
"Remote Browser not found!": "ไม่พบ Remote Browse!",
"remoteBrowsersDescription": "Remote Browsers เป็นทางเลือกหนึ่งแทนการเรียกใช้ Chromium บนเครื่องของคุณโดยตรง โดยสามารถตั้งค่าใช้งานร่วมกับบริการ เช่น browserless.io หรือเชื่อมต่อกับ",
"deleteRemoteBrowserMessage": "คุณแน่ใจหรือไม่ว่าต้องการลบ Remote Browser นี้สำหรับมอนิเตอร์ทั้งหมด?",
"mongodbCommandDescription": "รันคำสั่ง MongoDB กับฐานข้อมูล สำหรับข้อมูลเกี่ยวกับคำสั่งที่มีอยู่ โปรดดูที่ {documentation}",
"goAlertInfo": "GoAlert is a An open source application for on-call scheduling, automated escalations and notifications (like SMS or voice calls). Automatically engage the right person, the right way, and at the right time! {0}",
"aboutNotifyChannel": "การแจ้งเตือนไปยังช่องทางจะทำให้เกิดการแจ้งเตือนบนเดสก์ท็อปหรือมือถือของสมาชิกทุกคนในช่องนั้น ไม่ว่าสถานะของพวกเขาจะเป็น “ใช้งาน” หรือ “ไม่อยู่” ก็ตาม",
"DockerHostRequired": "กรุณาตั้งค่า Docker Host สำหรับมอนิเตอร์นี้",
"Select message type": "เลือกประเภทข้อความ",
"dataRetentionTimeError": "ระยะเวลาเก็บข้อมูลต้องเป็น 0 หรือมากกว่า",
"promosmsAllowLongSMS": "อนุญาตให้ส่ง SMS ยาว",
"apiKey-active": "ใช้งานอยู่",
"and": "และ",
"chromeExecutable": "ไฟล์ที่ใช้รัน Chrome/Chromium",
"Maintenance Time Window of a Day": "ช่วงเวลาการซ่อมบำรุงของวัน",
"Effective Date Range": "ช่วงวันที่มีผล (ไม่บังคับ)",
"leave blank for default subject": "เว้นว่างไว้สำหรับหัวข้อเริ่มต้น",
"emailCustomBody": "เนื้อหากำหนดเอง",
"leave blank for default body": "เว้นว่างไว้สำหรับเนื้อหาพื้นฐาน",
"emailTemplateMonitorJSON": "อ็อบเจ็กต์ที่อธิบายเกี่ยวกับมอนิเตอร์",
"Send to channel": "ส่งไปยังช่องทาง",
"Create new forum post": "สร้างโพสต์ฟอรัมใหม่",
"forumPostName": "ชื่อโพสต์ในฟอรัม",
"threadForumPostID": "รหัสเธรด / โพสต์ในฟอรัม",
"e.g. {discordThreadID}": "เช่น {discordThreadID}",
"Channel access token (Long-lived)": "Channel access token (Long-lived)",
"Your User ID": "ไอดีผู้ใช้ของคุณ",
"deleteMaintenanceMsg": "คุณแน่ใจหรือว่าต้องการลบการซ่อมบำรุงนี้?",
"atLeastOneMonitor": "เลือกมอนิเตอร์ที่ได้รับผลกระทบอย่างน้อยหนึ่งมอนิเตอร์",
"invertKeywordDescription": "ค้นหาคำสำคัญที่ไม่มีอยู่ แทนที่จะมีอยู่",
"Guild ID": "กิลด์ ID",
"Proto Service Name": "ชื่อบริการ Proto",
"Proto Method": "Proto เมทอด",
"Proto Content": "เนื้อหา Proto",
"Lowcost": "ต้นทุนต่ำ",
"high": "สูง",
"SMSManager API Docs": "เอกสาร API ของ SMSManager ",
"Gateway Type": "ประเภทเกตเวย์",
"Base URL": "URL หลัก",
"pushoverMessageTtl": "ข้อความ TTL (วินาที)",
"Free Mobile User Identifier": "Free Mobile User Identifier",
"Free Mobile API Key": "Free Mobile API Key",
"SendKey": "SendKey",
"goAlertIntegrationKeyInfo": "Get generic API integration key for the service in this format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" usually the value of token parameter of copied URL.",
"Mentioning": "การกล่าวถึง",
"Don't mention people": "อย่ากล่าวถึงบุคคล",
"Mention group": "กล่าวถึง {group}",
"Bark API Version": "เวอร์ชัน Bark API",
"openModalTo": "เปิดโมดัลไปยัง {0}",
"Add a domain": "เพิ่มโดเมน",
"Remove domain": "ลบโดเมน {0}",
"smseagleTo": "หมายเลขโทรศัพท์",
"smseagleRecipientType": "ประเภทผู้รับ",
"smseagleRecipient": "ผู้รับ (หากมีหลายคนให้แยกด้วยเครื่องหมายจุลภาค)",
"smseagleUrl": "URL ของอุปกรณ์ SMSEagle ของคุณ",
"Server URL should not contain the nfty topic": "URL ของเซิร์ฟเวอร์ไม่ควรมีหัวข้อ “nfty”",
"smseagleToken": "API Access token",
"pushDeerServerDescription": "เว้นว่างไว้เพื่อใช้เซิร์ฟเวอร์อย่างเป็นทางการ",
"Edit Tag": "แก้ไขแท็ก",
"Server Address": "ที่อยู่เซิร์ฟเวอร์",
"Expiry": "หมดอายุ",
"Expiry date": "วันที่หมดอายุ",
"Don't expire": "ไม่หมดอายุ",
"Continue": "ดำเนินการต่อ",
"Key Added": "เพิ่มคีย์แล้ว",
"No API Keys": "ไม่มี API คีย์",
"apiKey-expired": "หมดอายุแล้ว",
"apiKey-inactive": "ไม่ได้ใช้งาน",
"deleteAPIKeyMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบ API คีย์นี้?",
"Generate": "สร้าง",
"pagertreeUrgency": "ด่วน",
"pagertreeLow": "ต่ำ",
"pagertreeMedium": "ปานกลาง",
"pagertreeHigh": "สูง",
"lunaseaTarget": "เป้าหมาย",
"lunaseaDeviceID": "ไอดี ของอุปกรณ์",
"lunaseaUserID": "ไอดี ของผู้ใช้",
"ntfyUsernameAndPassword": "ชื่อผู้ใช้ และ รหัสผ่าน",
"twilioAccountSID": "บัญชี SID",
"twilioApiKey": "API คีย์ (ไม่บังคับ)",
"twilioFromNumber": "จากหมายเลข",
"twilioToNumber": "ถึงหมายเลข",
"Monitor Setting": "การตั้งค่ามอนิเตอร์ของ {0}",
"PushDeer Server": "เซิร์ฟเวอร์ PushDeer",
"Google Analytics ID": "ไอดี Google Analytics",
"API Keys": "API คีย์",
"pagertreeIntegrationUrl": "URL สำหรับการเชื่อมต่อระบบ (Integration URL)",
"twilioAuthToken": "Auth Token / Api Key Secret",
"Group": "กลุ่ม",
"Monitor Group": "กลุ่มมอนิเตอร์",
"monitorToastMessagesLabel": "การแจ้งเตือนมอนิเตอร์แบบ Toast",
"Press Enter to add broker": "กด Enter เพื่อเพิ่มโบรกเกอร์",
"Mechanism": "กลไก",
"Pick a SASL Mechanism...": "เลือกกลไก SASL…",
"noGroupMonitorMsg": "ไม่พร้อมใช้งาน โปรดสร้างกลุ่มมอนิเตอร์ก่อน",
"Close": "ปิด",
"FlashDuty Severity": "ความรุนแรง",
"Show Clickable Link": "แสดงลิงก์ที่คลิกได้",
"Open Badge Generator": "เปิดเครื่องมือสร้าง Badge",
"Badge Generator": "เครื่องมือสร้าง Badge ของ {0}",
"Badge Type": "ประเภท Badge",
"Badge Duration (in hours)": "ระยะเวลา Badge (เป็นชั่วโมง)",
"Badge Label": "ป้ายกำกับ Badge",
"nostrRelays": "รีเลย์ Nostr",
"nostrRelaysHelp": "URL รีเลย์หนึ่งรายการต่อบรรทัด",
"cacheBusterParam": "เพิ่มพารามิเตอร์ {0}",
"gamedigGuessPort": "Gamedig: เดาพอร์ต",
"Message format": "รูปแบบข้อความ",
"wayToGetBitrix24Webhook": "คุณสามารถสร้างเว็บฮุกได้โดยทำตามขั้นตอนที่ {0}",
"authIncorrectCreds": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง",
"2faAlreadyEnabled": "2FA ได้รับการเปิดใช้งานแล้ว",
"2faEnabled": "เปิดใช้งาน 2FA แล้ว",
"2faDisabled": "2FA ถูกปิดการใช้งาน",
"successAdded": "เพิ่มเรียบร้อยแล้ว",
"successResumed": "ดำเนินการต่อสำเร็จแล้ว",
"successPaused": "หยุดชั่วคราวสำเร็จแล้ว",
"successEnabled": "เปิดใช้งานสำเร็จแล้ว",
"tagNotFound": "ไม่พบแท็ก",
"foundChromiumVersion": "พบ Chromium/Chrome เวอร์ชัน: {0}",
"Add a Remote Browser": "เพิ่ม Remote Browser",
"self-hosted container": "คอนเทนเนอร์ที่โฮสต์เอง (self-hosted container)",
"remoteBrowserToggle": "โดยปกติแล้ว Chromium จะทำงานภายในคอนเทนเนอร์ของ Uptime Kuma คุณสามารถใช้ remote browser ได้โดยเปิดสวิตช์นี้",
"useRemoteBrowser": "ใช้ Remote Browser",
"aboutSlackUsername": "เปลี่ยนชื่อที่แสดงของผู้ส่งข้อความ หากคุณต้องการกล่าวถึงใคร ให้รวมไว้ในชื่อที่เป็นมิตรแทน",
"grpcMethodDescription": "ชื่อเมธอดจะถูกแปลงเป็นรูปแบบ camelCase เช่น sayHello, check, เป็นต้น",
"Enable TLS": "เปิดใช้งาน TLS",
"pagertreeSilent": "เงียบ",
"enableGRPCTls": "อนุญาตให้ส่งคำขอแบบ gRPC ด้วยการเชื่อมต่อ TLS",
"Sender name": "ชื่อผู้ส่ง",
"smsplanetNeedToApproveName": "ต้องได้รับการอนุมัติในแผงควบคุมของไคลเอนต์",
"Phone numbers": "หมายเลขโทรศัพท์",
"Badge Prefix": "คำนำหน้าค่าของ Badge",
"Badge Suffix": "คำตามหลังค่าของ Badge",
"Badge Label Color": "สีของป้ายข้อความ (Label) บน Badge",
"Badge Color": "สีของ Badge",
"Badge Preview": "ตัวอย่างการแสดง Badge (Preview)",
"Badge Label Suffix": "คำต่อท้ายข้อความ (Label) บน Badge",
"Badge Label Prefix": "คำนำหน้าข้อความ (Label) บน Badge",
"Badge Up Color": "สีของ Badge เมื่อสถานะเป็นปกติ (Up)",
"Badge Down Color": "สีของ Badge เมื่อสถานะเป็นออฟไลน์ (Down)",
"Badge Pending Color": "สีของ Badge ขณะรอการประมวลผล",
"Badge Maintenance Color": "สีของ Badge ในระหว่างการบำรุงรักษา",
"Badge Warn Color": "สีของ Badge เมื่อสถานะเตือน",
"Badge Warn Days": "จำนวนวันที่ Badge แสดงสถานะเตือน",
"Badge Down Days": "จำนวนวันที่ Badge แสดงสถานะออฟไลน์",
"Badge Style": "สไตล์ของ Badge",
"Badge value (For Testing only.)": "ค่าของ Badge (สำหรับการทดสอบเท่านั้น)",
"Badge URL": "URL ของ Badge",
"rabbitmqNodesRequired": "โปรดตั้งค่าโหนดสำหรับมอนิเตอร์นี้",
"Font Twemoji by Twitter licensed under": "ฟอนต์ Twemoji โดย Twitter ที่มีลิขสิทธิ์ภายใต้",
"the smsplanet documentation": "เอกสารของ smsplanet",
"smsplanetApiToken": "โทเค็นสำหรับ API ของ SMSPlanet",
"smsplanetApiDocs": "ข้อมูลรายละเอียดเกี่ยวกับการขอ API tokens สามารถดูได้ใน {the_smsplanet_documentation}",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "การแจ้งเตือนที่มีความสำคัญตามเวลา จะถูกส่งทันที แม้ว่าจะอยู่ในโหมดไม่รบกวน (Do Not Disturb) ก็ตาม",
"Clear": "กระจ่าง",
"equals": "เท่ากับ",
"Go back to home page.": "กลับไปหน้าหลัก",
"conditionValuePlaceholder": "ค่า",
"not starts with": "ไม่ได้เริ่มต้นด้วย",
"Notification Channel": "ช่องทางการรับแจ้งเตือน",
"Alphanumerical string and hyphens only": "ใช้ได้เฉพาะตัวอักษรและตัวเลข (a-z, A-Z, 0-9) และขีดกลาง (-) เท่านั้น",
"Message Template": "ข้อความของเทมเพลต",
"Plain Text": "ข้อความธรรมดา",
"wayToWriteWahaChatId": "หมายเลขโทรศัพท์ที่มีรหัสประเทศ แต่ไม่มีเครื่องหมายบวกที่เริ่มต้น ({0}), หมายเลขติดต่อ ({1}) หรือ หมายเลขกลุ่ม ({2}) การแจ้งเตือนจะถูกส่งไปยัง Chat ID นี้จาก WAHA Session",
"not equals": "ไม่เท่ากับ",
"No tags found.": "ไม่พบแท็ก",
"Conditions": "เงื่อนไข",
"conditionAdd": "เพิ่มเงื่อนไข",
"conditionDelete": "ลบเงื่อนไข",
"conditionAddGroup": "เพิ่มกลุ่ม",
"conditionDeleteGroup": "ลบกลุ่ม",
"contains": "ประกอบด้วย",
"not contains": "ไม่มี",
"Template Format": "รูปแบบของเทมเพลต",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "ระบุรหัสผู้ส่งข้อความแบบข้อความหรือลำดับหมายเลขโทรศัพท์ในรูปแบบ E.164 หากคุณต้องการรับการตอบกลับ",
"The phone number of the recipient in E.164 format.": "หมายเลขโทรศัพท์ของผู้รับในรูปแบบ E.164",
"Can be found on:": "สามารถดูได้ที่: {0}",
"From": "จาก",
"Time Sensitive (iOS Only)": "การแจ้งเตือนที่มีความสำคัญตามเวลา (เฉพาะ iOS)",
"Custom sound to override default notification sound": "เสียงที่กำหนดเองเพื่อแทนที่เสียงการแจ้งเตือนเริ่มต้น",
"Arcade": "อาร์เคด",
"Correct": "ถูกต้อง",
"Fail": "ล้มเหลว",
"Harp": "พิณ",
"Reveal": "เปิดเผย",
"Bubble": "ฟอง",
"Flute": "ขลุ่ย",
"Scifi": "ไซไฟ",
"Sound": "เสียง",
"starts with": "เริ่มต้นด้วย",
"ends with": "ลงท้ายด้วย",
"not ends with": "ไม่ลงท้ายด้วย",
"greater than or equal to": "มากกว่าหรือเท่ากับ",
"record": "บันทึก",
"less than": "น้อยกว่า",
"greater than": "มากกว่า",
"less than or equal to": "น้อยกว่าหรือเท่ากับ",
"Pop": "พอป",
"Elevator": "ลิฟต์",
"Doorbell": "ออด",
"Money": "เงิน",
"Guitar": "กีตาร์",
"successKeywordExplanation": "คีย์เวิร์ด MQTT ที่จะถูกพิจารณาว่าสำเร็จ",
"defaultFriendlyName": "มอนิเตอร์ใหม่",
"tagAlreadyOnMonitor": "แท็กที่ระบุ (ชื่อและค่า) มีอยู่ในระบบแล้ว หรืออยู่ในระหว่างรอการเพิ่ม",
"Add Tags": "เพิ่มแท็ก"
}

View file

@ -401,8 +401,8 @@
"TemplateCode": "TemplateCode",
"SignName": "SignName",
"Sms template must contain parameters: ": "Sms şablonu parametreleri içermelidir: ",
"Bark Endpoint": "Bark Endpoint",
"Bark Group": "Bark Group",
"Bark Endpoint": "Bark Uç Noktası",
"Bark Group": "Bark Grubu",
"Bark Sound": "Havlama Sesi",
"WebHookUrl": "WebHookUrl",
"SecretKey": "SecretKey",
@ -448,7 +448,7 @@
"Don't know how to get the token? Please read the guide:": "Tokeni nasıl alacağınızı bilmiyor musunuz? Lütfen kılavuzu okuyun:",
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Halihazırda Cloudflare Tüneli üzerinden bağlanıyorsanız mevcut bağlantı kesilebilir. Durdurmak istediğinden emin misin? Onaylamak için mevcut şifrenizi yazın.",
"HTTP Headers": "HTTP Başlıkları",
"Trust Proxy": "Trust Proxy",
"Trust Proxy": "Vekil Sunucuya Güven",
"Other Software": "Diğer Yazılımlar",
"For example: nginx, Apache and Traefik.": "Örneğin: nginx, Apache ve Traefik.",
"Please read": "Lütfen oku",
@ -602,8 +602,8 @@
"smseagleRecipient": "Alıcı(lar) (birden çok olanlar virgülle ayrılmalıdır)",
"smseagleToken": "API Erişim Tokenı",
"smseagleUrl": "SMSEagle cihaz URL\"niz",
"smseagleEncoding": "Unicode olarak gönder",
"smseaglePriority": "Mesaj önceliği (0-9, varsayılan = 0)",
"smseagleEncoding": "Unicode olarak gönder (varsayılan=GSM-7)",
"smseaglePriority": "Mesaj önceliği (0-9, en yüksek öncelik = 9)",
"Optional": "İsteğe bağlı",
"squadcast": "Squadcast",
"SendKey": "SendKey",
@ -656,7 +656,7 @@
"Free Mobile API Key": "Ücretsiz Mobil API Anahtarı",
"Enable TLS": "TLS'yi Etkinleştir",
"Proto Service Name": "Proto Service İsmi",
"Proto Method": "Proto Method",
"Proto Method": "Proto Yöntemi",
"Proto Content": "Proto İçeriği",
"Economy": "Ekonomik",
"Lowcost": "Düşük maliyetli",
@ -828,7 +828,7 @@
"noOrBadCertificate": "Sertifika Yok/Geçersiz",
"Select": "Seç",
"PushDeer Server": "PushDeer Sunucusu",
"wayToGetFlashDutyKey": "Kanal -> (Bir Kanal Seçin) -> Entegrasyonlar -> Yeni bir entegrasyon ekle' sayfasına gidebilir, bir push adresi almak için 'Çalışma Süresi Kuma' ekleyebilir, Entegrasyon Anahtarını adrese kopyalayabilirsiniz. Daha fazla bilgi için lütfen ziyaret edin",
"wayToGetFlashDutyKey": "Uptime Kuma'yı Flashduty ile entegre etmek için: Kanallar > Bir kanal seçin > Entegrasyonlar > Yeni bir entegrasyon ekleyin bölümüne gidin, Uptime Kuma'yı seçin ve Push URL'sini kopyalayın.",
"selectedMonitorCount": "Seçildi: {0}",
"Check/Uncheck": "İşaretle/İşareti Kaldır",
"pushDeerServerDescription": "Resmi sunucuyu kullanmak için boş bırakın",
@ -1117,5 +1117,63 @@
"Template Format": "Şablon Biçimi",
"YZJ Webhook URL": "YZJ Webhook URL'si",
"YZJ Robot Token": "YZJ Robot tokeni",
"telegramServerUrl": "(İsteğe bağlı) Sunucu URL'si"
"telegramServerUrl": "(İsteğe bağlı) Sunucu URL'si",
"defaultFriendlyName": "Yeni Monitör",
"smtpHelpText": "'SMTPS' SMTP/TLS'nin çalıştığını test eder; 'TLS'i reddet' düz metin üzerinden bağlanır; 'STARTTLS' bağlanır, bir STARTTLS komutu verir ve sunucu sertifikasını doğrular. Bunların hiçbiri e-posta göndermez.",
"OneChatAccessToken": "OneChat Erişim Tokeni",
"OneChatUserIdOrGroupId": "OneChat Kullanıcı ID veya Grup ID",
"Font Twemoji by Twitter licensed under": "Twemoji yazı tipi Twitter tarafından lisanslı",
"smsplanetApiToken": "SMSPlanet API için Token",
"smsplanetApiDocs": "API belirteçlerinin elde edilmesiyle ilgili ayrıntılı bilgi {the_smsplanet_documentation} bölümünde bulunabilir.",
"Use HTML for custom E-mail body": "Özel E-posta gövdesi için HTML kullanın",
"smseagleGroupV2": "Telefon defteri grup kimliği(ID)",
"smseagleContactV2": "Telefon defteri kişi kimlikleri(ID)",
"smseagleMsgType": "Mesaj tipi",
"smseagleMsgSms": "Sms mesajı (varsayılan)",
"smseagleMsgRing": "Telefon araması",
"smseagleMsgTts": "Metinden sese çağrı",
"smseagleMsgTtsAdvanced": "Metinden sese Gelişmiş çağrı",
"smseagleDuration": "Süre (saniye cinsinden)",
"smseagleTtsModel": "Metinden sese model kimliği(ID)",
"smseagleApiType": "API versiyonu",
"smseagleApiv1": "APIv1 (mevcut projeler ve geriye dönük uyumluluk için)",
"smseagleApiv2": "APIv2 (yeni entegrasyonlar için önerilir)",
"smseagleDocs": "Belgeleri veya APIv2 kullanılabilirliğini kontrol edin: {0}",
"smseagleComma": "Birden fazla virgül ile ayrılmalıdır",
"SpugPush Template Code": "Şablon Kodu",
"FlashDuty Push URL": "Bildirim URL",
"FlashDuty Push URL Placeholder": "Uyarı entegrasyonu sayfasından kopyalama",
"pingCountLabel": "Maksimum Paketler",
"pingCountDescription": "Durmadan önce gönderilecek paket sayısı",
"pingNumericLabel": "Sayısal Çıktı",
"pingNumericDescription": "İşaretlenirse, sembolik ana bilgisayar adları yerine IP adresleri çıktılanacaktır",
"pingGlobalTimeoutLabel": "Küresel Zaman Aşımı",
"pingGlobalTimeoutDescription": "Gönderilen paketlerden bağımsız olarak ping durmadan önce saniye cinsinden toplam süre",
"pingPerRequestTimeoutLabel": "Ping Başına Zaman Aşımı",
"pingPerRequestTimeoutDescription": "Bu, tek bir ping paketinin kayıp olarak değerlendirilmesinden önceki maksimum bekleme süresidir (saniye cinsinden)",
"pingIntervalAdjustedInfo": "Paket sayısına, genel zaman aşımına ve ping başına zaman aşımına göre ayarlanan aralık",
"Custom URL": "Özel URL",
"customUrlDescription": "Tıklanabilir URL olarak monitörünki yerine kullanılacaktır.",
"OneChatBotId": "OneChat Bot ID",
"the smsplanet documentation": "smsplanet dokümantasyonu",
"Phone numbers": "Telefon numaraları",
"Sender name": "Gönderen adı",
"smsplanetNeedToApproveName": "Müşteri panelinde onaylanması gerekir",
"Disable URL in Notification": "Bildirimdeki URL'yi Devre Dışı Bırak",
"Ip Family": "IP Ailesi",
"ipFamilyDescriptionAutoSelect": "IP ailesini belirlemek için {happyEyeballs} kullanır.",
"Happy Eyeballs algorithm": "Happy Eyeball algoritması",
"Add Another Tag": "Başka Etiket Ekle",
"Staged Tags for Batch Add": "Toplu Ekleme için Aşamalı Etiketler",
"pause": "Durakla",
"Manual": "Manuel",
"ntfyPriorityHelptextPriorityHigherThanDown": "Normal öncelik, {0} önceliğinden yüksek olmalıdır. {1} önceliği, {0} önceliği olan {2}den daha yüksektir",
"ntfyPriorityDown": "DOWN-olayları için öncelik seviyesi",
"Add Tags": "Etiket Ekle",
"tagAlreadyOnMonitor": "Bu etiket (isim ve değer) zaten monitörde veya eklenmeyi bekliyor.",
"tagAlreadyStaged": "Bu etiket (isim ve değer) bu grup için zaten hazırlandı.",
"tagNameExists": "Bu isimde bir sistem etiketi zaten var. Listeden seçin veya farklı bir isim kullanın.",
"Clear Form": "Formu Temizle",
"Optional: The audience to request the JWT for": "İsteğe bağlı: JWT'nin talep edileceği kitle",
"OAuth Audience": "OAuth Kitlesi"
}

View file

@ -261,7 +261,7 @@
"Message Title": "Заголовок повідомлення",
"Notification Sound": "Звук сповіщення",
"More info on:": "Більше інформації: {0}",
"pushoverDesc1": "Екстренний пріоритет (2) має таймуут повтору за замовчуванням 30 секунд і закінчується через 1 годину.",
"pushoverDesc1": "Екстренний пріоритет (2) має таймаут повтору за замовчуванням 30 секунд і закінчується через 1 годину.",
"pushoverDesc2": "Якщо ви бажаєте надсилати повідомлення різним пристроям, необхідно заповнити поле Пристрій.",
"SMS Type": "Тип SMS",
"octopushTypePremium": "Преміум (Швидкий - рекомендується для алертів)",
@ -529,7 +529,7 @@
"Schedule maintenance": "Графік обслуговування",
"Affected Monitors": "Задіяні монітори",
"HomeAssistant": "Home Assistant",
"smseaglePriority": "Пріоритет повідомлення (0-9, за замовчуванням = 0)",
"smseaglePriority": "Пріоритет повідомлення (0-9, найвищий пріоритет = 9)",
"smseagleRecipient": "Отримувач(і) (декілька отримувачів повинні бути відокремлені комами)",
"markdownSupported": "Підтримується синтаксис розмітки",
"Resend Notification if Down X times consequently": "Повторно надсилати сповіщення, якщо падіння відбулося X разів підряд",
@ -542,12 +542,12 @@
"Specific Monitor Type": "Специфічний моніторинг",
"Monitor": "Монітор | Монітори",
"smseagle": "SMSEagle",
"smseagleEncoding": "Надсилати в Unicode",
"smseagleEncoding": "Надсилати в Unicode (за замовчуванням=GSM-7)",
"smseagleUrl": "URL-адреса пристрою SMSEagle",
"smseagleToken": "Токен доступу API",
"smseagleRecipientType": "Тип одержувача",
"smseagleContact": "Телефонний контакт(и)",
"smseagleGroup": "Телефонна група(и)",
"smseagleGroup": "Назва(и) телефонної групи",
"smseagleTo": "Телефонний номер(и)",
"Help": "Допомога",
"Game": "Гра",
@ -833,7 +833,7 @@
"noOrBadCertificate": "Відсутність/поганий сертифікат",
"Select": "Вибрати",
"selectedMonitorCount": "Вибрано: {0}",
"wayToGetFlashDutyKey": "Ви можете перейти на сторінку \"Канал -> (Виберіть канал) -> Інтеграції -> Додати нову інтеграцію\", додайте \"Uptime Kuma\", щоб отримати пуш-адресу, скопіюйте ключ інтеграції в адресу. Для отримання додаткової інформації, будь ласка, відвідайте",
"wayToGetFlashDutyKey": "Щоб інтегрувати Uptime Kuma з Flashduty: Перейдіть до Канали > Виберіть канал > Інтеграції > Додати нову інтеграцію, виберіть Uptime Kuma і скопіюйте URL-адресу пуша.",
"nostrRecipientsHelp": "Формат npub, по одному в рядку",
"Check/Uncheck": "Встановити/зняти галочку",
"PushDeer Server": "Сервер PushDeer",
@ -1124,5 +1124,62 @@
"wayToWriteWahaChatId": "Номер телефону з міжнародним префіксом, але без знака плюс на початку ({0}), ID контакту ({1}) або ID групи ({2}). На цей ID чату надсилаються сповіщення з сеансу WAHA.",
"telegramServerUrl": "(Необов'язково) URL сервера",
"telegramServerUrlDescription": "Щоб зняти обмеження з Telegram bot api або отримати доступ у заблокованих регіонах (Китай, Іран тощо). Для отримання додаткової інформації натисніть {0}. За замовчуванням: {1}",
"Font Twemoji by Twitter licensed under": "Шрифт Twemoji від Twitter ліцензований під"
"Font Twemoji by Twitter licensed under": "Шрифт Twemoji від Twitter ліцензований під",
"the smsplanet documentation": "документації smsplanet",
"Phone numbers": "Номери телефонів",
"Sender name": "Ім'я відправника",
"smsplanetNeedToApproveName": "Потребує схвалення в клієнтській панелі",
"smsplanetApiToken": "Токен для API SMSPlanet",
"smsplanetApiDocs": "Детальну інформацію про отримання токенів API можна знайти в {the_smsplanet_documentation}.",
"defaultFriendlyName": "Новий монітор",
"Add Tags": "Додати теги",
"tagNameExists": "Системний тег з такою назвою вже існує. Виберіть його зі списку або використовуйте іншу назву.",
"tagAlreadyOnMonitor": "Цей тег (назва та значення) вже є в моніторі або очікує на додавання.",
"smseagleMsgType": "Тип повідомлення",
"smseagleMsgTtsAdvanced": "Розширений текстовий дзвінок",
"smseagleApiv1": "APIv1 (для існуючих проєктів та зворотної сумісності)",
"smseagleComma": "Декілька значень повинні бути відокремлені комою",
"FlashDuty Push URL": "URL пуша",
"pingCountDescription": "Кількість пакетів для відправлення перед зупинкою",
"pingNumericDescription": "Якщо позначено, замість символічних імен хостів будуть виводитися IP-адреси",
"pingPerRequestTimeoutDescription": "Це максимальний час очікування (у секундах) перед тим, як вважати один пінг-пакет втраченим",
"smtpHelpText": "'SMTPS' перевіряє, чи працює SMTP/TLS; 'Ігнорувати TLS' з'єднується через відкритий текст; “STARTTLS” з'єднується, видає команду STARTTLS і перевіряє сертифікат сервера. Жоден з них не надсилає електронного листа.",
"Staged Tags for Batch Add": "Покрокові теги для групового додавання",
"ntfyPriorityHelptextPriorityHigherThanDown": "Звичайний пріоритет має бути вищим за пріоритет {0}. Пріоритет {1} вищий за {0} пріоритет {2}",
"ntfyPriorityDown": "Пріоритет для подій НЕДОСТУПНИЙ",
"pingCountLabel": "Максимум пакетів",
"SpugPush Template Code": "Код шаблону",
"tagAlreadyStaged": "Цей тег (назва та значення) вже створено для цієї групи.",
"Use HTML for custom E-mail body": "Використовувати HTML для користувацького тіла електронного листа",
"smseagleGroupV2": "ID телефонної групи",
"smseagleContactV2": "ID телефонного контакту",
"smseagleMsgSms": "Sms повідомлення (за замовчуванням)",
"smseagleMsgRing": "Дзвінок",
"smseagleMsgTts": "Текстовий дзвінок",
"smseagleDuration": "Тривалість (в секундах)",
"smseagleTtsModel": "ID моделі перетворення тексту в мовлення",
"smseagleApiType": "Версія API",
"smseagleApiv2": "APIv2 (рекомендовано для нових інтеграцій)",
"smseagleDocs": "Перевірте наявність документації або APIv2: {0}",
"FlashDuty Push URL Placeholder": "Скопіюйте зі сторінки інтеграції сповіщень",
"pingNumericLabel": "Числовий вивід",
"pingGlobalTimeoutLabel": "Глобальний таймаут",
"pingGlobalTimeoutDescription": "Загальний час у секундах до зупинки пінгу, незалежно від надісланих пакетів",
"pingPerRequestTimeoutLabel": "Таймаут кожного пінгу",
"pingIntervalAdjustedInfo": "Інтервал налаштовується на основі кількості пакетів, глобального таймауту та таймауту кожного пінгу",
"Custom URL": "Користувацька URL-адреса",
"customUrlDescription": "Буде використано як URL-адресу для кліку замість URL-адреси монітора.",
"OneChatAccessToken": "Токен доступу OneChat",
"OneChatUserIdOrGroupId": "ID користувача або ID групи OneChat",
"OneChatBotId": "ID бота OneChat",
"Disable URL in Notification": "Вимкнути URL-адресу в сповіщенні",
"Add Another Tag": "Додати ще один тег",
"Clear Form": "Очистити форму",
"pause": "Пауза",
"Happy Eyeballs algorithm": "Алгоритм Happy Eyeballs",
"Manual": "Інструкція",
"Ip Family": "Сімейство IP",
"ipFamilyDescriptionAutoSelect": "Використовує {happyEyeballs} для визначення сімейства IP.",
"OAuth Audience": "Аудиторія OAuth",
"Optional: The audience to request the JWT for": "Необов'язково: Аудиторія, для якої необхідно подати запит на JWT"
}

View file

@ -398,8 +398,8 @@
"smseagleRecipient": "收信人(多个需用半角逗号分隔)",
"smseagleToken": "API 访问令牌",
"smseagleUrl": "您的 SMSEagle 设备 URL",
"smseagleEncoding": "以 Unicode 发送",
"smseaglePriority": "消息优先级0-9默认为 0",
"smseagleEncoding": "以 Unicode 发送 (默认为GSM-7)",
"smseaglePriority": "消息优先级0-99 为最高优先级",
"stackfield": "Stackfield",
"Customize": "自定义",
"Custom Footer": "自定义底部",
@ -414,7 +414,7 @@
"smtpDkimheaderFieldNames": "包含在哈希计算对象内的 Header 列表(可选)",
"smtpDkimskipFields": "不包含在哈希计算对象内的 Header 列表(可选)",
"wayToGetPagerDutyKey": "您可以在 Service -> Service Directory -> (选择一个 Service) -> Integrations -> Add integration 页面中搜索“Events API V2”以获取此 Integration Key更多信息请看{0}",
"wayToGetFlashDutyKey": "您可以进入 协作空间 -> (选择一个 协作空间) -> 集成数据 -> 新增一个集成 页面,添加“Uptime Kuma”集成获得一个推送地址复制地址中的 Integration Key更多信息前往{0}",
"wayToGetFlashDutyKey": "将Uptime Kuma与Flashduty集成进入 协作空间 -> (选择一个 协作空间) -> 集成数据 -> 新增一个集成 页面,选择“Uptime Kuma”复制推送URL。",
"Integration Key": "集成密钥",
"Integration URL": "集成网址",
"Auto resolve or acknowledged": "自动标记为已解决或已读",
@ -1120,5 +1120,62 @@
"YZJ Robot Token": "YZJ 机器人令牌",
"telegramServerUrl": "(可选) 服务器 Url",
"telegramServerUrlDescription": "用以解除 Telegram 的机器人 API 限制或在封锁区域(中国、伊朗等)获得访问权限。获取更多信息,请点击 {0}。默认值:{1}",
"Font Twemoji by Twitter licensed under": "由 Twitter 制作的 Twemoji 字体根据此许可证授权"
"Font Twemoji by Twitter licensed under": "由 Twitter 制作的 Twemoji 字体根据此许可证授权",
"smsplanetApiDocs": "有关获取 API token 的详细信息,请参阅 {the_smsplanet_documentation}。",
"smsplanetNeedToApproveName": "需要在客户端面板进行确认",
"Sender name": "发件人名称",
"Phone numbers": "手机号码",
"the smsplanet documentation": "smsplanet 文档",
"smsplanetApiToken": "SMSPlanet API 的 Token",
"Use HTML for custom E-mail body": "使用 HTML 作为自定义电子邮件内容",
"smseagleDuration": "时长 (以秒为单位)",
"smseagleApiv1": "APIv1 (用于现有项目和向后兼容性)",
"ntfyPriorityHelptextPriorityHigherThanDown": "常规优先级应高于 {0} 优先级。优先级 {1} 高于 {0} 优先级 {2}",
"FlashDuty Push URL Placeholder": "从警告集成页面复制",
"pingNumericDescription": "如果勾选将输出IP地址而不是符号主机名",
"pingPerRequestTimeoutDescription": "在视作单次ping数据包丢失前的最大等待时间 (以秒为单位)",
"ntfyPriorityDown": "故障事件的优先级",
"FlashDuty Push URL": "推送URL",
"pingCountLabel": "数据包最大数量",
"pingCountDescription": "停止前发送的数据包数量",
"pingNumericLabel": "数字输出",
"pingGlobalTimeoutLabel": "全局超时",
"pingGlobalTimeoutDescription": "无论多少发送了多少数据包总时间到达此值后停止ping (以秒为单位)",
"pingPerRequestTimeoutLabel": "单次ping超时时间",
"pingIntervalAdjustedInfo": "间隔时间将根据数据包数量、全局超时时间和单次ping超时时间调整",
"Custom URL": "自定义链接",
"defaultFriendlyName": "新监控项",
"smseagleMsgType": "消息类型",
"smseagleTtsModel": "文本转语音模型ID",
"smseagleApiType": "API 版本",
"smseagleApiv2": "APIv2 (推荐用于新的集成)",
"smseagleDocs": "阅读文档以检查APIv2的可用性: {0}",
"smseagleComma": "使用逗号分隔多项",
"Clear Form": "清除表单",
"pause": "暂停",
"Ip Family": "IP 协议",
"Add Another Tag": "新增标签",
"Staged Tags for Batch Add": "暂存标签以便后续批量添加",
"Happy Eyeballs algorithm": "Happy Eyeballs 算法",
"ipFamilyDescriptionAutoSelect": "使用 {happyEyeballs} 判断 IP 协议。",
"Manual": "手动",
"OAuth Audience": "OAuth 接收方",
"OneChatAccessToken": "OneChat Access Token密钥",
"Optional: The audience to request the JWT for": "可选项JWT 请求的接收方aud",
"tagNameExists": "该标签名已与系统标签重复。请直接从列表选择系统标签,或使用另一个标签名。",
"tagAlreadyOnMonitor": "该标签(键值对)已被用在监控项,或是等待添加。",
"OneChatUserIdOrGroupId": "OneChat 用户 ID 或群组 ID",
"customUrlDescription": "将替换监控项监控的链接,用于状态页面处,显示为可点击的链接。",
"smtpHelpText": "SMTPS测试 SMTP/TLS 是否正常工作Ignore TLS通过明文连接STARTTLS通过明文连接然后发出 STARTTLS 命令并验证服务器证书。这些方式都不会导致实际发送电子邮件。",
"SpugPush Template Code": "模板代码",
"Disable URL in Notification": "在通知中禁止解析链接",
"OneChatBotId": "OneChat 机器人 ID",
"smseagleMsgTtsAdvanced": "文本转语音高级呼叫",
"smseagleMsgTts": "文本转语音呼叫",
"Add Tags": "添加标签",
"tagAlreadyStaged": "该标签(键值对)已经被暂存。",
"smseagleMsgRing": "响铃呼叫",
"smseagleMsgSms": "SMS 短信(默认值)",
"smseagleContactV2": "联系人 ID",
"smseagleGroupV2": "群组 ID"
}

View file

@ -8,7 +8,7 @@
"ignoreTLSError": "忽略 HTTPS 網站的 TLS/SSL 錯誤",
"upsideDownModeDescription": "反轉顯示狀態。若服務可以連線,將顯示離線。",
"maxRedirectDescription": "最大重新導向跟隨次數。設為 0 將停用重新導向。",
"enableGRPCTls": "允許以 TLS 連線傳送 gRPC 求",
"enableGRPCTls": "允許以 TLS 連線傳送 gRPC 求",
"grpcMethodDescription": "方法名稱將轉換至駝峰式命名,如 sayHello、check 等。",
"acceptedStatusCodesDescription": "選擇視為成功回應的狀態碼。",
"Maintenance": "維護",
@ -38,7 +38,7 @@
"clearEventsMsg": "您確定要刪除此監測器的所有事件嗎?",
"clearHeartbeatsMsg": "您確定要刪除此監測器的所有心跳嗎?",
"confirmClearStatisticsMsg": "您確定要刪除所有統計資料嗎?",
"importHandleDescription": "若您想跳過所有相同名稱的監測器或通知,請選擇 '略過現有'。選擇 '覆寫' 將刪除所有現有的監測器及通知。",
"importHandleDescription": "若您想跳過所有相同名稱的監測器或通知,請選擇「略過現有」。選擇「覆寫」將刪除所有現有的監測器及通知。",
"confirmImportMsg": "您確定要匯入備份嗎?請確認是否選擇正確的匯入設定。",
"twoFAVerifyLabel": "請輸入權杖以驗證雙步驟驗證:",
"tokenValidSettingsMsg": "權杖有效!您可以儲存雙步驟驗證設定了。",
@ -119,7 +119,7 @@
"Update Password": "更新密碼",
"Disable Auth": "停用驗證",
"Enable Auth": "啟用驗證",
"disableauth.message1": ">你是否要{disableAuth}",
"disableauth.message1": "你是否要{disableAuth}",
"disable authentication": "取消登入驗證",
"disableauth.message2": "此功能是設計給已有{intendThirdPartyAuth}的使用者,例如 Cloudflare Access。",
"where you intend to implement third-party authentication": "第三方認證",
@ -224,7 +224,7 @@
"webhookJsonDesc": "{0} 適合任何現代的 HTTP 伺服器,如 Express.js",
"webhookFormDataDesc": "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析",
"webhookAdditionalHeadersTitle": "額外標頭",
"webhookAdditionalHeadersDesc": "設定 webhook 請求的額外 Header。每一個 Header 應被定義為一對 JSON 鍵值對。",
"webhookAdditionalHeadersDesc": "設定 webhook 請求的額外標頭。每個標頭應由一對 JSON 鍵值對定義。",
"smtp": "Email (SMTP)",
"secureOptionNone": "無 / STARTTLS (25, 587)",
"secureOptionTLS": "TLS (465)",
@ -232,8 +232,8 @@
"From Email": "寄件人",
"emailCustomSubject": "自訂主旨",
"To Email": "收件者",
"smtpCC": "CC",
"smtpBCC": "BCC",
"smtpCC": "副本",
"smtpBCC": "密件副本",
"discord": "Discord",
"Discord Webhook URL": "Discord Webhook 網址",
"wayToGetDiscordURL": "您可以前往伺服器設定 (Server Settings) -> 整合 (Integrations) -> 檢視 Webhooks (View Webhooks) -> 新 Webhook (New Webhook) 以取得新的 Webhook",
@ -300,15 +300,15 @@
"appriseNotInstalled": "尚未安裝 Apprise。{0}",
"Access Token": "存取權杖",
"Channel access token": "頻道存取權杖",
"Line Developers Console": "Line 開發者控制",
"lineDevConsoleTo": "Line 開發者控制 - {0}",
"Line Developers Console": "Line 開發者控制",
"lineDevConsoleTo": "Line 開發者控制 - {0}",
"Basic Settings": "基本設定",
"User ID": "使用者 ID",
"Messaging API": "即時通訊 API",
"wayToGetLineChannelToken": "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。",
"Icon URL": "圖示網址",
"aboutIconURL": "您可以在 \"圖示網址\" 中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。",
"aboutMattermostChannelName": "您可以在 \"頻道名稱\" 欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道",
"aboutIconURL": "您可以在「圖示網址」中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。",
"aboutMattermostChannelName": "您可以在「頻道名稱」欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道",
"matrix": "Matrix",
"promosmsTypeEco": "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件者。",
"promosmsTypeFlash": "SMS FLASH - 訊息會自動在收件者的裝置上顯示。僅限位於波蘭的收件者。",
@ -325,8 +325,8 @@
"Body": "主體",
"Headers": "標頭",
"PushUrl": "Push 網址",
"HeadersInvalidFormat": "求標頭不是有效的 JSON: ",
"BodyInvalidFormat": "求主體不是有效的 JSON: ",
"HeadersInvalidFormat": "求標頭不是有效的 JSON: ",
"BodyInvalidFormat": "求主體不是有效的 JSON: ",
"Monitor History": "監測器歷史紀錄",
"clearDataOlderThan": "保留 {0} 天內的監測器歷史紀錄。",
"PasswordsDoNotMatch": "密碼不相符。",
@ -377,9 +377,9 @@
"Services": "服務",
"Discard": "捨棄",
"Cancel": "取消",
"Powered by": "技術支援",
"Powered by": "威力本源",
"serwersms": "SerwerSMS.pl",
"serwersmsAPIUser": "API 使用者名稱 (包括 webapi_ 前綴)",
"serwersmsAPIUser": "API 使用者名稱 (包含 webapi_ 前置碼)",
"serwersmsAPIPassword": "API 密碼",
"serwersmsPhoneNumber": "電話號碼",
"serwersmsSenderName": "SMS 寄件人名稱 (由客戶入口網站註冊)",
@ -401,7 +401,7 @@
"smtpDkimDesc": "請參考 Nodemailer DKIM {0} 使用方式。",
"documentation": "文件",
"smtpDkimDomain": "網域名稱",
"smtpDkimKeySelector": "DKIM 選取器",
"smtpDkimKeySelector": "DKIM 選擇字",
"smtpDkimPrivateKey": "私密金鑰",
"smtpDkimHashAlgo": "雜湊演算法 (選填)",
"smtpDkimheaderFieldNames": "要簽署的郵件標頭 (選填)",
@ -432,17 +432,17 @@
"Certificate Chain": "憑證鏈結",
"Valid": "有效",
"Invalid": "無效",
"AccessKeyId": "標識使用者 ID",
"AccessKeyId": "AccessKey ID",
"SecretAccessKey": "AccessKey 密碼",
"PhoneNumbers": "電話號碼",
"TemplateCode": "範例程式碼",
"SignName": "簽名",
"Sms template must contain parameters: ": "Sms 範本必須包含參數: ",
"Sms template must contain parameters: ": "SMS 範本必須包含參數: ",
"Bark Endpoint": "Bark 端點",
"Bark Group": "Bark 群組",
"Bark Sound": "Bark 鈴聲",
"WebHookUrl": "WebHookURL",
"SecretKey": "對稱金鑰",
"SecretKey": "SecretKey",
"For safety, must use secret key": "為了安全起見,必須使用秘密金鑰",
"Device Token": "裝置權杖",
"Platform": "平臺",
@ -507,7 +507,7 @@
"onebotSafetyTips": "為了安全起見,必須設定存取權杖",
"PushDeer Key": "PushDeer 金鑰",
"Footer Text": "頁尾文字",
"Show Powered By": "顯示技術支援文字",
"Show Powered By": "顯示威力本源文字",
"Domain Names": "網域名稱",
"signedInDisp": "以 {0} 身分登入",
"signedInDispDisabled": "驗證已停用。",
@ -525,9 +525,9 @@
"Leave blank to use a shared sender number.": "留空以使用共享寄件人號碼。",
"Octopush API Version": "Octopush API 版本",
"Legacy Octopush-DM": "舊版 Octopush-DM",
"endpoint": "端",
"octopushAPIKey": "在控制的 HTTP API 憑證取得的 \"API 金鑰\"",
"octopushLogin": "在控制的 HTTP API 憑證取得的 \"Login\"",
"endpoint": "端",
"octopushAPIKey": "在控制的 HTTP API 憑證取得的 \"API 金鑰\"",
"octopushLogin": "在控制的 HTTP API 憑證取得的 \"Login\"",
"promosmsLogin": "API 登入名稱",
"promosmsPassword": "API 密碼",
"pushoversounds pushover": "Pushover (預設)",
@ -574,9 +574,9 @@
"certificationExpiryDescription": "TLS 將於 X 天後到期時觸發 HTTPS 監測器通知:",
"Setup Docker Host": "設定 Docker 主機",
"Connection Type": "連線類型",
"Docker Daemon": "Docker 精靈",
"Docker Daemon": "Docker Daemon",
"deleteDockerHostMsg": "您確定要為所有監測器刪除此 Docker 主機嗎?",
"socket": "通訊端",
"socket": "Socket",
"tcp": "TCP / HTTP",
"Docker Container": "Docker 容器",
"Container Name / ID": "容器名稱 / ID",
@ -586,7 +586,7 @@
"Domain": "網域",
"Workstation": "工作站",
"disableCloudflaredNoAuthMsg": "您處於無驗證模式。無須輸入密碼。",
"trustProxyDescription": "信任 'X-Forwarded-*' 標頭。如果您想要取得正確的客戶端 IP且您的 Uptime Kuma 架設於 Nginx 或 Apache 後方,您應該啟用此選項。",
"trustProxyDescription": "信任“X-Forwarded-*”標頭。如果您想要取得正確的用戶端 IP而您的 Uptime Kuma 位於 Nginx 或 Apache 等代理程式後面,則應該啟用此功能。",
"wayToGetLineNotifyToken": "您可以從 {0} 取得存取權杖",
"Examples": "範例",
"Home Assistant URL": "Home Assistant 網址",
@ -603,10 +603,10 @@
"Frontend Version": "前端版本",
"Frontend Version do not match backend version!": "前端版本與後端版本不符!",
"Base URL": "基底網址",
"goAlertInfo": "GoAlert 是用於待命排程、升級自動化,以及通知 (如簡訊或語音通話) 的開源應用程式。自動在正確的時間、用洽當的方法、聯絡合適的人! {0}",
"goAlertInfo": "GoAlert 是用於待命排程、升級自動化,以及通知 (如簡訊或語音通話) 的開放原始碼應用程式。自動在正確的時間、用恰當的方法、聯絡合適的人! {0}",
"goAlertIntegrationKeyInfo": "取得服務的通用 API 整合金鑰,格式為 \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\"。通常是已複製的網址的權杖參數值。",
"goAlert": "GoAlert",
"backupOutdatedWarning": "即將棄用:由於專案新增了大量新功能,且備份功能未被妥善維護,故此功能無法產生或復原完整備份。",
"backupOutdatedWarning": "已棄用:由於添加了許多功能且此備份功能有點無人維護,因此它無法產生或還原完整的備份。",
"backupRecommend": "請直接備份磁碟區或 ./data/ 資料夾。",
"Optional": "選填",
"squadcast": "Squadcast",
@ -684,7 +684,7 @@
"telegramMessageThreadID": "(選填) Telegram 話題 ID",
"startDateTime": "開始日期/時間",
"endDateTime": "結束日期/時間",
"cronSchedule": "計劃: ",
"cronSchedule": "排程: ",
"invalidCronExpression": "無效的 Cron 表達式:{0}",
"telegramProtectContent": "阻止轉發/儲存",
"telegramProtectContentDescription": "如果啟用Telegram 中的機器人訊息將受到保護,不會被轉發和儲存。",
@ -692,14 +692,14 @@
"uninstall": "移除",
"loadingError": "無法取得數據, 請重試。",
"markdownSupported": "支援 Markdown 語法",
"Packet Size": "數據包大小",
"Packet Size": "包大小",
"statusPageRefreshIn": "將於 {0} 後重新整理",
"confirmUninstallPlugin": "是否要移除這個外掛程式?",
"confirmUninstallPlugin": "是否要移除這個外掛程式",
"Key Added": "已建立金鑰",
"Clone Monitor": "複製監控項目",
"Clone": "複製",
"cloneOf": "從 {0} 複製",
"uninstalling": "正在卸載",
"uninstalling": "正在移除",
"notificationRegional": "地區性的",
"wayToGetZohoCliqURL": "您可以前往此頁面以瞭解如何建立 webhook 網址 {0}。",
"wayToGetKookBotToken": "到 {0} 建立應用程式並取得 bot token",
@ -714,9 +714,9 @@
"Expiry": "過期",
"apiKey-inactive": "無效",
"apiKey-expired": "已過期",
"Reconnecting...": "重新連線...",
"Reconnecting...": "重新連線...",
"Expiry date": "過期時間",
"Don't expire": "過期",
"Don't expire": "不過期",
"Continue": "繼續",
"Add Another": "新增作者",
"Add API Key": "新增 API 金鑰",
@ -726,7 +726,7 @@
"lunaseaUserID": "使用者 ID",
"Cannot connect to the socket server": "無法連線到 Socket 伺服器",
"Edit Maintenance": "編輯維護",
"deleteAPIKeyMsg": "您確定要刪除這個 API 金鑰?",
"deleteAPIKeyMsg": "您確定要刪除這個 API 金鑰",
"Custom Monitor Type": "自訂監視器類型",
"Google Analytics ID": "Google Analytics ID",
"Server Address": "伺服器位置",
@ -738,10 +738,10 @@
"Learn More": "閱讀更多",
"pushoverMessageTtl": "Message TTL (秒)",
"apiKeyAddedMsg": "您的 API 金鑰已建立。金鑰不會再次顯示,請將它放在安全的地方。",
"No API Keys": "無 API 金鑰",
"apiKey-active": "活躍",
"No API Keys": "沒有 API Keys",
"apiKey-active": "啟用",
"Expires": "過期",
"disableAPIKeyMsg": "您確定要停用這個 API 金鑰?",
"disableAPIKeyMsg": "確定要停用這個 API 金鑰?",
"Monitor Setting": "{0} 的監視器設定",
"Guild ID": "公會 ID",
"chromeExecutableDescription": "如果您使用 Docker 且未安裝 Chromium可能要花數分鐘安裝後才能顯示測試結果。安裝會使用 1GB 的硬碟空間。",
@ -757,21 +757,21 @@
"filterActivePaused": "暫停",
"Select": "選擇",
"enableNSCD": "啟用 NSCD名稱服務快取以快取所有 DNS 請求",
"Server URL should not contain the nfty topic": "伺服器地址不應包含 ntfy主題",
"Server URL should not contain the nfty topic": "伺服器地址不應包含 ntfy 主題",
"Invert Keyword": "反轉模式",
"Request Timeout": "請求時",
"timeoutAfter": "{0} 秒後時",
"Request Timeout": "請求時",
"timeoutAfter": "{0} 秒後時",
"styleElapsedTime": "在監控項詳情的心跳欄下顯示起止時間",
"styleElapsedTimeShowNoLine": "顯示(不帶連接線)",
"styleElapsedTimeShowWithLine": "顯示(帶連接線)",
"webhookCustomBodyDesc": "為 webhook 設定一個自定義 HTTP 請求體。可在模板內使用{msg},、{heartbeat}和{monitor} 變量。",
"webhookBodyPresetOption": "預設 - {0}",
"webhookBodyCustomOption": "自定義內容",
"webhookBodyCustomOption": "自訂主體內容",
"selectedMonitorCount": "已選:{0}",
"Check/Uncheck": "選中/取消選中",
"tailscalePingWarning": "如需使用 Tailscale Ping 客戶端,您需要以非 docker 方式安裝 Uptime Kuma並同時安裝 Tailscale 客戶端。",
"Check/Uncheck": "選取/取消選取",
"tailscalePingWarning": "如需使用 Tailscale Ping 監測器,您必須在伺服器上以非 Docker 方式安裝 Uptime Kuma且同時安裝 Tailscale 用戶端。",
"invertKeywordDescription": "出現關鍵詞將令檢測結果設為失敗,而非成功。",
"wayToGetKookGuildID": "在 Kook 設定中打開“開發者模式”,然後右鍵點選頻道可取得其 ID",
"wayToGetKookGuildID": "在 Kook 設定中打開「開發者模式」,然後右鍵點選頻道可取得其 ID",
"Notify Channel": "通知該頻道",
"aboutNotifyChannel": "勾選“通知該頻道”,會令該頻道內所有成員都收到一條桌面端或移動端通知,無論其狀態是在線或離開。",
"pagertreeIntegrationUrl": "整合 URL 地址",
@ -862,9 +862,9 @@
"successDisabled": "已成功停用。",
"successEnabled": "已成功啟用。",
"tagNotFound": "找不到標籤。",
"foundChromiumVersion": "找到 Chromium/Chrome。版本{0}",
"foundChromiumVersion": "發現 Chromium/Chrome。版本{0}",
"setupDatabaseSQLite": "一個簡單的資料庫檔案,適用於小規模部署。在 v2.0.0 之前Uptime Kuma 預設使用 SQLite 作為資料庫。",
"Pick a SASL Mechanism...": "選擇一個 SASL 機制...…",
"Pick a SASL Mechanism...": "選擇一個 SASL 機制…",
"Authorization Identity": "授權身份",
"AccessKey Id": "存取金鑰 ID",
"Secret AccessKey": "秘密存取金鑰",
@ -897,7 +897,7 @@
"self-hosted container": "自架容器",
"useRemoteBrowser": "使用遠端瀏覽器",
"Add a domain": "新增網域",
"Remove domain": "移除網域 '{0}'",
"Remove domain": "移除網域「{0}」",
"settingUpDatabaseMSG": "設定資料庫中,可能需要一段時間,請耐心等待。",
"smspartnerApiurl": "您可以在儀表板上的 {0} 找到您的 API 金鑰",
"smspartnerPhoneNumberHelptext": "號碼必須是國際格式 {0}, {1}。多個號碼必須用 {2} 分隔",
@ -927,7 +927,7 @@
"threemaRecipientTypePhone": "電話號碼",
"threemaSenderIdentityFormat": "8 個字元,通常以 * 開頭",
"threemaRecipient": "收件者",
"mongodbCommandDescription": "對資料庫執行 MongoDB 指令。有關可用指令的資訊,請參閱 {documentation}",
"mongodbCommandDescription": "對資料庫執行 MongoDB 命令。有關可用命令的資訊,請參閱 {documentation}",
"threemaRecipientTypeEmail": "電子郵件地址",
"Originator type": "發送者類型",
"smspartnerPhoneNumber": "電話號碼",
@ -936,7 +936,7 @@
"max 15 digits": "最多 15 位數字",
"What is a Remote Browser?": "什麼是遠端瀏覽器?",
"Bitrix24 Webhook URL": "Bitrix24 WebHook URL",
"wayToGetBitrix24Webhook": "您可以按照 {0} 的步驟創建一個 Webhook",
"wayToGetBitrix24Webhook": "您可以按照 {0} 的步驟建立 Webhook",
"apiKeySevenIO": "SevenIO API 金鑰",
"ntfyPriorityHelptextAllEvents": "所有事件都以最高優先級發送",
"Telephone number": "手機號碼",
@ -948,11 +948,11 @@
"and": "和",
"whatHappensAtForumPost": "建立一個新的論壇文章。這不會在現有文章中發布。要在現有文章中發文,請使用“{option}”",
"aboutSlackUsername": "變更訊息寄件者的顯示名稱。如果您想提及他人,請將其包含在好友的名稱中。",
"remoteBrowsersDescription": "遠端瀏覽器是本機運行 Chromium 的替代方案。使用 browserless.io 等服務進行設定或連接到您自己的服務",
"remoteBrowsersDescription": "遠端瀏覽器可作為在本機執行 Chromium 的替代方案。使用 browserless.io 等服務進行設定或連接到您自己的服務",
"Money": "錢",
"successKeyword": "成功關鍵字",
"successKeywordExplanation": "MQTT 關鍵字將被視為成功",
"Refresh Interval Description": "狀態頁面將每 {0} 秒刷新一次完整網站",
"successKeywordExplanation": "作為成功判斷依據的 MQTT 關鍵字",
"Refresh Interval Description": "狀態頁面將每 {0} 秒完全重新整理一次網站",
"wayToGetDiscordThreadId": "取得主題 / 論壇文章 ID 與取得頻道 ID 類似。詳細了解如何取得 ID {0}",
"Don't mention people": "不要提及他人",
"Mention group": "提及 {group}",
@ -961,7 +961,7 @@
"cacheBusterParamDescription": "隨機生成參數以跳過快取。",
"gamedigGuessPort": "GameDig隨機埠",
"Message format": "訊息格式",
"Send rich messages": "發送豐富訊息",
"Send rich messages": "發送豐富格式文字訊息",
"bitrix24SupportUserID": "輸入您在 Bitrix24 中的使用者 ID。您可以透過使用者的個人資料連結找到 ID。",
"remoteBrowserToggle": "預設情況下Chromium 在 Uptime Kuma 容器內運作。您可以透過切換此開關來使用遠端瀏覽器。",
"Elevator": "電梯",
@ -973,7 +973,7 @@
"Fail": "失敗",
"Correct": "正確的",
"time ago": "{0} 以前",
"ignoredTLSError": "TLS/SSL 錯誤已被略過",
"ignoredTLSError": "已忽略 TLS/SSL 錯誤",
"now": "現在",
"-year": "-年",
"Json Query Expression": "JSON查詢表達式",
@ -981,64 +981,64 @@
"receiverInfoSevenIO": "如果接收號碼不在德國,您必須在號碼前面添加國家代碼(例如,對於來自美國的國家代碼 1請使用 117612121212 而不是 017612121212",
"callMeBotGet": "您可以在此處填寫您生成的用於 {0}、{1} 或 {2} 的端點。 請注意您可能會受到速率限制。 速率限制被推測為:{3}(僅供參考)",
"gtxMessagingFromHint": "在手機上,收件人會看到 TPOA 地址作為消息的發送者。TPOA 允許的格式包括至多11個字母或數字、短代碼、當地長代碼或國際號碼{e164}、{e212} 或 {e214} 格式)",
"deleteRemoteBrowserMessage": "您確定要刪除此遠程瀏覽器嗎,這會影響所有監控項",
"deleteRemoteBrowserMessage": "您確定要為所有監控器刪除此遠端瀏覽器嗎",
"Command": "命令",
"wayToGetSevenIOApiKey": "訪問 app.seven.io > 開發人員 > api 密鑰 > 綠色添加按鈕下的儀錶板",
"wayToGetSevenIOApiKey": "造訪儀表板,位於 app.seven.io > 開發人員 > API 金鑰 > 綠色新增按鈕下方",
"senderSevenIO": "發信人號碼或名稱",
"receiverSevenIO": "收信人號碼",
"wayToWriteWhapiRecipient": "可用格式為不含 + 號的國際通用格式手機號碼({0})、聯繫人 ID{1})或組 ID{2})。",
"wayToGetWhapiUrlAndToken": "您可以通過進入您想要的頻道來獲取 API URL 和令牌:{0}",
"whapiRecipient": "手機號碼 / 聯繫人 ID / 組 ID",
"API URL": "API 地址",
"wayToWriteWhapiRecipient": "可用格式為不含 + 號的國際通用格式手機號碼({0})、聯絡人 ID{1})或群組 ID{2})。",
"wayToGetWhapiUrlAndToken": "您可以透過從 {0} 進入您想要的頻道來取得 API URL 及權杖",
"whapiRecipient": "手機號碼 / 聯絡人 ID / 群組 ID",
"API URL": "API URL",
"wayToGetHeiiOnCallDetails": "如需了解如何獲取 Trigger ID 和 API 密鑰,請訪問 {documentation}",
"gtxMessagingApiKeyHint": "你可以在此找到你的 API 密鑰My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)",
"From Phone Number / Transmission Path Originating Address (TPOA)": "發件人電話號碼 / 傳輸路徑起始地址TPOA",
"To Phone Number": "收件人電話號碼",
"gtxMessagingToHint": "國際通用格式,需要前導 \"+\" {e164}、{e212} 或 {e214} 格式)",
"Alphanumeric (recommended)": "符或數字類型(推薦)",
"cellsyntOriginatortypeAlphanumeric": "字符或數字類型(最多 11 個字母或數字)。收件人無法向此號碼回覆消息。",
"Alphanumeric (recommended)": "英數字(推薦)",
"cellsyntOriginatortypeAlphanumeric": "英數字字串(最多 11 個字母或數字)。收件人無法向此號碼回覆訊息。",
"cellsyntOriginatortypeNumeric": "數字類型(最多 15 位數)需使用國際通用格式,不以 00+國家代碼開頭,例如若要使用英國的號碼 07920 110 000 需填寫 447920110000。收件人可向此號碼回覆消息。",
"max 11 alphanumeric characters": "最多 11 個字母或數字",
"Community String": "SNMP 通訊字符串",
"snmpCommunityStringHelptext": "此字符串用作密碼以驗證和控制對SNMP啟用設備的訪問。請將其與您的SNMP設備配置匹配。",
"OID (Object Identifier)": "OID對象標識符",
"snmpOIDHelptext": "輸入您想監控的感器或狀態的 OID。如果您不確定 OID 是什麼,可以使用 MIB 瀏覽器或 SNMP 軟件等網絡管理工具進行查找。",
"OID (Object Identifier)": "OID物件識別碼",
"snmpOIDHelptext": "輸入您想監控的器或狀態的 OID。如果您不確定 OID 是什麼,可以使用 MIB 瀏覽器或 SNMP 軟體等網路管理工具進行查詢。",
"Condition": "條件",
"SNMP Version": "SNMP 版本",
"Please enter a valid OID.": "請輸入一個合法的 OID。",
"wayToGetThreemaGateway": "你可以在 {0} 註冊 Threema 網關。",
"wayToGetThreemaGateway": "你可以在 {0} 註冊 Threema 閘道。",
"threemaRecipientTypeIdentity": "Threema ID",
"threemaRecipientTypePhoneFormat": "E.164 標準,不含前導 + 號",
"threemaSenderIdentity": "網關 ID",
"threemaApiAuthenticationSecret": "網關密鑰",
"threemaBasicModeInfo": "註:此通知類型所使用的 Threema 網關為基礎模式(服務器端加密)。更多細節參見 {0}。",
"apiKeysDisabledMsg": "由於登錄驗證被禁用API 密鑰也被禁用。",
"Host Onesender": "Onesender 服務器",
"Token Onesender": "Onesender 令牌",
"threemaSenderIdentity": "閘道 ID",
"threemaApiAuthenticationSecret": "閘道金鑰",
"threemaBasicModeInfo": "註:此通知類型所使用的 Threema 閘道為基礎模式(伺服器端加密)。更多細節參見 {0}。",
"apiKeysDisabledMsg": "由於停用登入驗證API 金鑰也被停用。",
"Host Onesender": "",
"Token Onesender": "Onesender 權杖",
"Recipient Type": "收件人類型",
"Private Number": "私密號碼",
"privateOnesenderDesc": "請確保電話號碼有效。要向私人電話號碼發送消格式形如628123456789",
"groupOnesenderDesc": "請確保分組 ID 有效。要向分組發送消格式形如628123456789-342345",
"Group ID": "組 ID",
"wayToGetOnesenderUrlandToken": "你可以在 Onesender 網站獲取地址和令牌。更多信息參見 {0}",
"Add Remote Browser": "添加遠程瀏覽器",
"Group Name": "組名稱",
"OAuth2: Client Credentials": "OAuth2客戶端憑據",
"Authentication Method": "鑒權方式",
"Authorization Header": "鑒權請求頭",
"Form Data Body": "表單數據請求體",
"OAuth Token URL": "OAuth 令牌地址",
"Client ID": "戶端 ID",
"Client Secret": "客戶端秘鑰",
"privateOnesenderDesc": "請確保電話號碼有效。要對私人電話號碼傳送訊格式形如628123456789",
"groupOnesenderDesc": "請確保群組 ID 有效。要對群組傳送訊格式形如628123456789-342345",
"Group ID": "組 ID",
"wayToGetOnesenderUrlandToken": "你可以在 Onesender 網站取得 URL 及權杖。詳細訊息請見 {0}",
"Add Remote Browser": "新增遠端瀏覽器",
"Group Name": "組名稱",
"OAuth2: Client Credentials": "OAuth2用戶端認證",
"Authentication Method": "認證方式",
"Authorization Header": "驗證標頭",
"Form Data Body": "表單資料主體",
"OAuth Token URL": "OAuth 權杖 URL",
"Client ID": "戶端 ID",
"Client Secret": "用戶端密碼",
"OAuth Scope": "OAuth 範圍",
"Optional: Space separated list of scopes": "可選項:用空格分隔的範圍列表",
"Go back to home page.": "返回到首頁。",
"No tags found.": "未找到標籤。",
"Lost connection to the socket server.": "與 socket 服務器的連接丟失。",
"Cannot connect to the socket server.": "無法連接到 socket 服務器。",
"Lost connection to the socket server.": "與 Socket 伺服器的連線中斷。",
"Cannot connect to the socket server.": "無法連接 Socket 伺服器。",
"conditionDelete": "刪除條件",
"conditionAddGroup": "添加分組",
"conditionDeleteGroup": "刪除組",
"conditionAddGroup": "新增群組",
"conditionDeleteGroup": "刪除組",
"equals": "相等",
"not equals": "不相等",
"contains": "包含",
@ -1051,39 +1051,86 @@
"greater than or equal to": "不少於",
"Notification Channel": "通知頻道",
"Sound": "聲音",
"Alphanumerical string and hyphens only": "僅限字母、數字和連字-",
"Alphanumerical string and hyphens only": "僅限字母、數字和連字-",
"Arcade": "Arcade拱廊",
"Harp": "Harp豎琴",
"Flute": "Flute長笛",
"Guitar": "Guitar吉他",
"Pop": "Pop流行音樂",
"Custom sound to override default notification sound": "自定義聲音,用以覆蓋默認通知聲音",
"Time Sensitive (iOS Only)": "即時通知(僅 iOS 可用",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即使設備處於專注模式,即時通知也會立即發送。",
"From": "件人",
"Custom sound to override default notification sound": "自訂鈴聲以覆蓋預設通知鈴聲",
"Time Sensitive (iOS Only)": "具時效性訊息(僅限 iOS ",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即使裝置處於專注模式,具時效性通知也會立即送達。",
"From": "件人",
"Can be found on:": "可在此找到:{0}",
"The phone number of the recipient in E.164 format.": "收件人的 E.164 格式電話號碼。",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "如需可被回復,請輸入發送者 ID 或 E.164 格式的手機號碼。",
"RabbitMQ Nodes": "RabbitMQ 管理節點",
"rabbitmqNodesDescription": "輸入 RabbitMQ 管理節點的 URL包括協議和端口。例如:{0}",
"rabbitmqNodesRequired": "請設置此監視項的節點。",
"rabbitmqNodesDescription": "輸入 RabbitMQ 管理節點的 URL包括協定及連接埠。例如:{0}",
"rabbitmqNodesRequired": "請設定此監視器的節點。",
"rabbitmqNodesInvalid": "請使用 RabbitMQ 節點的完整 URL即完全限定 URL以 http 開頭)。",
"RabbitMQ Username": "RabbitMQ 用戶名",
"RabbitMQ Username": "RabbitMQ 帳號",
"RabbitMQ Password": "RabbitMQ 密碼",
"rabbitmqHelpText": "要使用此監控項,您需要在 RabbitMQ 設置中啟用管理插件。有關更多信息,請參閱 {rabitmq_documentation}。",
"SendGrid API Key": "SendGrid API 鑰",
"SendGrid API Key": "SendGrid API 鑰",
"not ends with": "不以此結尾",
"less than": "少於",
"Originator": "發件人",
"cellsyntOriginator": "在收件人處作為消息發送者顯示。允許的內容取決於發件人類型。",
"cellsyntDestination": "收件人的手機號碼需要使用以 00+國家代碼開頭的國際通用格式,例如若要發給英國的號碼 07920 110 000 需使用 00447920110000 作為收件人手機號碼至多17位數。需發送給多個收件人手機號碼時可使用英文逗號分隔每次請求最 多250 00個收件人手機號碼。",
"SIGNL4": "SIGNL4",
"SIGNL4 Webhook URL": "SIGNL4 Webhook 地址",
"signl4Docs": "你可以在此找到更多關於如何配置 SIGNL4 以及如何獲取 SIGNL4 Webhook 地址的信息{0}。",
"SIGNL4 Webhook URL": "SIGNL4 Webhook URL",
"signl4Docs": "您可以在此了解更多關於如何設定 SIGNL4 以及如何取得 SIGNL4 Webhook URL的資訊{0}。",
"Conditions": "條件",
"conditionAdd": "添加條件",
"conditionAdd": "新增條件",
"conditionValuePlaceholder": "值",
"Separate multiple email addresses with commas": "用逗號分隔多個電子郵件地址",
"record": "記錄",
"New Group": "新分組"
"New Group": "新群組",
"Font Twemoji by Twitter licensed under": "已經由 Twitter 的 Twemoji 授權",
"Phone numbers": "手機號碼",
"smsplanetApiToken": "SMSPlanet 的 Token",
"smsplanetApiDocs": "有關取得 API token的詳細信息請參閱 {the_smsplanet_documentation}。",
"the smsplanet documentation": "smsplanet 說明文件",
"Sender name": "發送者名稱",
"smsplanetNeedToApproveName": "需在用戶介面獲得允許",
"templateServiceName": "伺服器名稱",
"templateHostnameOrURL": "主機名稱或 URL",
"templateStatus": "狀態",
"telegramUseTemplate": "使用自訂訊息模板",
"telegramTemplateFormatDescription": "Telegram 允許使用不同的標記語言來傳送訊息,有關具體詳情請參閱 Telegram {0}。",
"telegramUseTemplateDescription": "如果啟用則使用自訂模板發送訊息。",
"Plain Text": "純文字",
"Message Template": "訊息模板",
"wayToGetWahaApiUrl": "你的 WAHA 主機 URL。",
"wayToGetWahaApiKey": "API Key 是你在 WAHA 使用的環境變數 WHATSAPP_API_KEY 的值。",
"wayToGetWahaSession": "從這個 WAHA 會話發送通知到聊天ID。你可以從 WAHA 儀錶板找到它。",
"Template Format": "模板格式",
"wahaSession": "WAHA 會話",
"wahaChatId": "聊天室ID (手機號碼/聯絡人 ID/群組 ID)",
"wayToWriteWahaChatId": "帶有國際前綴但不含開頭加號 ({0})、聯絡人 ID ({1}) 或群組 ID ({2}) 的電話號碼。通知從 WAHA 會話傳送到此聊天 ID。",
"YZJ Webhook URL": "YZJ 的 Webhook URL",
"YZJ Robot Token": "YZJ 的機器人 token",
"telegramServerUrl": "(可選) 伺服器 URL",
"telegramServerUrlDescription": "解除 Telegram 的機器人 API 限製或在被封鎖區域(中國、伊朗等)取得存取權限。欲了解更多信息,請點擊{0}。預設值:{1}",
"ipFamilyDescriptionAutoSelect": "透過 {happyEyeballs} 判斷 IP 家族。",
"Happy Eyeballs algorithm": "Happy Eyeballs 演算法",
"Use HTML for custom E-mail body": "在自訂 Email 內文中使用 HTML",
"Disable URL in Notification": "在通知中停用 URL",
"smseagleMsgType": "訊息類型",
"smseagleApiv1": "APIv1用於現有專案及維持向後相容",
"Ip Family": "IP 家族",
"ntfyPriorityDown": "下線事件優先級",
"smseagleGroupV2": "電話簿群組 ID",
"smseagleContactV2": "電話簿聯絡人 ID",
"smseagleMsgSms": "SMS 訊息(預設)",
"smseagleDuration": "長度(秒)",
"smseagleTtsModel": "文字轉語音模型 ID",
"smseagleApiType": "API 版本",
"smseagleApiv2": "APIv2推薦用於新的整合",
"smseagleComma": "多個項目間須以逗號分隔",
"SpugPush Template Code": "範本代碼",
"Clear Form": "清除表單",
"pause": "暫停",
"Staged Tags for Batch Add": "暫存標籤以批次新增",
"Add Another Tag": "新增標籤"
}

View file

@ -16,7 +16,7 @@
<span class="fs-4 title">{{ $t("Uptime Kuma") }}</span>
</router-link>
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/uptime-kuma/releases" class="btn btn-info me-3">
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/uptime-kuma/releases" class="btn btn-primary me-3">
<font-awesome-icon icon="arrow-alt-circle-up" /> {{ $t("New Update") }}
</a>

View file

@ -9,7 +9,8 @@
<div>{{ monitor.id }}</div>
</div>
</h1>
<p v-if="monitor.description">{{ monitor.description }}</p>
<!-- eslint-disable-next-line vue/no-v-html-->
<p v-if="monitor.description" v-html="descriptionHTML"></p>
<div class="d-flex">
<div class="tags">
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
@ -285,6 +286,8 @@ import Tag from "../components/Tag.vue";
import CertificateInfo from "../components/CertificateInfo.vue";
import { getMonitorRelativeURL } from "../util.ts";
import { URL } from "whatwg-url";
import DOMPurify from "dompurify";
import { marked } from "marked";
import { getResBaseURL } from "../util-frontend";
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-clike";
@ -399,6 +402,14 @@ export default {
screenshotURL() {
return getResBaseURL() + this.monitor.screenshot + "?time=" + this.cacheTime;
},
descriptionHTML() {
if (this.monitor.description != null) {
return DOMPurify.sanitize(marked(this.monitor.description));
} else {
return "";
}
}
},

View file

@ -223,12 +223,12 @@
<div class="row">
<div class="col">
<div class="mb-2">{{ $t("startDateTime") }}</div>
<input v-model="maintenance.dateRange[0]" type="datetime-local" class="form-control" :required="maintenance.strategy === 'single'">
<input v-model="maintenance.dateRange[0]" type="datetime-local" max="9999-12-31T23:59" class="form-control" :required="maintenance.strategy === 'single'">
</div>
<div class="col">
<div class="mb-2">{{ $t("endDateTime") }}</div>
<input v-model="maintenance.dateRange[1]" type="datetime-local" class="form-control" :required="maintenance.strategy === 'single'">
<input v-model="maintenance.dateRange[1]" type="datetime-local" max="9999-12-31T23:59" class="form-control" :required="maintenance.strategy === 'single'">
</div>
</div>
</div>

View file

@ -24,6 +24,9 @@
<option value="ping">
Ping
</option>
<option value="smtp">
SMTP
</option>
<option value="snmp">
SNMP
</option>
@ -52,6 +55,9 @@
<option value="push">
Push
</option>
<option value="manual">
{{ $t("Manual") }}
</option>
</optgroup>
<optgroup :label="$t('Specific Monitor Type')">
@ -109,7 +115,19 @@
<!-- Friendly Name -->
<div class="my-3">
<label for="name" class="form-label">{{ $t("Friendly Name") }}</label>
<input id="name" v-model="monitor.name" type="text" class="form-control" required data-testid="friendly-name-input">
<input id="name" v-model="monitor.name" type="text" class="form-control" data-testid="friendly-name-input" :placeholder="defaultFriendlyName">
</div>
<!-- Manual Status switcher -->
<div v-if="monitor.type === 'manual'" class="mb-3">
<div class="btn-group w-100 mb-3">
<button class="btn btn-success" @click="monitor.manual_status = 1">
<i class="fas fa-check"></i> {{ $t("Up") }}
</button>
<button class="btn btn-danger" @click="monitor.manual_status = 0">
<i class="fas fa-times"></i> {{ $t("Down") }}
</button>
</div>
</div>
<!-- URL -->
@ -281,8 +299,8 @@
</template>
<!-- Hostname -->
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping / SNMP only -->
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'snmp'" class="my-3">
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping / SNMP / SMTP only -->
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'smtp' || monitor.type === 'snmp'" class="my-3">
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
<input
id="hostname"
@ -297,7 +315,7 @@
<!-- Port -->
<!-- For TCP Port / Steam / MQTT / Radius Type / SNMP -->
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'snmp'" class="my-3">
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'smtp' || monitor.type === 'snmp'" class="my-3">
<label for="port" class="form-label">{{ $t("Port") }}</label>
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
</div>
@ -329,6 +347,18 @@
</select>
</div>
<div v-if="monitor.type === 'smtp'" class="my-3">
<label for="smtp_security" class="form-label">{{ $t("SMTP Security") }}</label>
<select id="smtp_security" v-model="monitor.smtpSecurity" class="form-select">
<option value="secure">SMTPS</option>
<option value="nostarttls">Ignore STARTTLS</option>
<option value="starttls">Use STARTTLS</option>
</select>
<div class="form-text">
{{ $t("smtpHelpText") }}
</div>
</div>
<!-- Json Query -->
<!-- For Json Query / SNMP -->
<div v-if="monitor.type === 'json-query' || monitor.type === 'snmp'" class="my-3">
@ -595,10 +625,14 @@
<input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required :min="minInterval" step="1">
</div>
<!-- Timeout: HTTP / Keyword / SNMP only -->
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'snmp' || monitor.type === 'rabbitmq'" class="my-3">
<label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label>
<input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1">
<!-- Timeout: HTTP / JSON query / Keyword / Ping / RabbitMQ / SNMP only -->
<div v-if="monitor.type === 'http' || monitor.type === 'json-query' || monitor.type === 'keyword' || monitor.type === 'ping' || monitor.type === 'rabbitmq' || monitor.type === 'snmp'" class="my-3">
<label for="timeout" class="form-label">
{{ monitor.type === 'ping' ? $t("pingGlobalTimeoutLabel") : $t("Request Timeout") }}
<span v-if="monitor.type !== 'ping'">({{ $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})</span>
</label>
<input id="timeout" v-model="monitor.timeout" type="number" class="form-control" :min="timeoutMin" :max="timeoutMax" :step="timeoutStep" required>
<div v-if="monitor.type === 'ping'" class="form-text">{{ $t("pingGlobalTimeoutDescription") }}</div>
</div>
<div class="my-3">
@ -660,10 +694,39 @@
</div>
</div>
<!-- Ping packet size -->
<!-- Max Packets / Count -->
<div v-if="monitor.type === 'ping'" class="my-3">
<label for="ping-count" class="form-label">{{ $t("pingCountLabel") }}</label>
<input id="ping-count" v-model="monitor.ping_count" type="number" class="form-control" required min="1" max="100" step="1">
<div class="form-text">
{{ $t("pingCountDescription") }}
</div>
</div>
<!-- Numeric Output -->
<div v-if="monitor.type === 'ping'" class="my-3 form-check">
<input id="ping_numeric" v-model="monitor.ping_numeric" type="checkbox" class="form-check-input" :checked="monitor.ping_numeric">
<label class="form-check-label" for="ping_numeric">
{{ $t("pingNumericLabel") }}
</label>
<div class="form-text">
{{ $t("pingNumericDescription") }}
</div>
</div>
<!-- Packet size -->
<div v-if="monitor.type === 'ping'" class="my-3">
<label for="packet-size" class="form-label">{{ $t("Packet Size") }}</label>
<input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required min="1" max="65500" step="1">
<input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required min="1" :max="65500" step="1">
</div>
<!-- per-request timeout -->
<div v-if="monitor.type === 'ping'" class="my-3">
<label for="ping_per_request_timeout" class="form-label">{{ $t("pingPerRequestTimeoutLabel") }}</label>
<input id="ping_per_request_timeout" v-model="monitor.ping_per_request_timeout" type="number" class="form-control" required min="0" max="300" step="1">
<div class="form-text">
{{ $t("pingPerRequestTimeoutDescription") }}
</div>
</div>
<!-- HTTP / Keyword only -->
@ -697,6 +760,20 @@
{{ $t("acceptedStatusCodesDescription") }}
</div>
</div>
<div class="my-3">
<label for="ipFamily" class="form-label">{{ $t("Ip Family") }}</label>
<select id="ipFamily" v-model="monitor.ipFamily" class="form-select">
<option :value="null">{{ $t("auto-select") }}</option>
<option value="ipv4">IPv4</option>
<option value="ipv6">IPv6</option>
</select>
<i18n-t v-if="monitor.ipFamily == null" keypath="ipFamilyDescriptionAutoSelect" tag="div" class="form-text">
<template #happyEyeballs>
<a href="https://en.wikipedia.org/wiki/Happy_Eyeballs" target="_blank">{{ $t("Happy Eyeballs algorithm") }}</a>
</template>
</i18n-t>
</div>
</template>
<!-- Parent Monitor -->
@ -948,6 +1025,10 @@
<label for="oauth_scopes" class="form-label">{{ $t("OAuth Scope") }}</label>
<input id="oauth_scopes" v-model="monitor.oauth_scopes" type="text" class="form-control" :placeholder="$t('Optional: Space separated list of scopes')">
</div>
<div class="my-3">
<label for="oauth_audience" class="form-label">{{ $t("OAuth Audience") }}</label>
<input id="oauth_audience" v-model="monitor.oauth_audience" type="text" class="form-control" :placeholder="$t('Optional: The audience to request the JWT for')">
</div>
</template>
</template>
<template v-else>
@ -1060,7 +1141,13 @@ import DockerHostDialog from "../components/DockerHostDialog.vue";
import RemoteBrowserDialog from "../components/RemoteBrowserDialog.vue";
import ProxyDialog from "../components/ProxyDialog.vue";
import TagsManager from "../components/TagsManager.vue";
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } from "../util.ts";
import {
genSecret,
isDev,
MAX_INTERVAL_SECOND,
MIN_INTERVAL_SECOND,
sleep,
} from "../util.ts";
import { hostNameRegexPattern } from "../util-frontend";
import HiddenInput from "../components/HiddenInput.vue";
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
@ -1075,6 +1162,7 @@ const monitorDefaults = {
parent: null,
url: "https://",
method: "GET",
ipFamily: null,
interval: 60,
retryInterval: 60,
resendInterval: 0,
@ -1082,7 +1170,6 @@ const monitorDefaults = {
notificationIDList: {},
ignoreTls: false,
upsideDown: false,
packetSize: 56,
expiryNotification: false,
maxredirects: 10,
accepted_statuscodes: [ "200-299" ],
@ -1157,6 +1244,48 @@ export default {
},
computed: {
timeoutStep() {
return this.monitor.type === "ping" ? 1 : 0.1;
},
timeoutMin() {
return this.monitor.type === "ping" ? 1 : 0;
},
timeoutMax() {
return this.monitor.type === "ping" ? 60 : undefined;
},
timeoutLabel() {
return this.monitor.type === "ping" ? this.$t("pingTimeoutLabel") : this.$t("Request Timeout");
},
timeoutDescription() {
if (this.monitor.type === "ping") {
return this.$t("pingTimeoutDescription");
}
return "";
},
defaultFriendlyName() {
if (this.monitor.hostname) {
return this.monitor.hostname;
}
if (this.monitor.url) {
if (this.monitor.url !== "http://" && this.monitor.url !== "https://") {
// Ensure monitor without a URL is not affected by invisible URL.
try {
const url = new URL(this.monitor.url);
return url.hostname;
} catch (e) {
return this.monitor.url.replace(/https?:\/\//, "");
}
}
}
// Default placeholder if neither hostname nor URL is available
return this.$t("defaultFriendlyName");
},
ipRegex() {
// Allow to test with simple dns server with port (127.0.0.1:5300)
@ -1175,6 +1304,7 @@ export default {
}
return this.$t(name);
},
remoteBrowsersOptions() {
return this.$root.remoteBrowserList.map(browser => {
return {
@ -1183,6 +1313,7 @@ export default {
};
});
},
remoteBrowsersToggle: {
get() {
return this.remoteBrowsersEnabled || this.monitor.remote_browser != null;
@ -1200,6 +1331,7 @@ export default {
}
}
},
isAdd() {
return this.$route.path === "/add";
},
@ -1250,6 +1382,7 @@ message HealthCheckResponse {
}
` ]);
},
bodyPlaceholder() {
if (this.monitor && this.monitor.httpBodyEncoding && this.monitor.httpBodyEncoding === "xml") {
return this.$t("Example:", [ `
@ -1415,9 +1548,25 @@ message HealthCheckResponse {
},
"monitor.timeout"(value, oldValue) {
// keep timeout within 80% range
if (value && value !== oldValue) {
this.monitor.timeout = this.clampTimeout(value);
if (this.monitor.type === "ping") {
this.finishUpdateInterval();
} else {
// keep timeout within 80% range
if (value && value !== oldValue) {
this.monitor.timeout = this.clampTimeout(value);
}
}
},
"monitor.ping_count"() {
if (this.monitor.type === "ping") {
this.finishUpdateInterval();
}
},
"monitor.ping_per_request_timeout"() {
if (this.monitor.type === "ping") {
this.finishUpdateInterval();
}
},
@ -1446,8 +1595,10 @@ message HealthCheckResponse {
// Set a default timeout if the monitor type has changed or if it's a new monitor
if (oldType || this.isAdd) {
if (this.monitor.type === "snmp") {
// snmp is not expected to be executed via the internet => we can choose a lower default timeout
// snmp is not expected to be executed via the internet => we can choose a lower default timeout
this.monitor.timeout = 5;
} else if (this.monitor.type === "ping") {
this.monitor.timeout = 10;
} else {
this.monitor.timeout = 48;
}
@ -1564,7 +1715,11 @@ message HealthCheckResponse {
if (this.isAdd) {
this.monitor = {
...monitorDefaults
...monitorDefaults,
ping_count: 3,
ping_numeric: true,
packetSize: 56,
ping_per_request_timeout: 2,
};
if (this.$root.proxyList && !this.monitor.proxyId) {
@ -1627,7 +1782,12 @@ message HealthCheckResponse {
}
// Handling for monitors that are missing/zeroed timeout
if (!this.monitor.timeout) {
this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
if (this.monitor.type === "ping") {
// set to default
this.monitor.timeout = 10;
} else {
this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
}
}
} else {
this.$root.toastError(res.msg);
@ -1700,6 +1860,10 @@ message HealthCheckResponse {
this.processing = true;
if (!this.monitor.name) {
this.monitor.name = this.defaultFriendlyName;
}
if (!this.isInputValid()) {
this.processing = false;
return;
@ -1840,11 +2004,48 @@ message HealthCheckResponse {
return Number.isFinite(clamped) ? clamped : maxTimeout;
},
calculatePingInterval() {
// If monitor.type is not "ping", simply return the configured interval
if (this.monitor.type !== "ping") {
return this.monitor.interval;
}
// Calculate the maximum theoretical time needed if every ping request times out
const theoreticalTotal = this.monitor.ping_count * this.monitor.ping_per_request_timeout;
// The global timeout (aka deadline) forces ping to terminate, so the effective limit
// is the smaller value between deadline and theoreticalTotal
const effectiveLimit = Math.min(this.monitor.timeout, theoreticalTotal);
// Add a 10% margin to the effective limit to ensure proper handling
const adjustedLimit = Math.ceil(effectiveLimit * 1.1);
// If the calculated limit is lower than the minimum allowed interval, use the minimum interval
if (adjustedLimit < this.minInterval) {
return this.minInterval;
}
return adjustedLimit;
},
finishUpdateInterval() {
// Update timeout if it is greater than the clamp timeout
let clampedValue = this.clampTimeout(this.monitor.interval);
if (this.monitor.timeout > clampedValue) {
this.monitor.timeout = clampedValue;
if (this.monitor.type === "ping") {
// Calculate the minimum required interval based on ping configuration
const calculatedPingInterval = this.calculatePingInterval();
// If the configured interval is too small, adjust it to the minimum required value
if (this.monitor.interval < calculatedPingInterval) {
this.monitor.interval = calculatedPingInterval;
// Notify the user that the interval has been automatically adjusted
toast.info(this.$t("pingIntervalAdjustedInfo"));
}
} else {
// Update timeout if it is greater than the clamp timeout
let clampedValue = this.clampTimeout(this.monitor.interval);
if (this.monitor.timeout > clampedValue) {
this.monitor.timeout = clampedValue;
}
}
},

View file

@ -157,12 +157,12 @@
<!-- Admin functions -->
<div v-if="hasToken" class="mb-4">
<div v-if="!enableEditMode">
<button class="btn btn-info me-2" data-testid="edit-button" @click="edit">
<button class="btn btn-primary me-2" data-testid="edit-button" @click="edit">
<font-awesome-icon icon="edit" />
{{ $t("Edit Status Page") }}
</button>
<a href="/manage-status-page" class="btn btn-info">
<a href="/manage-status-page" class="btn btn-primary">
<font-awesome-icon icon="tachometer-alt" />
{{ $t("Go to Dashboard") }}
</a>
@ -720,7 +720,7 @@ export default {
// Configure auto-refresh loop
feedInterval = setInterval(() => {
this.updateHeartbeatList();
}, (this.config.autoRefreshInterval + 10) * 1000);
}, Math.max(5, this.config.autoRefreshInterval) * 1000);
this.updateUpdateTimer();
}).catch( function (error) {
@ -806,7 +806,15 @@ export default {
clearInterval(this.updateCountdown);
this.updateCountdown = setInterval(() => {
const countdown = dayjs.duration(this.lastUpdateTime.add(this.config.autoRefreshInterval, "seconds").add(10, "seconds").diff(dayjs()));
// rounding here as otherwise we sometimes skip numbers in cases of time drift
const countdown = dayjs.duration(
Math.round(
this.lastUpdateTime
.add(Math.max(5, this.config.autoRefreshInterval), "seconds")
.diff(dayjs())
/ 1000
), "seconds");
if (countdown.as("seconds") < 0) {
clearInterval(this.updateCountdown);
} else {

View file

@ -8,19 +8,15 @@
// Backend uses the compiled file util.js
// Frontend uses util.ts
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0;
exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0;
const dayjs_1 = __importDefault(require("dayjs"));
const dayjs = require("dayjs");
exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_MAX = exports.PING_PER_REQUEST_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_GLOBAL_TIMEOUT_DEFAULT = exports.PING_GLOBAL_TIMEOUT_MAX = exports.PING_GLOBAL_TIMEOUT_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = void 0;
const dayjs_1 = require("dayjs");
const jsonata = require("jsonata");
exports.isDev = process.env.NODE_ENV === "development";
exports.isNode = typeof process !== "undefined" && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node);
const dayjs = (exports.isNode) ? require("dayjs") : dayjs_1.default;
exports.appName = "Uptime Kuma";
exports.DOWN = 0;
exports.UP = 1;
@ -35,6 +31,18 @@ exports.SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm";
exports.MAX_INTERVAL_SECOND = 2073600;
exports.MIN_INTERVAL_SECOND = 20;
exports.PING_PACKET_SIZE_MIN = 1;
exports.PING_PACKET_SIZE_MAX = 65500;
exports.PING_PACKET_SIZE_DEFAULT = 56;
exports.PING_GLOBAL_TIMEOUT_MIN = 1;
exports.PING_GLOBAL_TIMEOUT_MAX = 300;
exports.PING_GLOBAL_TIMEOUT_DEFAULT = 10;
exports.PING_COUNT_MIN = 1;
exports.PING_COUNT_MAX = 100;
exports.PING_COUNT_DEFAULT = 1;
exports.PING_PER_REQUEST_TIMEOUT_MIN = 1;
exports.PING_PER_REQUEST_TIMEOUT_MAX = 60;
exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = 2;
exports.CONSOLE_STYLE_Reset = "\x1b[0m";
exports.CONSOLE_STYLE_Bright = "\x1b[1m";
exports.CONSOLE_STYLE_Dim = "\x1b[2m";
@ -66,7 +74,6 @@ exports.CONSOLE_STYLE_BgMagenta = "\x1b[45m";
exports.CONSOLE_STYLE_BgCyan = "\x1b[46m";
exports.CONSOLE_STYLE_BgWhite = "\x1b[47m";
exports.CONSOLE_STYLE_BgGray = "\x1b[100m";
const consoleModuleColors = [
exports.CONSOLE_STYLE_FgCyan,
exports.CONSOLE_STYLE_FgGreen,
@ -159,11 +166,11 @@ class Logger {
module = module.toUpperCase();
level = level.toUpperCase();
let now;
if (dayjs_1.default.tz) {
now = dayjs_1.default.tz(new Date()).format();
if (dayjs.tz) {
now = dayjs.tz(new Date()).format();
}
else {
now = (0, dayjs_1.default)().format();
now = dayjs().format();
}
const levelColor = consoleLevelColors[level];
const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)];
@ -264,11 +271,11 @@ function polyfill() {
exports.polyfill = polyfill;
class TimeLogger {
constructor() {
this.startTime = (0, dayjs_1.default)().valueOf();
this.startTime = dayjs().valueOf();
}
print(name) {
if (exports.isDev && process.env.TIMELOGGER === "1") {
console.log(name + ": " + ((0, dayjs_1.default)().valueOf() - this.startTime) + "ms");
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
}
}
}
@ -380,19 +387,19 @@ function parseTimeFromTimeObject(obj) {
}
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
function isoToUTCDateTime(input) {
return (0, dayjs_1.default)(input).utc().format(exports.SQL_DATETIME_FORMAT);
return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT);
}
exports.isoToUTCDateTime = isoToUTCDateTime;
function utcToISODateTime(input) {
return dayjs_1.default.utc(input).toISOString();
return dayjs.utc(input).toISOString();
}
exports.utcToISODateTime = utcToISODateTime;
function utcToLocal(input, format = exports.SQL_DATETIME_FORMAT) {
return dayjs_1.default.utc(input).local().format(format);
return dayjs.utc(input).local().format(format);
}
exports.utcToLocal = utcToLocal;
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
return (0, dayjs_1.default)(input).utc().format(format);
return dayjs(input).utc().format(format);
}
exports.localToUTC = localToUTC;
function intHash(str, length = 10) {

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