Compare commits

...

24 Commits

Author SHA1 Message Date
Louis Lam
056d957c1e Update to 1.19.1 2022-12-26 23:49:20 +08:00
Louis Lam
e12225e595 Fix #2475 #2468 #2455, add Accept-Encoding only if encountered the abort error 2022-12-26 21:00:46 +08:00
Louis Lam
1b6c587cc9 Fix #2472 2022-12-26 14:46:29 +08:00
Louis Lam
4a1db336df Merge pull request #2465 from augustin64/patch-1
(fr) Fix typo
2022-12-25 19:18:46 +08:00
Augustin
9e9c5cd1d2 (fr) Fix typo 2022-12-24 19:41:55 +01:00
Louis Lam
1e689d99b4 Merge pull request #2393 from zImPatrick/discord-docker-fix
Fix discord notification not sending when docker container goes down
2022-12-24 14:26:53 +08:00
Louis Lam
14fffcf06b Globally fix if heartbeatJSON["msg"] is undefined 2022-12-24 14:23:50 +08:00
Louis Lam
6962e056ce Update to 1.19.0 2022-12-23 22:44:49 +08:00
Louis Lam
b7a898326e Merge pull request #2437 from MrEddX/bulgarian
Update bg-BG.js
2022-12-19 20:22:47 +08:00
MrEddX
a89be0e6d4 Update bg-BG.js
Translation Update
2022-12-19 13:37:42 +02:00
Louis Lam
b58b83541b Merge pull request #2428 from rbunpat/master
Update th-TH.js
2022-12-18 15:40:40 +08:00
Cyril59310
df4f91c20d Update EN language (#2429)
* Update EN language
2022-12-18 15:39:54 +08:00
Rachatat Bunpat
fc6b040a4e Update th-TH.js 2022-12-17 17:34:03 +07:00
Louis Lam
9838f36b50 Merge pull request #2418 from Johno95CZ/patch-1
Update cs-CZ.js
2022-12-17 17:44:55 +08:00
Louis Lam
a60072adbc Merge pull request #2423 from rbunpat/master
Update th-TH.js
2022-12-17 17:43:51 +08:00
Louis Lam
e7aeb6f6bf Merge pull request #2422 from Justman10000/master
Update
2022-12-17 17:43:25 +08:00
Rachatat Bunpat
06e570c52d Update th-TH.js 2022-12-16 19:15:14 +07:00
Justman10000
890b8f8333 Updated German 2022-12-16 10:42:41 +00:00
Louis Lam
df21f7da76 Check login for initServerTimezone 2022-12-16 12:56:40 +08:00
JohnoCZ
20c37a70f7 Update cs-CZ.js 2022-12-15 14:35:59 +01:00
Louis Lam
b75db27658 Fix lint 2022-12-15 13:57:28 +08:00
Louis Lam
765d8e1297 Fix #2318 2022-12-15 13:39:48 +08:00
Louis Lam
9dc2cc1f0d Copy timezone.js from dayjs 2022-12-15 13:05:04 +08:00
zImPatrick
f32441e2f6 fix discord notification not sending when docker container is down 2022-12-12 18:05:10 +01:00
19 changed files with 588 additions and 97 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.19.0-beta.2", "version": "1.19.1",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -38,7 +38,7 @@
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.18.5 && npm ci --production && npm run download-dist", "setup": "git checkout 1.19.1 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",

View File

@@ -16,7 +16,6 @@ const { CacheableDnsHttpAgent } = require("../cacheable-dns-http-agent");
const { DockerHost } = require("../docker"); const { DockerHost } = require("../docker");
const Maintenance = require("./maintenance"); const Maintenance = require("./maintenance");
const { UptimeCacheList } = require("../uptime-cache-list"); const { UptimeCacheList } = require("../uptime-cache-list");
const zlib = require("zlib");
/** /**
* status: * status:
@@ -276,9 +275,6 @@ class Monitor extends BeanModel {
...(this.body ? { data: JSON.parse(this.body) } : {}), ...(this.body ? { data: JSON.parse(this.body) } : {}),
timeout: this.interval * 1000 * 0.8, timeout: this.interval * 1000 * 0.8,
headers: { headers: {
// Fix #2253
// Read more: https://stackoverflow.com/questions/1759956/curl-error-18-transfer-closed-with-outstanding-read-data-remaining
"Accept-Encoding": "gzip, deflate",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"User-Agent": "Uptime-Kuma/" + version, "User-Agent": "Uptime-Kuma/" + version,
...(this.headers ? JSON.parse(this.headers) : {}), ...(this.headers ? JSON.parse(this.headers) : {}),
@@ -311,20 +307,8 @@ class Monitor extends BeanModel {
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`); log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
log.debug("monitor", `[${this.name}] Axios Request`); log.debug("monitor", `[${this.name}] Axios Request`);
let res; // Make Request
if (this.auth_method === "ntlm") { let res = await this.makeAxiosRequest(options);
options.httpsAgent.keepAlive = true;
res = await httpNtlm(options, {
username: this.basic_auth_user,
password: this.basic_auth_pass,
domain: this.authDomain,
workstation: this.authWorkstation ? this.authWorkstation : undefined
});
} else {
res = await axios.request(options);
}
bean.msg = `${res.status} - ${res.statusText}`; bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime; bean.ping = dayjs().valueOf() - startTime;
@@ -762,6 +746,40 @@ class Monitor extends BeanModel {
} }
} }
async makeAxiosRequest(options, finalCall = false) {
try {
let res;
if (this.auth_method === "ntlm") {
options.httpsAgent.keepAlive = true;
res = await httpNtlm(options, {
username: this.basic_auth_user,
password: this.basic_auth_pass,
domain: this.authDomain,
workstation: this.authWorkstation ? this.authWorkstation : undefined
});
} else {
res = await axios.request(options);
}
return res;
} catch (e) {
// Fix #2253
// Read more: https://stackoverflow.com/questions/1759956/curl-error-18-transfer-closed-with-outstanding-read-data-remaining
if (!finalCall && typeof e.message === "string" && e.message.includes("maxContentLength size of -1 exceeded")) {
log.debug("monitor", "makeAxiosRequest with gzip");
options.headers["Accept-Encoding"] = "gzip, deflate";
return this.makeAxiosRequest(options, true);
} else {
if (typeof e.message === "string" && e.message.includes("maxContentLength size of -1 exceeded")) {
e.message = "response timeout: incomplete response within a interval";
}
throw e;
}
}
}
/** Stop monitor */ /** Stop monitor */
stop() { stop() {
clearTimeout(this.heartbeatInterval); clearTimeout(this.heartbeatInterval);
@@ -1069,7 +1087,13 @@ class Monitor extends BeanModel {
for (let notification of notificationList) { for (let notification of notificationList) {
try { try {
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), bean.toJSON()); // Prevent if the msg is undefined, notifications such as Discord cannot send out.
const heartbeatJSON = bean.toJSON();
if (!heartbeatJSON["msg"]) {
heartbeatJSON["msg"] = "";
}
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), heartbeatJSON);
} catch (e) { } catch (e) {
log.error("monitor", "Cannot send notification to " + notification.name); log.error("monitor", "Cannot send notification to " + notification.name);
log.error("monitor", e); log.error("monitor", e);

View File

@@ -0,0 +1,20 @@
import { PluginFunc, ConfigType } from 'dayjs'
declare const plugin: PluginFunc
export = plugin
declare module 'dayjs' {
interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs
offsetName(type?: 'short' | 'long'): string | undefined
}
interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs
(date: ConfigType, format: string, timezone?: string): Dayjs
guess(): string
setDefault(timezone?: string): void
}
const tz: DayjsTimezone
}

View File

@@ -0,0 +1,115 @@
/**
* Copy from node_modules/dayjs/plugin/timezone.js
* Try to fix https://github.com/louislam/uptime-kuma/issues/2318
* Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc
* License: MIT
*/
!function (t, e) {
// eslint-disable-next-line no-undef
typeof exports == "object" && typeof module != "undefined" ? module.exports = e() : typeof define == "function" && define.amd ? define(e) : (t = typeof globalThis != "undefined" ? globalThis : t || self).dayjs_plugin_timezone = e();
}(this, (function () {
"use strict";
let t = {
year: 0,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5
};
let e = {};
return function (n, i, o) {
let r;
let a = function (t, n, i) {
void 0 === i && (i = {});
let o = new Date(t);
let r = function (t, n) {
void 0 === n && (n = {});
let i = n.timeZoneName || "short";
let o = t + "|" + i;
let r = e[o];
return r || (r = new Intl.DateTimeFormat("en-US", {
hour12: !1,
timeZone: t,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: i
}), e[o] = r), r;
}(n, i);
return r.formatToParts(o);
};
let u = function (e, n) {
let i = a(e, n);
let r = [];
let u = 0;
for (; u < i.length; u += 1) {
let f = i[u];
let s = f.type;
let m = f.value;
let c = t[s];
c >= 0 && (r[c] = parseInt(m, 10));
}
let d = r[3];
let l = d === 24 ? 0 : d;
let v = r[0] + "-" + r[1] + "-" + r[2] + " " + l + ":" + r[4] + ":" + r[5] + ":000";
let h = +e;
return (o.utc(v).valueOf() - (h -= h % 1e3)) / 6e4;
};
let f = i.prototype;
f.tz = function (t, e) {
void 0 === t && (t = r);
let n = this.utcOffset();
let i = this.toDate();
let a = i.toLocaleString("en-US", { timeZone: t }).replace("\u202f", " ");
let u = Math.round((i - new Date(a)) / 1e3 / 60);
let f = o(a).$set("millisecond", this.$ms).utcOffset(15 * -Math.round(i.getTimezoneOffset() / 15) - u, !0);
if (e) {
let s = f.utcOffset();
f = f.add(n - s, "minute");
}
return f.$x.$timezone = t, f;
}, f.offsetName = function (t) {
let e = this.$x.$timezone || o.tz.guess();
let n = a(this.valueOf(), e, { timeZoneName: t }).find((function (t) {
return t.type.toLowerCase() === "timezonename";
}));
return n && n.value;
};
let s = f.startOf;
f.startOf = function (t, e) {
if (!this.$x || !this.$x.$timezone) {
return s.call(this, t, e);
}
let n = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"));
return s.call(n, t, e).tz(this.$x.$timezone, !0);
}, o.tz = function (t, e, n) {
let i = n && e;
let a = n || e || r;
let f = u(+o(), a);
if (typeof t != "string") {
return o(t).tz(a);
}
let s = function (t, e, n) {
let i = t - 60 * e * 1e3;
let o = u(i, n);
if (e === o) {
return [ i, e ];
}
let r = u(i -= 60 * (o - e) * 1e3, n);
return o === r ? [ i, o ] : [ t - 60 * Math.min(o, r) * 1e3, Math.max(o, r) ];
}(o.utc(t, i).valueOf(), f, a);
let m = s[0];
let c = s[1];
let d = o(m).utcOffset(c);
return d.$x.$timezone = a, d;
}, o.tz.guess = function () {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}, o.tz.setDefault = function (t) {
r = t;
};
};
}));

View File

@@ -64,7 +64,7 @@ class Discord extends NotificationProvider {
}, },
{ {
name: "Error", name: "Error",
value: heartbeatJSON["msg"], value: heartbeatJSON["msg"] == null ? "N/A" : heartbeatJSON["msg"],
}, },
], ],
}], }],

View File

@@ -8,7 +8,7 @@ console.log("Welcome to Uptime Kuma");
// As the log function need to use dayjs, it should be very top // As the log function need to use dayjs, it should be very top
const dayjs = require("dayjs"); const dayjs = require("dayjs");
dayjs.extend(require("dayjs/plugin/utc")); dayjs.extend(require("dayjs/plugin/utc"));
dayjs.extend(require("dayjs/plugin/timezone")); dayjs.extend(require("./modules/dayjs/plugin/timezone"));
dayjs.extend(require("dayjs/plugin/customParseFormat")); dayjs.extend(require("dayjs/plugin/customParseFormat"));
// Check Node.js Version // Check Node.js Version

View File

@@ -1,17 +1,19 @@
const { log } = require("../../src/util"); const { log } = require("../../src/util");
const { Settings } = require("../settings"); const { Settings } = require("../settings");
const { sendInfo } = require("../client"); const { sendInfo } = require("../client");
const { checkLogin } = require("../util-server");
module.exports.generalSocketHandler = (socket, server) => { module.exports.generalSocketHandler = (socket, server) => {
socket.on("initServerTimezone", async (timezone) => { socket.on("initServerTimezone", async (timezone) => {
try { try {
checkLogin(socket);
log.debug("generalSocketHandler", "Timezone: " + timezone); log.debug("generalSocketHandler", "Timezone: " + timezone);
await Settings.set("initServerTimezone", true); await Settings.set("initServerTimezone", true);
await server.setTimezone(timezone); await server.setTimezone(timezone);
await sendInfo(socket); await sendInfo(socket);
} catch (e) { } catch (e) {
log.warn("initServerTimezone", e.message);
} }
}); });

View File

@@ -650,4 +650,23 @@ export default {
Enable: "Активирай", Enable: "Активирай",
Disable: "Деактивирай", Disable: "Деактивирай",
dnsCacheDescription: "Възможно е да не работи в IPv6 среда - деактивирайте, ако срещнете проблеми.", dnsCacheDescription: "Възможно е да не работи в IPv6 среда - деактивирайте, ако срещнете проблеми.",
"Single Maintenance Window": "Единичен времеви интервал за поддръжка",
"Maintenance Time Window of a Day": "Времеви интервал от деня за поддръжка",
"Effective Date Range": "Интервал от дни на влизане в сила",
"Schedule Maintenance": "Планирай поддръжка",
"Date and Time": "Дата и час",
"DateTime Range": "Изтрий времеви интервал",
Strategy: "Стратегия",
"Free Mobile User Identifier": "Free Mobile потребителски идентификатор",
"Free Mobile API Key": "Free Mobile API ключ",
"Enable TLS": "Активирай TLS",
"Proto Service Name": "Proto име на услугата",
"Proto Method": "Proto метод",
"Proto Content": "Proto съдържание",
Economy: "Икономичен",
Lowcost: "Евтин",
high: "висок",
"General Monitor Type": "Общ тип монитор",
"Passive Monitor Type": "Пасивет тип монитор",
"Specific Monitor Type": "Специфичен тип монитор",
}; };

View File

@@ -582,4 +582,45 @@ export default {
goAlert: "GoAlert", goAlert: "GoAlert",
backupOutdatedWarning: "Zastaralé: V poslední době byla funkčnost aplikace značně rozšířena, nicméně součást pro zálohování nepokrývá všechny možnosti. Z tohoto důvodu není možné vygenerovat úplnou zálohu a zajistit obnovení všech dat.", backupOutdatedWarning: "Zastaralé: V poslední době byla funkčnost aplikace značně rozšířena, nicméně součást pro zálohování nepokrývá všechny možnosti. Z tohoto důvodu není možné vygenerovat úplnou zálohu a zajistit obnovení všech dat.",
backupRecommend: "Prosím, zálohujte si ručně celý svazek nebo datovou složku (./data/).", backupRecommend: "Prosím, zálohujte si ručně celý svazek nebo datovou složku (./data/).",
"Optional": "Volitelný",
squadcast: "Squadcast",
SendKey: "SendKey",
"SMSManager API Docs": "SMSManager API Docs ",
"Gateway Type": "Gateway Typ",
SMSManager: "SMSManager",
"You can divide numbers with": "Čísla můžete dělit pomocí",
"or": "nebo",
recurringInterval: "Interval",
"Recurring": "Recurring",
strategyManual: "Aktivní/Neaktivní Ručně",
warningTimezone: "Používá se časové pásmo serveru",
weekdayShortMon: "Po",
weekdayShortTue: "Út",
weekdayShortWed: "St",
weekdayShortThu: "Čt",
weekdayShortFri: "Pá",
weekdayShortSat: "So",
weekdayShortSun: "Ne",
dayOfWeek: "Den v týdnu",
dayOfMonth: "Den v měsíci",
lastDay: "Poslední den",
lastDay1: "1. poslední den v měsíci",
lastDay2: "2. poslední den v měsíci",
lastDay3: "3. poslední den v měsíci",
lastDay4: "4. poslední den v měsíci",
"No Maintenance": "Žádna údržba",
pauseMaintenanceMsg: "Jsi si jistý, že chceš pozastavit údržbu?",
"maintenanceStatus-under-maintenance": "Údržba",
"maintenanceStatus-inactive": "Neaktivní",
"maintenanceStatus-scheduled": "Naplánováno",
"maintenanceStatus-ended": "Ukončeno",
"maintenanceStatus-unknown": "Neznámý",
"Display Timezone": "Zobrazit časové pásmo",
"Server Timezone": "Časové pásmo serveru",
statusPageMaintenanceEndDate: "Konec",
IconUrl: "Adresa URL ikony",
"Enable DNS Cache": "Povolit DNS Cache",
"Enable": "Povolit",
"Disable": "Zakázat",
dnsCacheDescription: "V některých prostředích IPv6 nemusí fungovat. Pokud narazíte na nějaké problémy, vypněte jej.",
}; };

View File

@@ -181,7 +181,7 @@ export default {
"Degraded Service": "Eingeschränkter Dienst", "Degraded Service": "Eingeschränkter Dienst",
"Add Group": "Gruppe hinzufügen", "Add Group": "Gruppe hinzufügen",
"Add a monitor": "Monitor hinzufügen", "Add a monitor": "Monitor hinzufügen",
"Edit Status Page": "Bearbeite Status-Seite", "Edit Status Page": "Statusseite bearbeiten",
"Go to Dashboard": "Gehe zum Dashboard", "Go to Dashboard": "Gehe zum Dashboard",
"Status Page": "Status-Seite", "Status Page": "Status-Seite",
"Status Pages": "Status-Seiten", "Status Pages": "Status-Seiten",
@@ -583,10 +583,10 @@ export default {
"Pick Affected Monitors...": "Wähle betroffene Monitore...", "Pick Affected Monitors...": "Wähle betroffene Monitore...",
"Start of maintenance": "Beginn der Wartung", "Start of maintenance": "Beginn der Wartung",
"All Status Pages": "Alle Status Seiten", "All Status Pages": "Alle Status Seiten",
"Select status pages...": "Wähle Status Seiten...", "Select status pages...": "Statusseiten auswählen...",
recurringIntervalMessage: "einmal pro Tag ausgeführt | Wird alle {0} Tage ausgführt", recurringIntervalMessage: "Einmal pro Tag ausgeführt | Wird alle {0} Tage ausgführt",
affectedMonitorsDescription: "Wähle alle Monitore die von der Wartung betroffen sind", affectedMonitorsDescription: "Wähle Monitore aus, die von der aktuellen Wartung betroffen sind",
affectedStatusPages: "Zeige diese Nachricht auf ausgewählten Status Seiten", affectedStatusPages: "Diese Wartungsmeldung auf ausgewählten Statusseiten anzeigen",
atLeastOneMonitor: "Wähle mindestens einen Monitor", atLeastOneMonitor: "Wähle mindestens einen Monitor",
deleteMaintenanceMsg: "Möchtest du diese Wartung löschen?", deleteMaintenanceMsg: "Möchtest du diese Wartung löschen?",
"Base URL": "Basis URL", "Base URL": "Basis URL",
@@ -605,8 +605,11 @@ export default {
or: "oder", or: "oder",
recurringInterval: "Intervall", recurringInterval: "Intervall",
Recurring: "Wiederkehrend", Recurring: "Wiederkehrend",
strategyManual: "Active/Inactive Manually", "Single Maintenance Window": "Einzigartiges Wartungsfenster",
warningTimezone: "Es wird die Zeitzone des Servers genutzt", "Maintenance Time Window of a Day": "Zeitfenster für die Wartung",
"Effective Date Range": "Bereich der Wirksamkeitsdaten",
strategyManual: "Aktiv/Inaktiv Manuell",
warningTimezone: "Es wird die Zeitzone des Servers verwendet",
weekdayShortMon: "Mo", weekdayShortMon: "Mo",
weekdayShortTue: "Di", weekdayShortTue: "Di",
weekdayShortWed: "Mi", weekdayShortWed: "Mi",
@@ -622,6 +625,7 @@ export default {
lastDay3: "3. letzter Tag im Monat", lastDay3: "3. letzter Tag im Monat",
lastDay4: "4. letzter Tag im Monat", lastDay4: "4. letzter Tag im Monat",
"No Maintenance": "Keine Wartung", "No Maintenance": "Keine Wartung",
"Schedule Maintenance": "Wartung planen",
pauseMaintenanceMsg: "Möchtest du wirklich pausieren?", pauseMaintenanceMsg: "Möchtest du wirklich pausieren?",
"maintenanceStatus-under-maintenance": "Unter Wartung", "maintenanceStatus-under-maintenance": "Unter Wartung",
"maintenanceStatus-inactive": "Inaktiv", "maintenanceStatus-inactive": "Inaktiv",
@@ -630,5 +634,8 @@ export default {
"maintenanceStatus-unknown": "Unbekannt", "maintenanceStatus-unknown": "Unbekannt",
"Display Timezone": "Zeitzone anzeigen", "Display Timezone": "Zeitzone anzeigen",
"Server Timezone": "Server Zeitzone", "Server Timezone": "Server Zeitzone",
"Date and Time": "Datum und Zeit",
"DateTime Range": "Datums- und Zeitbereich",
Strategy: "Strategie",
statusPageMaintenanceEndDate: "Ende", statusPageMaintenanceEndDate: "Ende",
}; };

View File

@@ -609,16 +609,16 @@ export default {
goAlert: "GoAlert", goAlert: "GoAlert",
backupOutdatedWarning: "Deprecated: Since a lot of features added and this backup feature is a bit unmaintained, it cannot generate or restore a complete backup.", backupOutdatedWarning: "Deprecated: Since a lot of features added and this backup feature is a bit unmaintained, it cannot generate or restore a complete backup.",
backupRecommend: "Please backup the volume or the data folder (./data/) directly instead.", backupRecommend: "Please backup the volume or the data folder (./data/) directly instead.",
"Optional": "Optional", Optional: "Optional",
squadcast: "Squadcast", squadcast: "Squadcast",
SendKey: "SendKey", SendKey: "SendKey",
"SMSManager API Docs": "SMSManager API Docs ", "SMSManager API Docs": "SMSManager API Docs ",
"Gateway Type": "Gateway Type", "Gateway Type": "Gateway Type",
SMSManager: "SMSManager", SMSManager: "SMSManager",
"You can divide numbers with": "You can divide numbers with", "You can divide numbers with": "You can divide numbers with",
"or": "or", or: "or",
recurringInterval: "Interval", recurringInterval: "Interval",
"Recurring": "Recurring", Recurring: "Recurring",
strategyManual: "Active/Inactive Manually", strategyManual: "Active/Inactive Manually",
warningTimezone: "It is using the server's timezone", warningTimezone: "It is using the server's timezone",
weekdayShortMon: "Mon", weekdayShortMon: "Mon",
@@ -647,7 +647,26 @@ export default {
statusPageMaintenanceEndDate: "End", statusPageMaintenanceEndDate: "End",
IconUrl: "Icon URL", IconUrl: "Icon URL",
"Enable DNS Cache": "Enable DNS Cache", "Enable DNS Cache": "Enable DNS Cache",
"Enable": "Enable", Enable: "Enable",
"Disable": "Disable", Disable: "Disable",
dnsCacheDescription: "It may be not working in some IPv6 environments, disable it if you encounter any issues.", dnsCacheDescription: "It may be not working in some IPv6 environments, disable it if you encounter any issues.",
"Single Maintenance Window": "Single Maintenance Window",
"Maintenance Time Window of a Day": "Maintenance Time Window of a Day",
"Effective Date Range": "Effective Date Range",
"Schedule Maintenance": "Schedule Maintenance",
"Date and Time": "Date and Time",
"DateTime Range": "DateTime Range",
Strategy: "Strategy",
"Free Mobile User Identifier": "Free Mobile User Identifier",
"Free Mobile API Key": "Free Mobile API Key",
"Enable TLS": "Enable TLS",
"Proto Service Name": "Proto Service Name",
"Proto Method": "Proto Method",
"Proto Content": "Proto Content",
Economy: "Economy",
Lowcost: "Lowcost",
high: "high",
"General Monitor Type": "General Monitor Type",
"Passive Monitor Type": "Passive Monitor Type",
"Specific Monitor Type": "Specific Monitor Type",
}; };

View File

@@ -186,7 +186,7 @@ export default {
startOrEndWithOnly: "Commence uniquement par {0}", startOrEndWithOnly: "Commence uniquement par {0}",
"No consecutive dashes": "Pas de double tirets", "No consecutive dashes": "Pas de double tirets",
Next: "Continuer", Next: "Continuer",
"Setup Proxy": "Configuer Proxy", "Setup Proxy": "Configurer Proxy",
defaultNotificationName: "Ma notification {notification} numéro ({number})", defaultNotificationName: "Ma notification {notification} numéro ({number})",
here: "ici", here: "ici",
Required: "Requis", Required: "Requis",

View File

@@ -5,7 +5,7 @@ export default {
retriesDescription: "จำนวนครั้งสูงสุดที่จะลองก่อนบริการถูกระบุว่าไม่สามารถใช้งานได้และส่งการแจ้งเตือน", retriesDescription: "จำนวนครั้งสูงสุดที่จะลองก่อนบริการถูกระบุว่าไม่สามารถใช้งานได้และส่งการแจ้งเตือน",
ignoreTLSError: "ไม่สนใจข้อผิดพลาด TLS/SSL สำหรับเว็บไซต์ HTTPS", ignoreTLSError: "ไม่สนใจข้อผิดพลาด TLS/SSL สำหรับเว็บไซต์ HTTPS",
upsideDownModeDescription: "กลับด้านสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้", upsideDownModeDescription: "กลับด้านสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้",
maxRedirectDescription: "จำนวนครั้งสูงสุดที่จะเปลี่ยนเส้นทาง, ตังเป็น 0 เพื่อปิดการเปลี่ยนเส้นทาง", maxRedirectDescription: "จำนวนครั้งสูงสุดที่จะเปลี่ยนเส้นทาง, ตังเป็น 0 เพื่อปิดการเปลี่ยนเส้นทาง",
acceptedStatusCodesDescription: "เลือกรหัสสถานะที่ถือว่าการตอบกลับสำเร็จ", acceptedStatusCodesDescription: "เลือกรหัสสถานะที่ถือว่าการตอบกลับสำเร็จ",
passwordNotMatchMsg: "รหัสผ่านไม่ตรงกัน", passwordNotMatchMsg: "รหัสผ่านไม่ตรงกัน",
notificationDescription: "การแจ้งเตือนต้องกำหนดให้มอนิเตอร์เพื่อให้สามารถใช้งานได้", notificationDescription: "การแจ้งเตือนต้องกำหนดให้มอนิเตอร์เพื่อให้สามารถใช้งานได้",
@@ -16,7 +16,7 @@ export default {
resolverserverDescription: "Cloudflare เป็นเซิร์ฟเวอร์ค้นหาเริ่มต้น, คุณสามารถเปลี่ยนเซิร์ฟเวอร์ได้ตลอดเวลา", resolverserverDescription: "Cloudflare เป็นเซิร์ฟเวอร์ค้นหาเริ่มต้น, คุณสามารถเปลี่ยนเซิร์ฟเวอร์ได้ตลอดเวลา",
rrtypeDescription: "เลือกประเภท DNS Record ที่คุณต้องการจะมอนิเตอร์", rrtypeDescription: "เลือกประเภท DNS Record ที่คุณต้องการจะมอนิเตอร์",
pauseMonitorMsg: "คุณแน่ใจหรือไม่ที่จะหยุดมอนิเตอร์ชั่วคราว?", pauseMonitorMsg: "คุณแน่ใจหรือไม่ที่จะหยุดมอนิเตอร์ชั่วคราว?",
enableDefaultNotificationDescription: "การแจ้งเตือนนี้จะถูกเปิดโดค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", enableDefaultNotificationDescription: "การแจ้งเตือนนี้จะถูกเปิดโดค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้",
clearEventsMsg: "คุณแน่ใจหรือไม่ที่จะลบเหตุการณ์ทั้งหมดสำหรับมอนิเตอร์นี้?", clearEventsMsg: "คุณแน่ใจหรือไม่ที่จะลบเหตุการณ์ทั้งหมดสำหรับมอนิเตอร์นี้?",
clearHeartbeatsMsg: "คุณแน่ใจหรือไม่ที่จะลบประวัติการตรวจสอบทั้งหมดสำหรับมอนิเตอร์นี้?", clearHeartbeatsMsg: "คุณแน่ใจหรือไม่ที่จะลบประวัติการตรวจสอบทั้งหมดสำหรับมอนิเตอร์นี้?",
confirmClearStatisticsMsg: "คุณแน่ใจหรือไม่ที่จะลบสถิติทั้งหมด?", confirmClearStatisticsMsg: "คุณแน่ใจหรือไม่ที่จะลบสถิติทั้งหมด?",
@@ -49,12 +49,12 @@ export default {
Status: "สถานะ", Status: "สถานะ",
DateTime: "วันที่และเวลา", DateTime: "วันที่และเวลา",
Message: "ข้อความ", Message: "ข้อความ",
"No important events": "ไม่มีกิจกรรมที่สำคัญ", "No important events": "ไม่มีเหตการณ์ที่สำคัญ",
Resume: "ดำเนินการต่อ", Resume: "ดำเนินการต่อ",
Edit: "แก้ไข", Edit: "แก้ไข",
Delete: "ลบ", Delete: "ลบ",
Current: "ปัจจุบัน", Current: "ปัจจุบัน",
Uptime: "เวลาที่ใช้งาน", Uptime: "เวลาที่ใช้งานได้",
"Cert Exp.": "วันหมดอายุใบรับรอง", "Cert Exp.": "วันหมดอายุใบรับรอง",
days: "วัน", days: "วัน",
day: "วัน", day: "วัน",
@@ -69,7 +69,7 @@ export default {
URL: "URL", URL: "URL",
Hostname: "ชื่อโฮสต์", Hostname: "ชื่อโฮสต์",
Port: "พอร์ต", Port: "พอร์ต",
"Heartbeat Interval": "ระยะห่างระหว่างการทดสอบ", "Heartbeat Interval": "ระยะเวลาระหว่างการทดสอบ",
Retries: "จำนวนครั้งที่จะลองใหม่", Retries: "จำนวนครั้งที่จะลองใหม่",
"Heartbeat Retry Interval": "ระยะห่างระหว่างการทดสอบใหม่หลังจากไม่สำเร็จ", "Heartbeat Retry Interval": "ระยะห่างระหว่างการทดสอบใหม่หลังจากไม่สำเร็จ",
Advanced: "ขั้นสูง", Advanced: "ขั้นสูง",
@@ -112,7 +112,7 @@ export default {
No: "ไม่", No: "ไม่",
Username: "ชื่อผู้ใช้", Username: "ชื่อผู้ใช้",
Password: "รหัสผ่าน", Password: "รหัสผ่าน",
"Remember me": "คงอยู่ในระบบ", "Remember me": "จดจำฉันไว้",
Login: "เข้าสู่ระบบ", Login: "เข้าสู่ระบบ",
"No Monitors, please": "ไม่มีมอนิเตอร์, กรุณา", "No Monitors, please": "ไม่มีมอนิเตอร์, กรุณา",
"add one": "สร้าง", "add one": "สร้าง",
@@ -120,7 +120,7 @@ export default {
Email: "อีเมล", Email: "อีเมล",
Test: "ทดสอบ", Test: "ทดสอบ",
"Certificate Info": "ข้อมูลใบรับรอง", "Certificate Info": "ข้อมูลใบรับรอง",
"Resolver Server": "เซิร์ฟเวอร์ทีค้นหา", "Resolver Server": "เซิร์ฟเวอร์ทีค้นหา",
"Resource Record Type": "ประเภท DNS Record", "Resource Record Type": "ประเภท DNS Record",
"Last Result": "ผลล่าสุด", "Last Result": "ผลล่าสุด",
"Create your admin account": "สร้างบัญชีผู้ดูแลระบบ", "Create your admin account": "สร้างบัญชีผู้ดูแลระบบ",
@@ -138,8 +138,8 @@ export default {
Events: "เหตุการณ์", Events: "เหตุการณ์",
Heartbeats: "ประวัติการตรวจสอบ", Heartbeats: "ประวัติการตรวจสอบ",
"Auto Get": "ดึงอัตโนมัติ", "Auto Get": "ดึงอัตโนมัติ",
backupDescription: "คุณสามารถสำรองข้อมูลการแจ้งเตือนและมอนิเตอร์ทั้งหมดได้ในไฟล์ JSON", backupDescription: "คุณสามารถสำรองข้อมูลการแจ้งเตือนและมอนิเตอร์ทั้งหมดไว้ได้ในไฟล์ JSON",
backupDescription2: "หมายเหตุ : ประวัติและข้อมูลกิจกรรมจะไม่ถูกสำรอง", backupDescription2: "หมายเหตุ : ประวัติและข้อมูลเหตการณ์จะไม่ถูกสำรอง",
backupDescription3: "ข้อมูลที่ละเอียดอ่อนเช่นกุญแจการแจ้งเตือนจะรวมอยู่ในไฟล์ข้อมูลสำรอง, โปรดเก็บข้อมูลสำรองอย่างปลอดภัย", backupDescription3: "ข้อมูลที่ละเอียดอ่อนเช่นกุญแจการแจ้งเตือนจะรวมอยู่ในไฟล์ข้อมูลสำรอง, โปรดเก็บข้อมูลสำรองอย่างปลอดภัย",
alertNoFile: "กรุณาเลือกไฟล์ที่จะใช้งาน", alertNoFile: "กรุณาเลือกไฟล์ที่จะใช้งาน",
alertWrongFileType: "กรุณาเลือกไฟล์ที่เป็น JSON", alertWrongFileType: "กรุณาเลือกไฟล์ที่เป็น JSON",
@@ -153,7 +153,7 @@ export default {
"Enable 2FA": "เปิดใช้งาน 2FA", "Enable 2FA": "เปิดใช้งาน 2FA",
"Disable 2FA": "ปิดใช้งาน 2FA", "Disable 2FA": "ปิดใช้งาน 2FA",
"2FA Settings": "ตั้งค่า 2FA", "2FA Settings": "ตั้งค่า 2FA",
"Two Factor Authentication": "การตรวจสอบสิทธิ์สองปัจจัย", "Two Factor Authentication": "การยืนยันตัวตนแบบสองขั้นตอน",
Active: "ใช้งาน", Active: "ใช้งาน",
Inactive: "ไม่ใช้งาน", Inactive: "ไม่ใช้งาน",
Token: "กุญแจ", Token: "กุญแจ",
@@ -189,7 +189,7 @@ export default {
"Status Pages": "หน้าสถานะ", "Status Pages": "หน้าสถานะ",
defaultNotificationName: "การแจ้งเตือน {notification} ของฉัน ({number})", defaultNotificationName: "การแจ้งเตือน {notification} ของฉัน ({number})",
here: "ที่นี่", here: "ที่นี่",
Required: "ต้องการ", Required: "จำเป็น",
telegram: "Telegram", telegram: "Telegram",
"Bot Token": "กุญแจของบอท", "Bot Token": "กุญแจของบอท",
wayToGetTelegramToken: "คุณสามารถรับกุญแจได้จาก {0}.", wayToGetTelegramToken: "คุณสามารถรับกุญแจได้จาก {0}.",
@@ -202,7 +202,7 @@ export default {
"Post URL": "URL โพสต์", "Post URL": "URL โพสต์",
"Content Type": "ประเภทเนื้อหา", "Content Type": "ประเภทเนื้อหา",
webhookJsonDesc: "{0} ดีสำหรับเซิร์ฟเวอร์ HTTP สมัยใหม่เช่น Express.js", webhookJsonDesc: "{0} ดีสำหรับเซิร์ฟเวอร์ HTTP สมัยใหม่เช่น Express.js",
webhookFormDataDesc: "{multipart} ดีสำหรับ PHP, JSON จะต้องถูกประมวลผลด้วย {decodeFunction}", webhookFormDataDesc: "{multipart} ดีสำหรับ PHP, ข้อมูล JSON จะต้องถูกประมวลผลด้วย {decodeFunction}",
smtp: "Email (SMTP)", smtp: "Email (SMTP)",
secureOptionNone: "None / STARTTLS (25, 587)", secureOptionNone: "None / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)", secureOptionTLS: "TLS (465)",
@@ -224,7 +224,7 @@ export default {
signal: "Signal", signal: "Signal",
Number: "หมายเลข", Number: "หมายเลข",
Recipients: "ผู้รับ", Recipients: "ผู้รับ",
needSignalAPI: "คุณต้องมี Signal Client ที่มี Rest APIl", needSignalAPI: "คุณต้องมี Signal Client ที่มี Rest API",
wayToCheckSignalURL: "คุณสามารถตรวจสอบ URL นี้เพื่อดูวิธีตั้งค่า :", wayToCheckSignalURL: "คุณสามารถตรวจสอบ URL นี้เพื่อดูวิธีตั้งค่า :",
signalImportant: "สำคัญ: คุณไม่สามารถผสมกลุ่มและตัวเลขในผู้รับได้!", signalImportant: "สำคัญ: คุณไม่สามารถผสมกลุ่มและตัวเลขในผู้รับได้!",
gotify: "Gotify", gotify: "Gotify",
@@ -236,7 +236,7 @@ export default {
"Channel Name": "ชื่อห้อง", "Channel Name": "ชื่อห้อง",
"Uptime Kuma URL": "Uptime Kuma URL", "Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "ข้อมูลเพิ่มเติมสำหรับ Webhooks : {0}", aboutWebhooks: "ข้อมูลเพิ่มเติมสำหรับ Webhooks : {0}",
aboutChannelName: "ใส่ชื่อห้องน {0} ในช่องชื่อห้องถ้าต้องการที่จะข้าม Webhook, เช่น: #ช่องอื่นๆ", aboutChannelName: "ใส่ชื่อห้องน {0} ในช่องชื่อห้องถ้าต้องการที่จะข้าม Webhook, เช่น: #ช่องอื่นๆ",
aboutKumaURL: "ถ้าคุณไม่ใส่ข้อมูลในช่อง Uptime Kuma URL ค่าเริ่มต้นจะเป็นจะเป็น Uptime Kuma Github", aboutKumaURL: "ถ้าคุณไม่ใส่ข้อมูลในช่อง Uptime Kuma URL ค่าเริ่มต้นจะเป็นจะเป็น Uptime Kuma Github",
emojiCheatSheet: "ตาราง Emoji : {0}", emojiCheatSheet: "ตาราง Emoji : {0}",
"rocket.chat": "Rocket.Chat", "rocket.chat": "Rocket.Chat",
@@ -248,7 +248,7 @@ export default {
clicksendsms: "ClickSend SMS", clicksendsms: "ClickSend SMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (รองรับการแจ้งเตือนมากกว่า 50 บริการ)", apprise: "Apprise (รองรับการแจ้งเตือนมากกว่า 50 บริการ)",
GoogleChat: "Google Chat (Google Workspace only)", GoogleChat: "Google Chat (สำหรับ Google Workspace เท่านั้น)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",
line: "Line Messenger", line: "Line Messenger",
mattermost: "Mattermost", mattermost: "Mattermost",
@@ -257,8 +257,8 @@ export default {
"Message Title": "หัวข้อข้อความ", "Message Title": "หัวข้อข้อความ",
"Notification Sound": "เสียงแจ้งเตือน", "Notification Sound": "เสียงแจ้งเตือน",
"More info on:": "ข้อมูลเพิ่มเติม : {0}", "More info on:": "ข้อมูลเพิ่มเติม : {0}",
pushoverDesc1: "ลำดับความสำคญฉุกเฉิน (2) มีการหมดเวลาเริ่มต้น 30 วินาทีระหว่างลองใหม่และจะหมดอายุหลังจาก 1 ชั่วโมง", pushoverDesc1: "ลำดับความสำคญฉุกเฉิน (2) มีการหมดเวลาเริ่มต้น 30 วินาทีระหว่างการลองใหม่และจะหมดอายุหลังจาก 1 ชั่วโมง",
pushoverDesc2: "ถ้าคุณต้องการจะส่งการแจ้งเตือนไปยังอุปกรณ์อื่น ๆ สามารถกำหนดได้ที่ช่องอุปกรณ์", pushoverDesc2: "ถ้าคุณต้องการจะส่งการแจ้งเตือนไปยังอุปกรณ์อื่นๆ สามารถกำหนดได้ที่ช่องอุปกรณ์",
"SMS Type": "ประเภท SMS", "SMS Type": "ประเภท SMS",
octopushTypePremium: "พรีเมี่ยม (เร็ว - แนะนำสำหรับการแจ้งเตือน)", octopushTypePremium: "พรีเมี่ยม (เร็ว - แนะนำสำหรับการแจ้งเตือน)",
octopushTypeLowCost: "ต้นทุนต่ำ (ช้า - บางครั้งจะถูกบล็อกโดยผู้ให้บริการ)", octopushTypeLowCost: "ต้นทุนต่ำ (ช้า - บางครั้งจะถูกบล็อกโดยผู้ให้บริการ)",
@@ -274,8 +274,8 @@ export default {
"Read more:": "อ่านเพิ่มเติม : {0}", "Read more:": "อ่านเพิ่มเติม : {0}",
"Status:": "สถานะ : {0}", "Status:": "สถานะ : {0}",
"Read more": "อ่านเพิ่มเติม", "Read more": "อ่านเพิ่มเติม",
appriseInstalled: "Apprise ถูกติดตังแล้ว", appriseInstalled: "Apprise ถูกติดตังแล้ว",
appriseNotInstalled: "Apprise ยังไม่ถูกติดตัง {0}", appriseNotInstalled: "Apprise ยังไม่ถูกติดตัง {0}",
"Access Token": "กุญแจการเข้าถึง", "Access Token": "กุญแจการเข้าถึง",
"Channel access token": "กุญแจการเข้าถึงของช่อง", "Channel access token": "กุญแจการเข้าถึงของช่อง",
"Line Developers Console": "Line Developers Console", "Line Developers Console": "Line Developers Console",
@@ -285,11 +285,11 @@ export default {
"Messaging API": "Messaging API", "Messaging API": "Messaging API",
wayToGetLineChannelToken: "ขั้นแรกให้เข้า {0} สร้างผู้ให้บริการและช่องทาง (Messaging API) จากนั้นคุณจะได้รับกุญแจการเข้าถึงช่องและไอดีผู้ใช้จากรายการเมนูที่กล่าวถึงข้างต้น", wayToGetLineChannelToken: "ขั้นแรกให้เข้า {0} สร้างผู้ให้บริการและช่องทาง (Messaging API) จากนั้นคุณจะได้รับกุญแจการเข้าถึงช่องและไอดีผู้ใช้จากรายการเมนูที่กล่าวถึงข้างต้น",
"Icon URL": "Icon URL", "Icon URL": "Icon URL",
aboutIconURL: "คุณสามารถระบุลิงก์ไปยังรูปภาพใน \"URL ไอคอน\" เพื่อแทนที่รูปภาพโปรไฟล์เริ่มต้น จะไม่ถูกใช้หากมีการตั้งค่า Icon Emoji", aboutIconURL: "คุณสามารถระบุลิงก์รูปภาพใน \"URL ไอคอน\" เพื่อแทนที่รูปภาพโปรไฟล์เริ่มต้น จะไม่ถูกใช้หากมีการตั้งค่า Icon Emoji",
aboutMattermostChannelName: "คุณลบล้างช่องเริ่มต้นที่ Webhook โพสต์ได้ด้วยการป้อนชื่อช่องลงในช่อง \"ชื่อช่อง\" ต้องเปิดใช้งานในการตั้งค่า Mattermost Webhook เช่น #ช่องอื่นๆ", aboutMattermostChannelName: "คุณลบช่องเริ่มต้นที่ Webhook โพสต์ได้ด้วยการป้อนชื่อช่องลงในช่อง \"ชื่อช่อง\" ต้องเปิดใช้งานในการตั้งค่า Mattermost Webhook เช่น #ช่องอื่นๆ",
matrix: "Matrix", matrix: "Matrix",
promosmsTypeEco: "SMS ECO - ราคาถูก แต่ช้าและมักจะโอเวอร์โหลด จำกัดเฉพาะผู้รับโปแลนด์", promosmsTypeEco: "SMS ECO - ราคาถูก แต่ช้าและมักจะโอเวอร์โหลด จำกัดเฉพาะผู้รับในโปแลนด์",
promosmsTypeFlash: "SMS FLASH - ข้อความจะแสดงบนอุปกรณ์ของผู้รับโดยอัตโนมัติ จำกัดเฉพาะผู้รับโปแลนด์", promosmsTypeFlash: "SMS FLASH - ข้อความจะแสดงบนอุปกรณ์ของผู้รับโดยอัตโนมัติ จำกัดเฉพาะผู้รับในโปแลนด์",
promosmsTypeFull: "SMS FULL - SMS ระดับพรีเมียม คุณสามารถใช้ชื่อผู้ส่งของคุณได้ (คุณต้องลงทะเบียนชื่อก่อน) เชื่อถือได้สำหรับการแจ้งเตือน", promosmsTypeFull: "SMS FULL - SMS ระดับพรีเมียม คุณสามารถใช้ชื่อผู้ส่งของคุณได้ (คุณต้องลงทะเบียนชื่อก่อน) เชื่อถือได้สำหรับการแจ้งเตือน",
promosmsTypeSpeed: "SMS SPEED - ลำดับความสำคัญสูงสุดในระบบ รวดเร็วและเชื่อถือได้ แต่มีค่าใช้จ่ายสูง (ประมาณสองเท่าของราคาเต็ม SMS)", promosmsTypeSpeed: "SMS SPEED - ลำดับความสำคัญสูงสุดในระบบ รวดเร็วและเชื่อถือได้ แต่มีค่าใช้จ่ายสูง (ประมาณสองเท่าของราคาเต็ม SMS)",
promosmsPhoneNumber: "หมายเลขโทรศัพท์ (สำหรับผู้รับโปแลนด์ คุณสามารถข้ามรหัสพื้นที่ได้)", promosmsPhoneNumber: "หมายเลขโทรศัพท์ (สำหรับผู้รับโปแลนด์ คุณสามารถข้ามรหัสพื้นที่ได้)",
@@ -298,7 +298,7 @@ export default {
matrixHomeserverURL: "URL ของโฮมเซิร์ฟเวอร์ (พร้อม http(s):// และพอร์ตเสริม)", matrixHomeserverURL: "URL ของโฮมเซิร์ฟเวอร์ (พร้อม http(s):// และพอร์ตเสริม)",
"Internal Room Id": "รหัสห้องภายใน", "Internal Room Id": "รหัสห้องภายใน",
matrixDesc1: "คุณค้นหารหัสห้องภายในได้โดยดูในส่วนขั้นสูงของการตั้งค่าห้องในไคลเอ็นต์ Matrix มันควรจะมีลักษณะเช่น !PMdRCpsIfLwsfjIye6:kiznick.server.", matrixDesc1: "คุณค้นหารหัสห้องภายในได้โดยดูในส่วนขั้นสูงของการตั้งค่าห้องในไคลเอ็นต์ Matrix มันควรจะมีลักษณะเช่น !PMdRCpsIfLwsfjIye6:kiznick.server.",
matrixDesc2: "ขอแนะนำเป็นอย่างยิ่งให้คุณสร้างผู้ใช้ใหม่และอย่าใช้โทเค็นการเข้าถึงของผู้ใช้ Matrix ของคุณเอง เนื่องจากจะทำให้สามารถเข้าถึงบัญชีของคุณและห้องทั้งหมดที่คุณเข้าร่วมได้อย่างเต็มที่ ให้สร้างผู้ใช้ใหม่และเชิญเฉพาะห้องที่คุณต้องการรับการแจ้งเตือนแทน คุณสามารถรับโทเค็นเพื่อการเข้าถึงได้โดยเรียกใช้ {0}", matrixDesc2: "ขอแนะนำเป็นอย่างยิ่งให้คุณสร้างผู้ใช้ใหม่และอย่าใช้โทเค็นการเข้าถึงของผู้ใช้ Matrix ของคุณเอง เนื่องจากจะทำให้สามารถเข้าถึงบัญชีของคุณและห้องทั้งหมดที่คุณเข้าร่วม ให้สร้างผู้ใช้ใหม่และเชิญเฉพาะห้องที่คุณต้องการรับการแจ้งเตือนแทน คุณสามารถรับโทเค็นเพื่อการเข้าถึงได้โดยเรียกใช้ {0}",
Method: "วิธี", Method: "วิธี",
Body: "เนื้อหา", Body: "เนื้อหา",
Headers: "ส่วนหัว", Headers: "ส่วนหัว",
@@ -310,12 +310,12 @@ export default {
PasswordsDoNotMatch: "รหัสผ่านไม่ตรงกัน", PasswordsDoNotMatch: "รหัสผ่านไม่ตรงกัน",
records: "บันทึก", records: "บันทึก",
"One record": "หนึ่งบันทึก", "One record": "หนึ่งบันทึก",
steamApiKeyDescription: "สำหรับการมอนิเตอร์ Steam Game Server คุณต้องมี Steam Web-API key, คุณสามารถสมัครได้จากที่นี่ : ", steamApiKeyDescription: "สำหรับการมอนิเตอร์ Steam Game Server คุณต้องมี Steam Web-API key, คุณสามารถสมัครได้จากที่นี่ : ",
"Current User": "ผู้ใช้ปัจจุบัน", "Current User": "ผู้ใช้ปัจจุบัน",
topic: "หัวข้อ", topic: "หัวข้อ",
topicExplanation: "MQTT หัวข้อที่จะมอนิเตอร์", topicExplanation: "หัวข้อ MQTT ที่จะมอนิเตอร์",
successMessage: "ข้อความที่จะถือว่าประสบความสำเร็จ", successMessage: "ข้อความที่จะถือว่าประสบความสำเร็จ",
successMessageExplanation: "MQTT ข้อความที่จะถือว่าประสบความสำเร็จ", successMessageExplanation: "ข้อความ MQTT ที่จะถือว่าประสบความสำเร็จ",
recent: "ล่าสุด", recent: "ล่าสุด",
Done: "สำเร็จ", Done: "สำเร็จ",
Info: "ข้อมูล", Info: "ข้อมูล",
@@ -354,7 +354,7 @@ export default {
Discard: "ทิ้ง", Discard: "ทิ้ง",
Cancel: "ยกเลิก", Cancel: "ยกเลิก",
"Powered by": "ขับเคลื่อนโดย", "Powered by": "ขับเคลื่อนโดย",
shrinkDatabaseDescription: "ทริกเกอร์ฐานข้อมูล VACUUM สำหรับ SQLite หากฐานข้อมูลของคุณถูกสร้างขึ้นหลังจาก 1.10.0 แสดงว่า AUTO_VACUUM เปิดใช้งานอยู่แล้วและไม่จำเป็นต้องดำเนินการนี้", shrinkDatabaseDescription: "ทริกเกอร์ฐานข้อมูล VACUUM สำหรับ SQLite หากฐานข้อมูลของคุณถูกสร้างขึ้นหลังจากเวอร์ชั่น 1.10.0 แสดงว่า AUTO_VACUUM เปิดใช้งานอยู่แล้วและไม่จำเป็นต้องดำเนินการนี้",
serwersms: "SerwerSMS.pl", serwersms: "SerwerSMS.pl",
serwersmsAPIUser: "API Username (incl. webapi_ prefix)", serwersmsAPIUser: "API Username (incl. webapi_ prefix)",
serwersmsAPIPassword: "API Password", serwersmsAPIPassword: "API Password",
@@ -364,14 +364,14 @@ export default {
Customize: "ปรับแต่ง", Customize: "ปรับแต่ง",
"Custom Footer": "ส่วนท้ายที่กำหนดเอง", "Custom Footer": "ส่วนท้ายที่กำหนดเอง",
"Custom CSS": "CSS ที่กำหนดเอง", "Custom CSS": "CSS ที่กำหนดเอง",
smtpDkimSettings: "ตั้งค่า DKIM", smtpDkimSettings: "การตั้งค่า DKIM",
smtpDkimDesc: "โปรดดู Nodemailer DKIM {0} สำหรับการใช้งาน", smtpDkimDesc: "โปรดดู Nodemailer DKIM {0} สำหรับการใช้งาน",
documentation: "อการ", documentation: "คู่มือการใช้งาน",
smtpDkimDomain: "ชื่อโดเมน", smtpDkimDomain: "ชื่อโดเมน",
smtpDkimKeySelector: "Key Selector", smtpDkimKeySelector: "Key Selector",
smtpDkimPrivateKey: "Private Key", smtpDkimPrivateKey: "Private Key",
smtpDkimHashAlgo: "อัลกอริทึมแฮช (ไม่บังคับ)", smtpDkimHashAlgo: "อัลกอริทึมแฮช (ไม่บังคับ)",
smtpDkimheaderFieldNames: "คีย์ส่วนหัวเพื่อลงชื่อ (ไม่บังคับ)", smtpDkimheaderFieldNames: "คีย์ส่วนหัวสำหรับลงชื่อ (ไม่บังคับ)",
smtpDkimskipFields: "Header Keys ไม่ต้องเซ็น (ไม่บังคับ)", smtpDkimskipFields: "Header Keys ไม่ต้องเซ็น (ไม่บังคับ)",
gorush: "Gorush", gorush: "Gorush",
alerta: "Alerta", alerta: "Alerta",
@@ -383,11 +383,11 @@ export default {
deleteStatusPageMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้", deleteStatusPageMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้",
Proxies: "พร็อกซี", Proxies: "พร็อกซี",
default: "ค่าเริ่มต้น", default: "ค่าเริ่มต้น",
enabled: "เปิดใช้งาน", enabled: "เปิดใช้งานแล้ว",
setAsDefault: "ตังเป็นค่าเริ่มต้น", setAsDefault: "ตังเป็นค่าเริ่มต้น",
deleteProxyMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบพร็อกซีสำหรับมอนิเตอร์ทั้งหมด?", deleteProxyMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบพร็อกซีสำหรับมอนิเตอร์ทั้งหมด?",
proxyDescription: "พร็อกซีจะต้องตั้งค่าให้มอนิเตอร์เพื่อให้ใช้งานได้", proxyDescription: "ต้องตั้งค่ามอนิเตอร์ให้ใช้พร็อกซีเพื่อให้ใช้งานได้",
enableProxyDescription: "พร็อกซีนี้จะไม่ส่งผลต่อมอนิเตอร์จนกว่าจะเปิดใช้งาน คุณสามารถควบคุมการปิดใช้งานพร็อกซีชั่วคราวจากมอนิเตอร์ทั้งหมดได้โดยสถานะการเปิดใช้งาน", enableProxyDescription: "พร็อกซีนี้จะไม่ส่งผลต่อมอนิเตอร์จนกว่าจะเปิดใช้งาน คุณสามารถควบคุมการปิดใช้งานพร็อกซีชั่วคราวจากมอนิเตอร์ทั้งหมดได้ที่ส่วนสถานะการเปิดใช้งาน",
setAsDefaultProxyDescription: "พร็อกซีนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", setAsDefaultProxyDescription: "พร็อกซีนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้",
"Certificate Chain": "ห่วงโซ่ใบรับรอง", "Certificate Chain": "ห่วงโซ่ใบรับรอง",
Valid: "ถูกต้อง", Valid: "ถูกต้อง",
@@ -413,7 +413,7 @@ export default {
"WeCom Bot Key": "WeCom Bot Key", "WeCom Bot Key": "WeCom Bot Key",
"Setup Proxy": "ติดตั้งพร็อกซี่", "Setup Proxy": "ติดตั้งพร็อกซี่",
"Proxy Protocol": "โปรโตคอลพร็อกซี่", "Proxy Protocol": "โปรโตคอลพร็อกซี่",
"Proxy Server": "พร็อกซีเซิร์ฟ", "Proxy Server": "เซิร์ฟเวอร์พร็อกซี",
"Proxy server has authentication": "พร็อกซีเซิร์ฟเวอร์มีการตรวจสอบสิทธิ์", "Proxy server has authentication": "พร็อกซีเซิร์ฟเวอร์มีการตรวจสอบสิทธิ์",
User: "ผู้ใช้", User: "ผู้ใช้",
Installed: "ติดตั้งแล้ว", Installed: "ติดตั้งแล้ว",
@@ -430,29 +430,29 @@ export default {
startOrEndWithOnly: "เริ่มหรือจบด้วย {0} เท่านั้น", startOrEndWithOnly: "เริ่มหรือจบด้วย {0} เท่านั้น",
"No consecutive dashes": "ไม่มีขีดกลางติดต่อกัน", "No consecutive dashes": "ไม่มีขีดกลางติดต่อกัน",
Next: "ต่อไป", Next: "ต่อไป",
"The slug is already taken. Please choose another slug.": "ชื่อนี้ถูกใช้งานไปแล้ว กรุณาใช้ชื่ออื่น", "The slug is already taken. Please choose another slug.": "ชื่อนี้ถูกใช้งานแล้ว กรุณาใช้ชื่ออื่น",
"No Proxy": "ไม่มีพร็อกซี่", "No Proxy": "ไม่มีพร็อกซี่",
"HTTP Basic Auth": "HTTP Basic Auth", "HTTP Basic Auth": "HTTP Basic Auth",
"New Status Page": "หน้าสถานะใหม่", "New Status Page": "หน้าสถานะใหม่",
"Page Not Found": "ไม่พบหน้านี้", "Page Not Found": "ไม่พบหน้านี้",
"Reverse Proxy": "พร็อกซีย้อนกลับ", "Reverse Proxy": "พร็อกซีย้อนกลับ",
Backup: "สำรอง", Backup: "สำรองข้อมูล",
About: "เกี่ยวกับ", About: "เกี่ยวกับ",
wayToGetCloudflaredURL: "(ดาวโหลด cloudflared จาก {0})", wayToGetCloudflaredURL: "(ดาวโหลด cloudflared จาก {0})",
cloudflareWebsite: "เว็บไซต์ Cloudflare", cloudflareWebsite: "เว็บไซต์ Cloudflare",
"Message:": "ข้อความ :", "Message:": "ข้อความ :",
"Don't know how to get the token? Please read the guide:": "ไม่รู้วิธีการรับกุญแจ?, กรุณาอ่านคู่มือ", "Don't know how to get the token? Please read the guide:": "ไม่รู้วิธีการรับกุญแจ?, กรุณาอ่านคู่มือ",
"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.": "การเชื่อมต่อปัจุบันอาจขาดหายหากคุณกำลังเชื่อมต่อ Cloudflare Tunnel คุณแน่ใจหรือไม่ที่จะหยุด, พิมรหัสผ่านของคุณเพื่อยืนยัน", "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.": "การเชื่อมต่อปัจุบันอาจขาดหายหากคุณกำลังเชื่อมต่อ Cloudflare Tunnel คุณแน่ใจหรือไม่ที่จะหยุด, พิมรหัสผ่านของคุณเพื่อยืนยัน",
"Other Software": "ซอฟต์แวร์อื่น ๆ ", "Other Software": "ซอฟต์แวร์อื่นๆ ",
"For example: nginx, Apache and Traefik.": "เช่น: nginx, Apache และ Traefik", "For example: nginx, Apache and Traefik.": "เช่น: nginx, Apache และ Traefik",
"Please read": "กรุณาอ่าน", "Please read": "กรุณาอ่าน",
"Subject:": "เรื่อง :", "Subject:": "เรื่อง :",
"Valid To:": "ถูกต้องถึง :", "Valid To:": "ใช้ได้ถึง :",
"Days Remaining:": "จำนวนวันที่เหลือ :", "Days Remaining:": "จำนวนวันที่เหลือ :",
"Issuer:": "ผู้ออก :", "Issuer:": "ผู้ออก :",
"Fingerprint:": "ลายนิ้วมือ :", "Fingerprint:": "ลายนิ้วมือ :",
"No status pages": "ไม่มีหน้าสถานะ", "No status pages": "ไม่มีหน้าสถานะ",
"Domain Name Expiry Notification": "แจ้งเตือนการหมดอายุโดเมน", "Domain Name Expiry Notification": "แจ้งเตือนการหมดอายุของโดเมน",
Proxy: "Proxy", Proxy: "Proxy",
"Date Created": "วันที่สร้าง", "Date Created": "วันที่สร้าง",
onebotHttpAddress: "ที่อยู่ HTTP OneBot ", onebotHttpAddress: "ที่อยู่ HTTP OneBot ",
@@ -466,8 +466,8 @@ export default {
"Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"", "Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"",
"Domain Names": "Domain Names", "Domain Names": "Domain Names",
signedInDisp: "เข้าใช้งานในฐานะ {0}", signedInDisp: "เข้าใช้งานในฐานะ {0}",
signedInDispDisabled: "ปิดการตรวจสอบสิทธิ์", signedInDispDisabled: "ปิดการยืนยันตัวตน",
"Certificate Expiry Notification": "แจ้งเตือนการรับรองหมดอายุ", "Certificate Expiry Notification": "แจ้งเตือนใบรับรองหมดอายุ",
"API Username": "API Username", "API Username": "API Username",
"API Key": "API Key", "API Key": "API Key",
"Recipient Number": "หมายเลขผู้รับ", "Recipient Number": "หมายเลขผู้รับ",
@@ -476,8 +476,8 @@ export default {
"Octopush API Version": "Octopush API Version", "Octopush API Version": "Octopush API Version",
"Legacy Octopush-DM": "Legacy Octopush-DM", "Legacy Octopush-DM": "Legacy Octopush-DM",
endpoint: "endpoint", endpoint: "endpoint",
octopushAPIKey: "\"API key\" จากข้อมูลรับรอง HTTP API ในแผงควบคุม", octopushAPIKey: "\"API key\" จากข้อมูลยืนยันตัวตน HTTP API ในแผงควบคุม",
octopushLogin: "\"Login\" จากข้อมูลรับรอง HTTP API ในแผงควบคุม", octopushLogin: "\"Login\" จากข้อมูลยืนยันตัวตน HTTP API ในแผงควบคุม",
promosmsLogin: "API Login Name", promosmsLogin: "API Login Name",
promosmsPassword: "API Password", promosmsPassword: "API Password",
"pushoversounds pushover": "Pushover (default)", "pushoversounds pushover": "Pushover (default)",
@@ -507,16 +507,16 @@ export default {
pushyToken: "Device token", pushyToken: "Device token",
"Show update if available": "แสดงการอัปเดตถ้ามี", "Show update if available": "แสดงการอัปเดตถ้ามี",
"Also check beta release": "ตรวจสอบรุ่นเบต้า", "Also check beta release": "ตรวจสอบรุ่นเบต้า",
"Using a Reverse Proxy?": "ใช้ Reverse Proxy?", "Using a Reverse Proxy?": "ใช้ Reverse Proxy อยู่ใช่มั้ย?",
"Check how to config it for WebSocket": "ตรวจสอบวิธีการตั้งค่าสำหรับ WebSocket", "Check how to config it for WebSocket": "ตรวจสอบวิธีการตั้งค่าสำหรับ WebSocket",
"Steam Game Server": "Steam Game Server", "Steam Game Server": "Steam Game Server",
"Most likely causes:": "สาเหตุที่เป็นไปได้มากที่สุด :", "Most likely causes:": "สาเหตุที่เป็นไปได้มากที่สุด :",
"The resource is no longer available.": "ทรัพยากรไม่สามารถใช้งานได้อีกต่อไป", "The resource is no longer available.": "ทรัพยากรไม่สามารถใช้งานได้อีกต่อไป",
"There might be a typing error in the address.": "อาจมีข้อผิดพลาดในการพิมพ์ที่อยู่", "There might be a typing error in the address.": "อาจมีข้อผิดพลาดในการพิมพ์ที่อยู่",
"What you can try:": "สิ่งที่คุณสามารถลอง :", "What you can try:": "สิ่งที่คุณสามารถลองทำ :",
"Retype the address.": "พิมพ์ที่อยู่อีกครั้ง", "Retype the address.": "พิมพ์ที่อยู่อีกครั้ง",
"Go back to the previous page.": "กลับไปที่หน้าก่อนหน้า", "Go back to the previous page.": "กลับไปหน้าที่แล้ว",
"Coming Soon": "เร็ว ๆ นี้", "Coming Soon": "เร็วๆ นี้",
wayToGetClickSendSMSToken: "คุณสามารถรับ API Username และ API Key ได้จาก {0}", wayToGetClickSendSMSToken: "คุณสามารถรับ API Username และ API Key ได้จาก {0}",
wayToGetLineNotifyToken: "คุณสามารถรับ access token ได้จาก {0}", wayToGetLineNotifyToken: "คุณสามารถรับ access token ได้จาก {0}",
resendEveryXTimes: "ส่งซ้ำทุก {0} ครั้ง", resendEveryXTimes: "ส่งซ้ำทุก {0} ครั้ง",
@@ -525,7 +525,7 @@ export default {
"Resend Notification if Down X times consequently": "ส่งการแจ้งเตือนซ้ำถ้าออฟไลน์ครบ X ครั้ง", "Resend Notification if Down X times consequently": "ส่งการแจ้งเตือนซ้ำถ้าออฟไลน์ครบ X ครั้ง",
error: "เกิดข้อผิดพลาด", error: "เกิดข้อผิดพลาด",
critical: "วิกฤต", critical: "วิกฤต",
wayToGetPagerDutyKey: "คุณสามารถรับได้โดยการไปที่ Service -> Service Directory -> (Select a service) -> Integrations -> Add integration, และค้นหา \"Events API V2\", สำหรับข้อมูลเพิ่มเติม {0}", wayToGetPagerDutyKey: "คุณสามารถรับคีย์ได้โดยการไปที่ Service -> Service Directory -> (Select a service) -> Integrations -> Add integration, และค้นหา \"Events API V2\", สำหรับข้อมูลเพิ่มเติม {0}",
"Integration Key": "Integration Key", "Integration Key": "Integration Key",
"Integration URL": "Integration URL", "Integration URL": "Integration URL",
"Auto resolve or acknowledged": "แก้ไขอัตโนมัติหรือยอมรับ", "Auto resolve or acknowledged": "แก้ไขอัตโนมัติหรือยอมรับ",
@@ -539,16 +539,16 @@ export default {
"Trust Proxy": "Trust Proxy", "Trust Proxy": "Trust Proxy",
HomeAssistant: "Home Assistant", HomeAssistant: "Home Assistant",
RadiusSecret: "Radius Secret", RadiusSecret: "Radius Secret",
RadiusSecretDescription: "แบ่งปันข้อมูลลับระหว่างผู้ใช้งานและเซิร์ฟเวอร์", RadiusSecretDescription: "แบ่งปันคีย์ลับระหว่างผู้ใช้งานและเซิร์ฟเวอร์",
RadiusCalledStationId: "Called Station Id", RadiusCalledStationId: "Called Station Id",
RadiusCalledStationIdDescription: "Identifier of the called device", RadiusCalledStationIdDescription: "Identifier of the called device",
RadiusCallingStationId: "Calling Station Id", RadiusCallingStationId: "Calling Station Id",
RadiusCallingStationIdDescription: "Identifier of the calling device", RadiusCallingStationIdDescription: "Identifier of the calling device",
"Connection String": "Connection String", "Connection String": "Connection String",
Query: "Query", Query: "Query",
settingsCertificateExpiry: "วันหมดอายุใบรับรอง TLS", settingsCertificateExpiry: "วันหมดอายุของใบรับรอง TLS",
certificationExpiryDescription: "การตรวจสอบ HTTPS แจ้งเตือนใบอนุญาติ TLS จะหมดอายุใน:", certificationExpiryDescription: "การตรวจสอบ HTTPS จะแจ้งเตือนถ้าใบอนุญาติ TLS จะหมดอายุใน:",
"Setup Docker Host": "Setup Docker Host", "Setup Docker Host": "ติดตั้ง Docker Host",
"Connection Type": "ประเภทการเชื่อมต่อ", "Connection Type": "ประเภทการเชื่อมต่อ",
"Docker Daemon": "Docker Daemon", "Docker Daemon": "Docker Daemon",
deleteDockerHostMsg: "คุณแน่ใจหรือไม่ที่จะลบ Docker host นี้สำหรับการมอนิเตอร์ทั้งหมด?", deleteDockerHostMsg: "คุณแน่ใจหรือไม่ที่จะลบ Docker host นี้สำหรับการมอนิเตอร์ทั้งหมด?",
@@ -565,14 +565,14 @@ export default {
trustProxyDescription: "เชื่อ Header 'X-Forwarded-*' ถ้าคุณต้องการไอพีที่ถูกต้องและ Uptime Kuma อยู่ข้างหลัง Nginx หรือ Apache, คุณควรเปิดใช้งาน", trustProxyDescription: "เชื่อ Header 'X-Forwarded-*' ถ้าคุณต้องการไอพีที่ถูกต้องและ Uptime Kuma อยู่ข้างหลัง Nginx หรือ Apache, คุณควรเปิดใช้งาน",
Examples: "ตัวอย่าง", Examples: "ตัวอย่าง",
"Home Assistant URL": "Home Assistant URL", "Home Assistant URL": "Home Assistant URL",
"Long-Lived Access Token": "Access Token แบบมีอายุ", "Long-Lived Access Token": "Access Token แบบมีอายุนาน",
"Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Access Token แบบมีอายุนานสามารถสร้างได้โดยคลิกชื่อบนโปรไฟล์ (ล่างซ้าย) และเลื่อนไปข้างล่างจากนั้นคลิก \"Create Token\"", "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Access Token แบบมีอายุนานสามารถสร้างได้โดยคลิกชื่อบนโปรไฟล์ (ล่างซ้าย) และเลื่อนไปข้างล่างจากนั้นคลิก \"Create Token\"",
"Notification Service": "บริการแจ้งเตือน", "Notification Service": "บริการแจ้งเตือน",
"default: notify all devices": "ค่าเริ่มต้น: แจ้งเตือนทุกอุปกรณ์", "default: notify all devices": "ค่าเริ่มต้น: แจ้งเตือนทุกอุปกรณ์",
"A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "รายการแจ้งเตือนสามารถหาได้ใน Home Assistant ในเมนู \"Developer Tools > Services\" ค้นหา \"notification\" เพื่อหาชื่ออุปกรณ์หรือชื่อโทรศัพท์", "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "รายการแจ้งเตือนสามารถหาได้ใน Home Assistant ในเมนู \"Developer Tools > Services\" แล้วค้นหา \"notification\" เพื่อหาชื่ออุปกรณ์หรือชื่อโทรศัพท์",
"Automations can optionally be triggered in Home Assistant:": "สามารถเลือกสั่งงานระบบอัตโนมัติได้ใน Home Assistant:", "Automations can optionally be triggered in Home Assistant:": "สามารถเลือกสั่งงานระบบอัตโนมัติได้ใน Home Assistant:",
"Trigger type:": "ชนิดสิ่งกระตุ้น:", "Trigger type:": "ชนิดสิ่งกระตุ้น:",
"Event type:": "ชนิดกิจกรรม:", "Event type:": "ชนิดเหตการณ์:",
"Event data:": "ข้อมูลกิจกรรม:", "Event data:": "ข้อมูลกิจกรรม:",
"Then choose an action, for example switch the scene to where an RGB light is red.": "จากนั้นเลือกการกระทำ, ตัวอย่าง เช่น เปลี่ยนเป็นไฟสีแดง", "Then choose an action, for example switch the scene to where an RGB light is red.": "จากนั้นเลือกการกระทำ, ตัวอย่าง เช่น เปลี่ยนเป็นไฟสีแดง",
"Frontend Version": "เวอร์ชั่น Frontend", "Frontend Version": "เวอร์ชั่น Frontend",

View File

@@ -17,7 +17,7 @@ import lang from "./mixins/lang";
import { router } from "./router"; import { router } from "./router";
import { appName } from "./util.ts"; import { appName } from "./util.ts";
import dayjs from "dayjs"; import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone"; import timezone from "./modules/dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
dayjs.extend(utc); dayjs.extend(utc);

View File

@@ -0,0 +1,25 @@
export let SECONDS_A_MINUTE = 60;
export let SECONDS_A_HOUR = SECONDS_A_MINUTE * 60;
export let SECONDS_A_DAY = SECONDS_A_HOUR * 24;
export let SECONDS_A_WEEK = SECONDS_A_DAY * 7;
export let MILLISECONDS_A_SECOND = 1e3;
export let MILLISECONDS_A_MINUTE = SECONDS_A_MINUTE * MILLISECONDS_A_SECOND;
export let MILLISECONDS_A_HOUR = SECONDS_A_HOUR * MILLISECONDS_A_SECOND;
export let MILLISECONDS_A_DAY = SECONDS_A_DAY * MILLISECONDS_A_SECOND;
export let MILLISECONDS_A_WEEK = SECONDS_A_WEEK * MILLISECONDS_A_SECOND; // English locales
export let MS = "millisecond";
export let S = "second";
export let MIN = "minute";
export let H = "hour";
export let D = "day";
export let W = "week";
export let M = "month";
export let Q = "quarter";
export let Y = "year";
export let DATE = "date";
export let FORMAT_DEFAULT = "YYYY-MM-DDTHH:mm:ssZ";
export let INVALID_DATE_STRING = "Invalid Date"; // regex
export let REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/;
export let REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g;

View File

@@ -0,0 +1,20 @@
import { PluginFunc, ConfigType } from 'dayjs/esm'
declare const plugin: PluginFunc
export = plugin
declare module 'dayjs/esm' {
interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs
offsetName(type?: 'short' | 'long'): string | undefined
}
interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs
(date: ConfigType, format: string, timezone?: string): Dayjs
guess(): string
setDefault(timezone?: string): void
}
const tz: DayjsTimezone
}

View File

@@ -0,0 +1,188 @@
/**
* Copy from node_modules/dayjs/plugin/timezone.js
* Try to fix https://github.com/louislam/uptime-kuma/issues/2318
* Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc
* License: MIT
*/
import { MIN, MS } from "../../constant";
let typeToPos = {
year: 0,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5
}; // Cache time-zone lookups from Intl.DateTimeFormat,
// as it is a *very* slow method.
let dtfCache = {};
let getDateTimeFormat = function getDateTimeFormat(timezone, options) {
if (options === void 0) {
options = {};
}
let timeZoneName = options.timeZoneName || "short";
let key = timezone + "|" + timeZoneName;
let dtf = dtfCache[key];
if (!dtf) {
dtf = new Intl.DateTimeFormat("en-US", {
hour12: false,
timeZone: timezone,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: timeZoneName
});
dtfCache[key] = dtf;
}
return dtf;
};
export default (function (o, c, d) {
let defaultTimezone;
let makeFormatParts = function makeFormatParts(timestamp, timezone, options) {
if (options === void 0) {
options = {};
}
let date = new Date(timestamp);
let dtf = getDateTimeFormat(timezone, options);
return dtf.formatToParts(date);
};
let tzOffset = function tzOffset(timestamp, timezone) {
let formatResult = makeFormatParts(timestamp, timezone);
let filled = [];
for (let i = 0; i < formatResult.length; i += 1) {
let _formatResult$i = formatResult[i];
let type = _formatResult$i.type;
let value = _formatResult$i.value;
let pos = typeToPos[type];
if (pos >= 0) {
filled[pos] = parseInt(value, 10);
}
}
let hour = filled[3]; // Workaround for the same behavior in different node version
// https://github.com/nodejs/node/issues/33027
/* istanbul ignore next */
let fixedHour = hour === 24 ? 0 : hour;
let utcString = filled[0] + "-" + filled[1] + "-" + filled[2] + " " + fixedHour + ":" + filled[4] + ":" + filled[5] + ":000";
let utcTs = d.utc(utcString).valueOf();
let asTS = +timestamp;
let over = asTS % 1000;
asTS -= over;
return (utcTs - asTS) / (60 * 1000);
}; // find the right offset a given local time. The o input is our guess, which determines which
// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)
// https://github.com/moment/luxon/blob/master/src/datetime.js#L76
let fixOffset = function fixOffset(localTS, o0, tz) {
// Our UTC time is just a guess because our offset is just a guess
let utcGuess = localTS - o0 * 60 * 1000; // Test whether the zone matches the offset for this ts
let o2 = tzOffset(utcGuess, tz); // If so, offset didn't change and we're done
if (o0 === o2) {
return [ utcGuess, o0 ];
} // If not, change the ts by the difference in the offset
utcGuess -= (o2 - o0) * 60 * 1000; // If that gives us the local time we want, we're done
let o3 = tzOffset(utcGuess, tz);
if (o2 === o3) {
return [ utcGuess, o2 ];
} // If it's different, we're in a hole time.
// The offset has changed, but the we don't adjust the time
return [ localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3) ];
};
let proto = c.prototype;
proto.tz = function (timezone, keepLocalTime) {
if (timezone === void 0) {
timezone = defaultTimezone;
}
let oldOffset = this.utcOffset();
let date = this.toDate();
let target = date.toLocaleString("en-US", {
timeZone: timezone
}).replace("\u202f", " ");
let diff = Math.round((date - new Date(target)) / 1000 / 60);
let ins = d(target).$set(MS, this.$ms).utcOffset(-Math.round(date.getTimezoneOffset() / 15) * 15 - diff, true);
if (keepLocalTime) {
let newOffset = ins.utcOffset();
ins = ins.add(oldOffset - newOffset, MIN);
}
ins.$x.$timezone = timezone;
return ins;
};
proto.offsetName = function (type) {
// type: short(default) / long
let zone = this.$x.$timezone || d.tz.guess();
let result = makeFormatParts(this.valueOf(), zone, {
timeZoneName: type
}).find(function (m) {
return m.type.toLowerCase() === "timezonename";
});
return result && result.value;
};
let oldStartOf = proto.startOf;
proto.startOf = function (units, startOf) {
if (!this.$x || !this.$x.$timezone) {
return oldStartOf.call(this, units, startOf);
}
let withoutTz = d(this.format("YYYY-MM-DD HH:mm:ss:SSS"));
let startOfWithoutTz = oldStartOf.call(withoutTz, units, startOf);
return startOfWithoutTz.tz(this.$x.$timezone, true);
};
d.tz = function (input, arg1, arg2) {
let parseFormat = arg2 && arg1;
let timezone = arg2 || arg1 || defaultTimezone;
let previousOffset = tzOffset(+d(), timezone);
if (typeof input !== "string") {
// timestamp number || js Date || Day.js
return d(input).tz(timezone);
}
let localTs = d.utc(input, parseFormat).valueOf();
let _fixOffset = fixOffset(localTs, previousOffset, timezone);
let targetTs = _fixOffset[0];
let targetOffset = _fixOffset[1];
let ins = d(targetTs).utcOffset(targetOffset);
ins.$x.$timezone = timezone;
return ins;
};
d.tz.guess = function () {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
};
d.tz.setDefault = function (timezone) {
defaultTimezone = timezone;
};
});

View File

@@ -106,7 +106,13 @@ class Logger {
} }
module = module.toUpperCase(); module = module.toUpperCase();
level = level.toUpperCase(); level = level.toUpperCase();
const now = dayjs.tz(new Date()).format(); let now;
if (dayjs.tz) {
now = dayjs.tz(new Date()).format();
}
else {
now = dayjs().format();
}
const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg; const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg;
if (level === "INFO") { if (level === "INFO") {
console.info(formattedMessage); console.info(formattedMessage);

View File

@@ -122,7 +122,12 @@ class Logger {
module = module.toUpperCase(); module = module.toUpperCase();
level = level.toUpperCase(); level = level.toUpperCase();
const now = dayjs.tz(new Date()).format(); let now;
if (dayjs.tz) {
now = dayjs.tz(new Date()).format();
} else {
now = dayjs().format();
}
const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg; const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg;
if (level === "INFO") { if (level === "INFO") {