From 8c941b1d565b1f3ff61450f91b6e9a6e56d20543 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Tue, 21 Sep 2021 13:02:41 +0800
Subject: [PATCH 01/48] Add i18n for notification form

---
 src/components/notifications/Apprise.vue    | 19 ++---
 src/components/notifications/Discord.vue    | 11 ++-
 src/components/notifications/Gotify.vue     |  6 +-
 src/components/notifications/Line.vue       | 20 ++---
 src/components/notifications/LunaSea.vue    |  4 +-
 src/components/notifications/Mattermost.vue | 30 +++----
 src/components/notifications/Octopush.vue   | 22 ++---
 src/components/notifications/Pushbullet.vue |  8 +-
 src/components/notifications/Pushover.vue   | 24 +++---
 src/components/notifications/Pushy.vue      |  6 +-
 src/components/notifications/RocketChat.vue | 28 +++----
 src/components/notifications/SMTP.vue       | 14 ++--
 src/components/notifications/Signal.vue     | 14 ++--
 src/components/notifications/Slack.vue      | 28 +++----
 src/components/notifications/Teams.vue      |  9 +-
 src/components/notifications/Telegram.vue   | 16 ++--
 src/components/notifications/Webhook.vue    |  9 +-
 src/languages/en.js                         | 93 +++++++++++++++++++++
 18 files changed, 228 insertions(+), 133 deletions(-)

diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue
index de3fe52b3..567b1eae7 100644
--- a/src/components/notifications/Apprise.vue
+++ b/src/components/notifications/Apprise.vue
@@ -1,20 +1,19 @@
 <template>
     <div class="mb-3">
-        <label for="apprise-url" class="form-label">Apprise URL</label>
+        <label for="apprise-url" class="form-label">{{ $t("Apprise URL") }}</label>
         <input id="apprise-url" v-model="$parent.notification.appriseURL" type="text" class="form-control" required>
         <div class="form-text">
-            <p>Example: twilio://AccountSid:AuthToken@FromPhoneNo</p>
-            <p>
-                Read more: <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
-            </p>
+            <p>{{ $t("Example:", ["twilio://AccountSid:AuthToken@FromPhoneNo"]) }}</p>
+            <i18n-t keypath="Read more:" tag="p">
+                <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
+            </i18n-t>
         </div>
     </div>
     <div class="mb-3">
-        <p>
-            Status:
-            <span v-if="appriseInstalled" class="text-primary">Apprise is installed</span>
-            <span v-else class="text-danger">Apprise is not installed. <a href="https://github.com/caronc/apprise" target="_blank">Read more</a></span>
-        </p>
+        <i18n-t keypath="Status:" tag="p">
+            <span v-if="appriseInstalled" class="text-primary">{{ $t("Apprise is installed") }}</span>
+            <span v-else class="text-danger">{{ $t("Apprise is not installed. ") }}<a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a></span>
+        </i18n-t>
     </div>
 </template>
 
diff --git a/src/components/notifications/Discord.vue b/src/components/notifications/Discord.vue
index c45a5262a..99389d29e 100644
--- a/src/components/notifications/Discord.vue
+++ b/src/components/notifications/Discord.vue
@@ -1,20 +1,19 @@
 <template>
     <div class="mb-3">
-        <label for="discord-webhook-url" class="form-label">Discord Webhook URL</label>
+        <label for="discord-webhook-url" class="form-label">{{ $t("Discord Webhook URL") }}</label>
         <input id="discord-webhook-url" v-model="$parent.notification.discordWebhookUrl" type="text" class="form-control" required autocomplete="false">
         <div class="form-text">
-            You can get this by going to Server Settings -> Integrations -> Create Webhook
+            {{ $t("wayToGetDiscordURL") }}
         </div>
     </div>
 
     <div class="mb-3">
-        <label for="discord-username" class="form-label">Bot Display Name</label>
+        <label for="discord-username" class="form-label">{{ $t("Bot Display Name") }}</label>
         <input id="discord-username" v-model="$parent.notification.discordUsername" type="text" class="form-control" autocomplete="false" :placeholder="$root.appName">
     </div>
 
-
     <div class="mb-3">
-        <label for="discord-prefix-message" class="form-label">Prefix Custom Message</label>
-        <input id="discord-prefix-message" v-model="$parent.notification.discordPrefixMessage" type="text" class="form-control" autocomplete="false" placeholder="Hello @everyone is...">
+        <label for="discord-prefix-message" class="form-label">{{ $t("Prefix Custom Message") }}</label>
+        <input id="discord-prefix-message" v-model="$parent.notification.discordPrefixMessage" type="text" class="form-control" autocomplete="false" :placeholder="$t('Hello @everyone is...')">
     </div>
 </template>
diff --git a/src/components/notifications/Gotify.vue b/src/components/notifications/Gotify.vue
index f8039d58a..9e16d4e43 100644
--- a/src/components/notifications/Gotify.vue
+++ b/src/components/notifications/Gotify.vue
@@ -1,17 +1,17 @@
 <template>
     <div class="mb-3">
-        <label for="gotify-application-token" class="form-label">Application Token</label>
+        <label for="gotify-application-token" class="form-label">{{ $t("Application Token") }}</label>
         <HiddenInput id="gotify-application-token" v-model="$parent.notification.gotifyapplicationToken" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
     <div class="mb-3">
-        <label for="gotify-server-url" class="form-label">Server URL</label>
+        <label for="gotify-server-url" class="form-label">{{ $t("Server URL") }}</label>
         <div class="input-group mb-3">
             <input id="gotify-server-url" v-model="$parent.notification.gotifyserverurl" type="text" class="form-control" required>
         </div>
     </div>
 
     <div class="mb-3">
-        <label for="gotify-priority" class="form-label">Priority</label>
+        <label for="gotify-priority" class="form-label">{{ $t("Priority") }}</label>
         <input id="gotify-priority" v-model="$parent.notification.gotifyPriority" type="number" class="form-control" required min="0" max="10" step="1">
     </div>
 </template>
diff --git a/src/components/notifications/Line.vue b/src/components/notifications/Line.vue
index 7d5312c37..f6dbc7489 100644
--- a/src/components/notifications/Line.vue
+++ b/src/components/notifications/Line.vue
@@ -1,21 +1,21 @@
 <template>
     <div class="mb-3">
-        <label for="line-channel-access-token" class="form-label">Channel access token</label>
+        <label for="line-channel-access-token" class="form-label">{{ $t("Channel access token") }}</label>
         <HiddenInput id="line-channel-access-token" v-model="$parent.notification.lineChannelAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
-    <div class="form-text">
-        Line Developers Console - <b>Basic Settings</b>
-    </div>
+    <i18n-t keypath="Line Developers Console - " tag="div" class="form-text">
+        <b>{{ $t("Basic Settings") }}</b>
+    </i18n-t>
     <div class="mb-3" style="margin-top: 12px;">
         <label for="line-user-id" class="form-label">User ID</label>
         <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
     </div>
-    <div class="form-text">
-        Line Developers Console - <b>Messaging API</b>
-    </div>
-    <div class="form-text" style="margin-top: 8px;">
-        First access the <a href="https://developers.line.biz/console/" target="_blank">Line Developers Console</a>, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items.
-    </div>
+    <i18n-t keypath="Line Developers Console - " tag="div" class="form-text">
+        <b>{{ $t("Messaging API") }}</b>
+    </i18n-t>
+    <i18n-t keypath="wayToGetLineChannelToken" tag="div" class="form-text" style="margin-top: 8px;">
+        <a href="https://developers.line.biz/console/" target="_blank">{{ $t("Line Developers Console") }}</a>
+    </i18n-t>
 </template>
 
 <script>
diff --git a/src/components/notifications/LunaSea.vue b/src/components/notifications/LunaSea.vue
index 1511f411c..34a986885 100644
--- a/src/components/notifications/LunaSea.vue
+++ b/src/components/notifications/LunaSea.vue
@@ -1,9 +1,9 @@
 <template>
     <div class="mb-3">
-        <label for="lunasea-device" class="form-label">LunaSea Device ID<span style="color: red;"><sup>*</sup></span></label>
+        <label for="lunasea-device" class="form-label">{{ $t("LunaSea Device ID") }}<span style="color: red;"><sup>*</sup></span></label>
         <input id="lunasea-device" v-model="$parent.notification.lunaseaDevice" type="text" class="form-control" required>
         <div class="form-text">
-            <p><span style="color: red;"><sup>*</sup></span>Required</p>
+            <p><span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}</p>
         </div>
     </div>
 </template>
diff --git a/src/components/notifications/Mattermost.vue b/src/components/notifications/Mattermost.vue
index 3e1a7bdc6..4c6e69189 100644
--- a/src/components/notifications/Mattermost.vue
+++ b/src/components/notifications/Mattermost.vue
@@ -1,32 +1,32 @@
 <template>
     <div class="mb-3">
-        <label for="mattermost-webhook-url" class="form-label">Webhook URL<span style="color:red;"><sup>*</sup></span></label>
+        <label for="mattermost-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color:red;"><sup>*</sup></span></label>
         <input id="mattermost-webhook-url" v-model="$parent.notification.mattermostWebhookUrl" type="text" class="form-control" required>
-        <label for="mattermost-username" class="form-label">Username</label>
+        <label for="mattermost-username" class="form-label">{{ $t("Username") }}</label>
         <input id="mattermost-username" v-model="$parent.notification.mattermostusername" type="text" class="form-control">
-        <label for="mattermost-iconurl" class="form-label">Icon URL</label>
+        <label for="mattermost-iconurl" class="form-label">{{ $t("Icon URL") }}</label>
         <input id="mattermost-iconurl" v-model="$parent.notification.mattermosticonurl" type="text" class="form-control">
-        <label for="mattermost-iconemo" class="form-label">Icon Emoji</label>
+        <label for="mattermost-iconemo" class="form-label">{{ $t("Icon Emoji") }}</label>
         <input id="mattermost-iconemo" v-model="$parent.notification.mattermosticonemo" type="text" class="form-control">
-        <label for="mattermost-channel" class="form-label">Channel Name</label>
+        <label for="mattermost-channel" class="form-label">{{ $t("Channel Name") }}</label>
         <input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control">
         <div class="form-text">
-            <span style="color:red;"><sup>*</sup></span>Required
+            <span style="color:red;"><sup>*</sup></span>{{ $t("Required") }}
+            <i18n-t keypath="aboutWebhooks" tag="p" style="margin-top: 8px;">
+                <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a>
+            </i18n-t>
             <p style="margin-top: 8px;">
-                More info about webhooks on: <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a>
+                {{ $t("aboutMattermostChannelName") }}
             </p>
             <p style="margin-top: 8px;">
-                You can override the default channel that webhook posts to by entering the channel name into "Channel Name" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel
+                {{ $t("aboutKumaURL") }}
             </p>
             <p style="margin-top: 8px;">
-                If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
-            </p>
-            <p style="margin-top: 8px;">
-                You can provide a link to a picture in "Icon URL" to override the default profile picture. Will not be used if Icon Emoji is set.
-            </p>
-            <p style="margin-top: 8px;">
-                Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a> Note: emoji takes preference over Icon URL.
+                {{ $t("aboutIconURL") }}
             </p>
+            <i18n-t keypath="emojiCheatSheet" tag="p" style="margin-top: 8px;">
+                <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
+            </i18n-t>
         </div>
     </div>
 </template>
diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue
index 10fb6df75..b8ea6dfac 100644
--- a/src/components/notifications/Octopush.vue
+++ b/src/components/notifications/Octopush.vue
@@ -6,27 +6,27 @@
         <input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required>
     </div>
     <div class="mb-3">
-        <label for="octopush-type-sms" class="form-label">SMS Type</label>
+        <label for="octopush-type-sms" class="form-label">{{ $t("SMS Type") }}</label>
         <select id="octopush-type-sms" v-model="$parent.notification.octopushSMSType" class="form-select">
-            <option value="sms_premium">Premium (Fast - recommended for alerting)</option>
-            <option value="sms_low_cost">Low Cost (Slow, sometimes blocked by operator)</option>
+            <option value="sms_premium">{{ $t("octopushTypePremium") }}</option>
+            <option value="sms_low_cost">{{ $t("octopushTypeLowCost") }}</option>
         </select>
-        <div class="form-text">
-            Check octopush prices <a href="https://octopush.com/tarifs-sms-international/" target="_blank">https://octopush.com/tarifs-sms-international/</a>.
-        </div>
+        <i18n-t keypath="Check octopush prices" tag="div" class="form-text">
+            <a href="https://octopush.com/tarifs-sms-international/" target="_blank">https://octopush.com/tarifs-sms-international/</a>
+        </i18n-t>
     </div>
     <div class="mb-3">
-        <label for="octopush-phone-number" class="form-label">Phone number (intl format, eg : +33612345678) </label>
+        <label for="octopush-phone-number" class="form-label">{{ $t("octopushPhoneNumber") }}</label>
         <input id="octopush-phone-number" v-model="$parent.notification.octopushPhoneNumber" type="text" class="form-control" required>
     </div>
     <div class="mb-3">
-        <label for="octopush-sender-name" class="form-label">SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)</label>
+        <label for="octopush-sender-name" class="form-label">{{ $t("octopushSMSSender") }}</label>
         <input id="octopush-sender-name" v-model="$parent.notification.octopushSenderName" type="text" minlength="3" maxlength="11" class="form-control">
     </div>
 
-    <p style="margin-top: 8px;">
-        More info on: <a href="https://octopush.com/api-sms-documentation/envoi-de-sms/" target="_blank">https://octopush.com/api-sms-documentation/envoi-de-sms/</a>
-    </p>
+    <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+        <a href="https://octopush.com/api-sms-documentation/envoi-de-sms/" target="_blank">https://octopush.com/api-sms-documentation/envoi-de-sms/</a>
+    </i18n-t>
 </template>
 
 <script>
diff --git a/src/components/notifications/Pushbullet.vue b/src/components/notifications/Pushbullet.vue
index 2c805e0ae..ea848222d 100644
--- a/src/components/notifications/Pushbullet.vue
+++ b/src/components/notifications/Pushbullet.vue
@@ -1,12 +1,12 @@
 <template>
     <div class="mb-3">
-        <label for="pushbullet-access-token" class="form-label">Access Token</label>
+        <label for="pushbullet-access-token" class="form-label">{{ $t("Access Token") }}</label>
         <HiddenInput id="pushbullet-access-token" v-model="$parent.notification.pushbulletAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
 
-    <p style="margin-top: 8px;">
-        More info on: <a href="https://docs.pushbullet.com" target="_blank">https://docs.pushbullet.com</a>
-    </p>
+    <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+        <a href="https://docs.pushbullet.com" target="_blank">https://docs.pushbullet.com</a>
+    </i18n-t>
 </template>
 
 <script>
diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue
index ae836b5cb..7f0a94173 100644
--- a/src/components/notifications/Pushover.vue
+++ b/src/components/notifications/Pushover.vue
@@ -1,14 +1,14 @@
 <template>
     <div class="mb-3">
-        <label for="pushover-user" class="form-label">User Key<span style="color: red;"><sup>*</sup></span></label>
+        <label for="pushover-user" class="form-label">{{ $t("User Key") }}<span style="color: red;"><sup>*</sup></span></label>
         <HiddenInput id="pushover-user" v-model="$parent.notification.pushoveruserkey" :required="true" autocomplete="one-time-code"></HiddenInput>
-        <label for="pushover-app-token" class="form-label">Application Token<span style="color: red;"><sup>*</sup></span></label>
+        <label for="pushover-app-token" class="form-label">{{ $t("Application Token") }}<span style="color: red;"><sup>*</sup></span></label>
         <HiddenInput id="pushover-app-token" v-model="$parent.notification.pushoverapptoken" :required="true" autocomplete="one-time-code"></HiddenInput>
-        <label for="pushover-device" class="form-label">Device</label>
+        <label for="pushover-device" class="form-label">{{ $t("Device") }}</label>
         <input id="pushover-device" v-model="$parent.notification.pushoverdevice" type="text" class="form-control">
-        <label for="pushover-device" class="form-label">Message Title</label>
+        <label for="pushover-device" class="form-label">{{ $t("Message Title") }}</label>
         <input id="pushover-title" v-model="$parent.notification.pushovertitle" type="text" class="form-control">
-        <label for="pushover-priority" class="form-label">Priority</label>
+        <label for="pushover-priority" class="form-label">{{ $t("Priority") }}</label>
         <select id="pushover-priority" v-model="$parent.notification.pushoverpriority" class="form-select">
             <option>-2</option>
             <option>-1</option>
@@ -16,7 +16,7 @@
             <option>1</option>
             <option>2</option>
         </select>
-        <label for="pushover-sound" class="form-label">Notification Sound</label>
+        <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
         <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
             <option>pushover</option>
             <option>bike</option>
@@ -42,15 +42,15 @@
             <option>none</option>
         </select>
         <div class="form-text">
-            <span style="color: red;"><sup>*</sup></span>Required
+            <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
+            <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+                <a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a>
+            </i18n-t>
             <p style="margin-top: 8px;">
-                More info on: <a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a>
+                {{ $t("pushoverDesc1") }}
             </p>
             <p style="margin-top: 8px;">
-                Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.
-            </p>
-            <p style="margin-top: 8px;">
-                If you want to send notifications to different devices, fill out Device field.
+                {{ $t("pushoverDesc2") }}
             </p>
         </div>
     </div>
diff --git a/src/components/notifications/Pushy.vue b/src/components/notifications/Pushy.vue
index 64e4ef9c5..b980f546d 100644
--- a/src/components/notifications/Pushy.vue
+++ b/src/components/notifications/Pushy.vue
@@ -10,9 +10,9 @@
             <HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
         </div>
     </div>
-    <p style="margin-top: 8px;">
-        More info on: <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a>
-    </p>
+    <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+        <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a>
+    </i18n-t>
 </template>
 
 <script>
diff --git a/src/components/notifications/RocketChat.vue b/src/components/notifications/RocketChat.vue
index 0776a1544..328da8bd7 100644
--- a/src/components/notifications/RocketChat.vue
+++ b/src/components/notifications/RocketChat.vue
@@ -1,29 +1,29 @@
 <template>
     <div class="mb-3">
-        <label for="rocket-webhook-url" class="form-label">Webhook URL<span style="color: red;"><sup>*</sup></span></label>
+        <label for="rocket-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
         <input id="rocket-webhook-url" v-model="$parent.notification.rocketwebhookURL" type="text" class="form-control" required>
-        <label for="rocket-username" class="form-label">Username</label>
+        <label for="rocket-username" class="form-label">{{ $t("Username") }}</label>
         <input id="rocket-username" v-model="$parent.notification.rocketusername" type="text" class="form-control">
-        <label for="rocket-iconemo" class="form-label">Icon Emoji</label>
+        <label for="rocket-iconemo" class="form-label">{{ $t("Icon Emoji") }}</label>
         <input id="rocket-iconemo" v-model="$parent.notification.rocketiconemo" type="text" class="form-control">
-        <label for="rocket-channel" class="form-label">Channel Name</label>
+        <label for="rocket-channel" class="form-label">{{ $t("Channel Name") }}</label>
         <input id="rocket-channel-name" v-model="$parent.notification.rocketchannel" type="text" class="form-control">
-        <label for="rocket-button-url" class="form-label">Uptime Kuma URL</label>
+        <label for="rocket-button-url" class="form-label">{{ $t("Uptime Kuma URL") }}</label>
         <input id="rocket-button" v-model="$parent.notification.rocketbutton" type="text" class="form-control">
         <div class="form-text">
-            <span style="color: red;"><sup>*</sup></span>Required
+            <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
+            <i18-t keypath="aboutWebhooks" tag="p" style="margin-top: 8px;">
+                <a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a>
+            </i18-t>
             <p style="margin-top: 8px;">
-                More info about webhooks on: <a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a>
+                {{ $t("aboutChannelName", [$t("rocket.chat")]) }}
             </p>
             <p style="margin-top: 8px;">
-                Enter the channel name on Rocket.chat Channel Name field if you want to bypass the webhook channel. Ex: #other-channel
-            </p>
-            <p style="margin-top: 8px;">
-                If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
-            </p>
-            <p style="margin-top: 8px;">
-                Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
+                {{ $t("aboutKumaURL") }}
             </p>
+            <i18n-t keypath="emojiCheatSheet" tag="p" style="margin-top: 8px;">
+                <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
+            </i18n-t>
         </div>
     </div>
 </template>
diff --git a/src/components/notifications/SMTP.vue b/src/components/notifications/SMTP.vue
index b86a62608..fa9b9c64f 100644
--- a/src/components/notifications/SMTP.vue
+++ b/src/components/notifications/SMTP.vue
@@ -12,8 +12,8 @@
     <div class="mb-3">
         <label for="secure" class="form-label">Secure</label>
         <select id="secure" v-model="$parent.notification.smtpSecure" class="form-select">
-            <option :value="false">None / STARTTLS (25, 587)</option>
-            <option :value="true">TLS (465)</option>
+            <option :value="false">{{ $t("secureOptionNone") }}</option>
+            <option :value="true">{{ $t("secureOptionTLS") }}</option>
         </select>
     </div>
 
@@ -21,7 +21,7 @@
         <div class="form-check">
             <input id="ignore-tls-error" v-model="$parent.notification.smtpIgnoreTLSError" class="form-check-input" type="checkbox" value="">
             <label class="form-check-label" for="ignore-tls-error">
-                Ignore TLS Error
+                {{ $t("Ignore TLS Error") }}
             </label>
         </div>
     </div>
@@ -37,24 +37,24 @@
     </div>
 
     <div class="mb-3">
-        <label for="from-email" class="form-label">From Email</label>
+        <label for="from-email" class="form-label">{{ $t("From Email") }}</label>
         <input id="from-email" v-model="$parent.notification.smtpFrom" type="text" class="form-control" required autocomplete="false" placeholder="&quot;Uptime Kuma&quot; &lt;example@kuma.pet&gt;">
         <div class="form-text">
         </div>
     </div>
 
     <div class="mb-3">
-        <label for="to-email" class="form-label">To Email</label>
+        <label for="to-email" class="form-label">{{ $t("To Email") }}</label>
         <input id="to-email" v-model="$parent.notification.smtpTo" type="text" class="form-control" required autocomplete="false" placeholder="example2@kuma.pet, example3@kuma.pet">
     </div>
 
     <div class="mb-3">
-        <label for="to-cc" class="form-label">CC</label>
+        <label for="to-cc" class="form-label">{{ $t("smtpCC") }}</label>
         <input id="to-cc" v-model="$parent.notification.smtpCC" type="text" class="form-control" autocomplete="false">
     </div>
 
     <div class="mb-3">
-        <label for="to-bcc" class="form-label">BCC</label>
+        <label for="to-bcc" class="form-label">{{ $t("smtpBCC") }}</label>
         <input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false">
     </div>
 </template>
diff --git a/src/components/notifications/Signal.vue b/src/components/notifications/Signal.vue
index 8598d07fa..212aba6a3 100644
--- a/src/components/notifications/Signal.vue
+++ b/src/components/notifications/Signal.vue
@@ -1,23 +1,25 @@
 <template>
     <div class="mb-3">
-        <label for="signal-url" class="form-label">Post URL</label>
+        <label for="signal-url" class="form-label">{{ $t("Post URL") }}</label>
         <input id="signal-url" v-model="$parent.notification.signalURL" type="url" pattern="https?://.+" class="form-control" required>
     </div>
 
     <div class="mb-3">
-        <label for="signal-number" class="form-label">Number</label>
+        <label for="signal-number" class="form-label">{{ $t("Number") }}</label>
         <input id="signal-number" v-model="$parent.notification.signalNumber" type="text" class="form-control" required>
     </div>
 
     <div class="mb-3">
-        <label for="signal-recipients" class="form-label">Recipients</label>
+        <label for="signal-recipients" class="form-label">{{ $t("Recipients") }}</label>
         <input id="signal-recipients" v-model="$parent.notification.signalRecipients" type="text" class="form-control" required>
 
         <div class="form-text">
-            You need to have a signal client with REST API.
+            <p style="margin-top: 8px;">
+                {{ $t("needSignalAPI") }}
+            </p>
 
             <p style="margin-top: 8px;">
-                You can check this url to view how to setup one:
+                {{ $t("canCheckLike") }}
             </p>
 
             <p style="margin-top: 8px;">
@@ -25,7 +27,7 @@
             </p>
 
             <p style="margin-top: 8px;">
-                IMPORTANT: You cannot mix groups and numbers in recipients!
+                {{ $t("signalImportant") }}
             </p>
         </div>
     </div>
diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue
index 1ec2cdfc1..9cd05fb59 100644
--- a/src/components/notifications/Slack.vue
+++ b/src/components/notifications/Slack.vue
@@ -1,29 +1,29 @@
 <template>
     <div class="mb-3">
-        <label for="slack-webhook-url" class="form-label">Webhook URL<span style="color: red;"><sup>*</sup></span></label>
+        <label for="slack-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
         <input id="slack-webhook-url" v-model="$parent.notification.slackwebhookURL" type="text" class="form-control" required>
-        <label for="slack-username" class="form-label">Username</label>
+        <label for="slack-username" class="form-label">{{ $t("Username") }}</label>
         <input id="slack-username" v-model="$parent.notification.slackusername" type="text" class="form-control">
-        <label for="slack-iconemo" class="form-label">Icon Emoji</label>
+        <label for="slack-iconemo" class="form-label">{{ $t("Icon Emoji") }}</label>
         <input id="slack-iconemo" v-model="$parent.notification.slackiconemo" type="text" class="form-control">
-        <label for="slack-channel" class="form-label">Channel Name</label>
+        <label for="slack-channel" class="form-label">{{ $t("Channel Name") }}</label>
         <input id="slack-channel-name" v-model="$parent.notification.slackchannel" type="text" class="form-control">
-        <label for="slack-button-url" class="form-label">Uptime Kuma URL</label>
+        <label for="slack-button-url" class="form-label">{{ $t("Uptime Kuma URL") }}</label>
         <input id="slack-button" v-model="$parent.notification.slackbutton" type="text" class="form-control">
         <div class="form-text">
-            <span style="color: red;"><sup>*</sup></span>Required
+            <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
+            <i18-t keypath="aboutWebhooks" tag="p" style="margin-top: 8px;">
+                <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
+            </i18-t>
             <p style="margin-top: 8px;">
-                More info about webhooks on: <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
+                {{ $t("aboutChannelName", [$t("slack")]) }}
             </p>
             <p style="margin-top: 8px;">
-                Enter the channel name on Slack Channel Name field if you want to bypass the webhook channel. Ex: #other-channel
-            </p>
-            <p style="margin-top: 8px;">
-                If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
-            </p>
-            <p style="margin-top: 8px;">
-                Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
+                {{ $t("aboutKumaURL") }}
             </p>
+            <i18n-t keypath="emojiCheatSheet" tag="p" style="margin-top: 8px;">
+                <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
+            </i18n-t>
         </div>
     </div>
 </template>
diff --git a/src/components/notifications/Teams.vue b/src/components/notifications/Teams.vue
index 45ba26c84..0c36cfed0 100644
--- a/src/components/notifications/Teams.vue
+++ b/src/components/notifications/Teams.vue
@@ -1,6 +1,6 @@
 <template>
     <div class="mb-3">
-        <label for="teams-webhookurl" class="form-label">Webhook URL</label>
+        <label for="teams-webhookurl" class="form-label">{{ $t("Webhook URL") }}</label>
         <input
             id="teams-webhookurl"
             v-model="$parent.notification.webhookUrl"
@@ -8,12 +8,11 @@
             class="form-control"
             required
         />
-        <div class="form-text">
-            You can learn how to create a webhook url
+        <i18n-t keypath="wayToGetTeamsURL" class="form-text" tag="div">
             <a
                 href="https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook"
                 target="_blank"
-            >here</a>.
-        </div>
+            >{{ $t("here") }}</a>
+        </i18n-t>
     </div>
 </template>
diff --git a/src/components/notifications/Telegram.vue b/src/components/notifications/Telegram.vue
index a59c804a2..1581c0279 100644
--- a/src/components/notifications/Telegram.vue
+++ b/src/components/notifications/Telegram.vue
@@ -1,14 +1,14 @@
 <template>
     <div class="mb-3">
-        <label for="telegram-bot-token" class="form-label">Bot Token</label>
+        <label for="telegram-bot-token" class="form-label">{{ $t("Bot Token") }}</label>
         <HiddenInput id="telegram-bot-token" v-model="$parent.notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput>
         <div class="form-text">
-            You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
+            {{ $t("You can get a token from") }} <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
         </div>
     </div>
 
     <div class="mb-3">
-        <label for="telegram-chat-id" class="form-label">Chat ID</label>
+        <label for="telegram-chat-id" class="form-label">{{ $t("Chat ID") }}</label>
 
         <div class="input-group mb-3">
             <input id="telegram-chat-id" v-model="$parent.notification.telegramChatID" type="text" class="form-control" required>
@@ -18,10 +18,10 @@
         </div>
 
         <div class="form-text">
-            Support Direct Chat / Group / Channel's Chat ID
+            {{ $t("supportTelegramChatID") }}
 
             <p style="margin-top: 8px;">
-                You can get your chat id by sending message to the bot and go to this url to view the chat_id:
+                {{ $t("wayToGetTelegramChatID") }}
             </p>
 
             <p style="margin-top: 8px;">
@@ -49,7 +49,7 @@ export default {
     },
     computed: {
         telegramGetUpdatesURL() {
-            let token = "<YOUR BOT TOKEN HERE>"
+            let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`
 
             if (this.$parent.notification.telegramBotToken) {
                 token = this.$parent.notification.telegramBotToken;
@@ -71,11 +71,11 @@ export default {
                     } else if (update.message) {
                         this.notification.telegramChatID = update.message.chat.id;
                     } else {
-                        throw new Error("Chat ID is not found, please send a message to this bot first")
+                        throw new Error(this.$t("chatIDNotFound"))
                     }
 
                 } else {
-                    throw new Error("Chat ID is not found, please send a message to this bot first")
+                    throw new Error(this.$t("chatIDNotFound"))
                 }
 
             } catch (error) {
diff --git a/src/components/notifications/Webhook.vue b/src/components/notifications/Webhook.vue
index 1554a83d7..3423910f8 100644
--- a/src/components/notifications/Webhook.vue
+++ b/src/components/notifications/Webhook.vue
@@ -1,11 +1,11 @@
 <template>
     <div class="mb-3">
-        <label for="webhook-url" class="form-label">Post URL</label>
+        <label for="webhook-url" class="form-label">{{ $t("Post URL") }}</label>
         <input id="webhook-url" v-model="$parent.notification.webhookURL" type="url" pattern="https?://.+" class="form-control" required>
     </div>
 
     <div class="mb-3">
-        <label for="webhook-content-type" class="form-label">Content Type</label>
+        <label for="webhook-content-type" class="form-label">{{ $t("Content Type") }}</label>
         <select id="webhook-content-type" v-model="$parent.notification.webhookContentType" class="form-select" required>
             <option value="json">
                 application/json
@@ -17,7 +17,10 @@
 
         <div class="form-text">
             <p>"application/json" is good for any modern http servers such as express.js</p>
-            <p>"multipart/form-data" is good for PHP, you just need to parse the json by <strong>json_decode($_POST['data'])</strong></p>
+            <i18n-t keypath="webhookFormDataDesc" tag="p">
+                "multipart/form-data"
+                <strong>json_decode($_POST['data'])</strong>
+            </i18n-t>
         </div>
     </div>
 </template>
diff --git a/src/languages/en.js b/src/languages/en.js
index 37704225a..ca5689442 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -168,21 +168,114 @@ export default {
     "Search...": "Search...",
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
+    // Start notification form
+    here: "here",
+    "Required": "Required",
+
     "telegram": "Telegram",
+    "Bot Token": "Bot Token",
+    "You can get a token from": "You can get a token from",
+    "Chat ID": "Chat ID",
+    supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
+    wayToGetTelegramChatID: "You can get your chat id by sending message to the bot and go to this url to view the chat_id:",
+    "YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
+    chatIDNotFound: "Chat ID is not found, please send a message to this bot first",
+
     "webhook": "Webhook",
+    "Post URL": "Post URL",
+    "Content Type": "Content Type",
+    webhookJsonDesc: "{0} is good for any modern http servers such as express.js",
+    webhookFormDataDesc: "{0} is good for PHP, you just need to parse the json by {1}",
+
     "smtp": "Email (SMTP)",
+    secureOptionNone: "None / STARTTLS (25, 587)",
+    secureOptionTLS: "TLS (465)",
+    "Ignore TLS Error": "Ignore TLS Error",
+    "From Email": "From Email",
+    "To Email": "To Email",
+    smtpCC: "CC",
+    smtpBCC: "BCC",
+
     "discord": "Discord",
+    "Discord Webhook URL": "Discord Webhook URL",
+    wayToGetDiscordURL: "You can get this by going to Server Settings -> Integrations -> Create Webhook",
+    "Bot Display Name": "Bot Display Name",
+    "Prefix Custom Message": "Prefix Custom Message",
+    "Hello @everyone is...": "Hello @everyone is...",
+
     "teams": "Microsoft Teams",
+    "Webhook URL": "Webhook URL",
+    wayToGetTeamsURL: "You can learn how to create a webhook url {0}.",
+
     "signal": "Signal",
+    "Number": "Number",
+    "Recipients": "Recipients",
+    needSignalAPI: "You need to have a signal client with REST API.",
+    canCheckLike: "You can check this url to view how to setup one:",
+    signalImportant: "IMPORTANT: You cannot mix groups and numbers in recipients!",
+
     "gotify": "Gotify",
+    "Application Token": "Application Token",
+    "Server URL": "Server URL",
+    "Priority": "Priority",
+
     "slack": "Slack",
+    "Icon Emoji": "Icon Emoji",
+    "Channel Name": "Channel Name",
+    "Uptime Kuma URL": "Uptime Kuma URL",
+    aboutWebhooks: "More info about webhooks on: {0}",
+    aboutChannelName: "Enter the channel name on {0} Channel Name field if you want to bypass the webhook channel. Ex: #other-channel",
+    aboutKumaURL: "If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.",
+    emojiCheatSheet: "Emoji cheat sheet: {0}",
+
     "rocket.chat": "Rocket.chat",
+
     "pushover": "Pushover",
+    "User Key": "User Key",
+    "Device": "Device",
+    "Message Title": "Message Title",
+    "Notification Sound": "Notification Sound",
+    "More info on:": "More info on: {0}",
+    pushoverDesc1: "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.",
+    pushoverDesc2: "If you want to send notifications to different devices, fill out Device field.",
+
     "pushy": "Pushy",
+
     "octopush": "Octopush",
+    "SMS Type": "SMS Type",
+    octopushTypePremium: "Premium (Fast - recommended for alerting)",
+    octopushTypeLowCost: "Low Cost (Slow, sometimes blocked by operator)",
+    "Check octopush prices": "Check octopush prices {0}.",
+    octopushPhoneNumber: "Phone number (intl format, eg : +33612345678) ",
+    octopushSMSSender: "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)",
+
     "lunasea": "LunaSea",
+    "LunaSea Device ID": "LunaSea Device ID",
+
     "apprise": "Apprise (Support 50+ Notification services)",
+    "Apprise URL": "Apprise URL",
+    "Example:": "Example: {0}",
+    "Read more:": "Read more: {0}",
+    "Status:": "Status: {0}",
+    "Read more": "Read more",
+    "Apprise is installed": "Apprise is installed",
+    "Apprise is not installed. ": "Apprise is not installed. ",
+
     "pushbullet": "Pushbullet",
+    "Access Token": "Access Token",
+
     "line": "Line Messenger",
+    "Channel access token": "Channel access token",
+    "Line Developers Console": "Line Developers Console",
+    "Line Developers Console - ": "Line Developers Console - {0}",
+    "Basic Settings": "Basic Settings",
+    "User ID": "User ID",
+    "Messaging API": "Messaging API",
+    wayToGetLineChannelToken: "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items.",
+
     "mattermost": "Mattermost",
+    "Icon URL": "Icon URL",
+    aboutIconURL: "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.",
+    aboutMattermostChannelName: "You can override the default channel that webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel",
+    // End notification form
 }

From 601204ae7749e105352e741c4e307b74036c65d8 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Tue, 21 Sep 2021 17:25:54 +0800
Subject: [PATCH 02/48] Default friendly name i18n and auto increase

---
 src/components/NotificationDialog.vue | 21 ++++++++++++++++++---
 src/languages/en.js                   |  1 +
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index 78d890919..2cf5eed2d 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -108,15 +108,15 @@ export default {
     watch: {
         "notification.type"(to, from) {
             let oldName;
-
+            console.log(this.$root.notificationList)
             if (from) {
-                oldName = `My ${ucfirst(from)} Alert (1)`;
+                oldName = this.getUniqueDefaultName(from);
             } else {
                 oldName = "";
             }
 
             if (! this.notification.name || this.notification.name === oldName) {
-                this.notification.name = `My ${ucfirst(to)} Alert (1)`
+                this.notification.name = this.getUniqueDefaultName(to);
             }
         },
     },
@@ -192,6 +192,21 @@ export default {
                 }
             })
         },
+        /**
+         * @param {string} notificationKey
+         * @return {string}
+         */
+        getUniqueDefaultName(notificationKey) {
+            let index = 1
+            let name = ""
+            do {
+                name = this.$t("defaultNotificationName", [
+                    this.$t(notificationKey).replace(/\(.+\)/, ""),
+                    index++
+                ]);
+            } while (this.$root.notificationList.find(it => it.name === name))
+            return name
+        }
     },
 }
 </script>
diff --git a/src/languages/en.js b/src/languages/en.js
index ca5689442..c4525aef8 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -169,6 +169,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     // Start notification form
+    defaultNotificationName: "My {0} Alert ({1})",
     here: "here",
     "Required": "Required",
 

From 6e55c447732cd17827bfbdb8ead5bb04715893d5 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Wed, 22 Sep 2021 16:13:23 +0800
Subject: [PATCH 03/48] Chore

---
 src/components/NotificationDialog.vue    | 3 +--
 src/components/notifications/Apprise.vue | 2 +-
 src/components/notifications/Signal.vue  | 2 +-
 src/languages/en.js                      | 4 ++--
 4 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index 2cf5eed2d..fb8ff2e0a 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -108,7 +108,6 @@ export default {
     watch: {
         "notification.type"(to, from) {
             let oldName;
-            console.log(this.$root.notificationList)
             if (from) {
                 oldName = this.getUniqueDefaultName(from);
             } else {
@@ -193,7 +192,7 @@ export default {
             })
         },
         /**
-         * @param {string} notificationKey
+         * @param {keyof NotificationFormList} notificationKey
          * @return {string}
          */
         getUniqueDefaultName(notificationKey) {
diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue
index 567b1eae7..e0f116f03 100644
--- a/src/components/notifications/Apprise.vue
+++ b/src/components/notifications/Apprise.vue
@@ -11,7 +11,7 @@
     </div>
     <div class="mb-3">
         <i18n-t keypath="Status:" tag="p">
-            <span v-if="appriseInstalled" class="text-primary">{{ $t("Apprise is installed") }}</span>
+            <span v-if="appriseInstalled" class="text-primary">{{ $t("Apprise is installed. ") }}</span>
             <span v-else class="text-danger">{{ $t("Apprise is not installed. ") }}<a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a></span>
         </i18n-t>
     </div>
diff --git a/src/components/notifications/Signal.vue b/src/components/notifications/Signal.vue
index 212aba6a3..e3c9e7aaf 100644
--- a/src/components/notifications/Signal.vue
+++ b/src/components/notifications/Signal.vue
@@ -19,7 +19,7 @@
             </p>
 
             <p style="margin-top: 8px;">
-                {{ $t("canCheckLike") }}
+                {{ $t("wayToCheckSignalURL") }}
             </p>
 
             <p style="margin-top: 8px;">
diff --git a/src/languages/en.js b/src/languages/en.js
index c4525aef8..c97754034 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -212,7 +212,7 @@ export default {
     "Number": "Number",
     "Recipients": "Recipients",
     needSignalAPI: "You need to have a signal client with REST API.",
-    canCheckLike: "You can check this url to view how to setup one:",
+    wayToCheckSignalURL: "You can check this url to view how to setup one:",
     signalImportant: "IMPORTANT: You cannot mix groups and numbers in recipients!",
 
     "gotify": "Gotify",
@@ -259,7 +259,7 @@ export default {
     "Read more:": "Read more: {0}",
     "Status:": "Status: {0}",
     "Read more": "Read more",
-    "Apprise is installed": "Apprise is installed",
+    "Apprise is installed. ": "Apprise is installed. ",
     "Apprise is not installed. ": "Apprise is not installed. ",
 
     "pushbullet": "Pushbullet",

From 6e9d12638c5909cdf9185401fef1bca120e7d56a Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Wed, 22 Sep 2021 16:20:59 +0800
Subject: [PATCH 04/48] Avoid space ending in translation key

---
 src/components/notifications/Apprise.vue | 4 ++--
 src/components/notifications/Line.vue    | 4 ++--
 src/languages/en.js                      | 6 +++---
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue
index e0f116f03..30abfc900 100644
--- a/src/components/notifications/Apprise.vue
+++ b/src/components/notifications/Apprise.vue
@@ -11,8 +11,8 @@
     </div>
     <div class="mb-3">
         <i18n-t keypath="Status:" tag="p">
-            <span v-if="appriseInstalled" class="text-primary">{{ $t("Apprise is installed. ") }}</span>
-            <span v-else class="text-danger">{{ $t("Apprise is not installed. ") }}<a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a></span>
+            <span v-if="appriseInstalled" class="text-primary">{{ $t("Apprise is installed.") }}</span>
+            <span v-else class="text-danger">{{ $t("Apprise is not installed.") }}<a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a></span>
         </i18n-t>
     </div>
 </template>
diff --git a/src/components/notifications/Line.vue b/src/components/notifications/Line.vue
index f6dbc7489..0d7da26a4 100644
--- a/src/components/notifications/Line.vue
+++ b/src/components/notifications/Line.vue
@@ -3,14 +3,14 @@
         <label for="line-channel-access-token" class="form-label">{{ $t("Channel access token") }}</label>
         <HiddenInput id="line-channel-access-token" v-model="$parent.notification.lineChannelAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
-    <i18n-t keypath="Line Developers Console - " tag="div" class="form-text">
+    <i18n-t keypath="lineDevConsoleTo" tag="div" class="form-text">
         <b>{{ $t("Basic Settings") }}</b>
     </i18n-t>
     <div class="mb-3" style="margin-top: 12px;">
         <label for="line-user-id" class="form-label">User ID</label>
         <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
     </div>
-    <i18n-t keypath="Line Developers Console - " tag="div" class="form-text">
+    <i18n-t keypath="lineDevConsoleTo" tag="div" class="form-text">
         <b>{{ $t("Messaging API") }}</b>
     </i18n-t>
     <i18n-t keypath="wayToGetLineChannelToken" tag="div" class="form-text" style="margin-top: 8px;">
diff --git a/src/languages/en.js b/src/languages/en.js
index c97754034..948ed437a 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -259,8 +259,8 @@ export default {
     "Read more:": "Read more: {0}",
     "Status:": "Status: {0}",
     "Read more": "Read more",
-    "Apprise is installed. ": "Apprise is installed. ",
-    "Apprise is not installed. ": "Apprise is not installed. ",
+    "Apprise is installed.": "Apprise is installed. ",
+    "Apprise is not installed.": "Apprise is not installed. ",
 
     "pushbullet": "Pushbullet",
     "Access Token": "Access Token",
@@ -268,7 +268,7 @@ export default {
     "line": "Line Messenger",
     "Channel access token": "Channel access token",
     "Line Developers Console": "Line Developers Console",
-    "Line Developers Console - ": "Line Developers Console - {0}",
+    lineDevConsoleTo: "Line Developers Console - {0}",
     "Basic Settings": "Basic Settings",
     "User ID": "User ID",
     "Messaging API": "Messaging API",

From 624f632a7a537d3b21ef0c5d55b6b108a5046df2 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Wed, 22 Sep 2021 22:15:50 +0800
Subject: [PATCH 05/48] Apprise status translation key

---
 src/components/NotificationDialog.vue    | 2 +-
 src/components/notifications/Apprise.vue | 6 ++++--
 src/languages/en.js                      | 4 ++--
 3 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index fb8ff2e0a..0c2035def 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -200,7 +200,7 @@ export default {
             let name = ""
             do {
                 name = this.$t("defaultNotificationName", [
-                    this.$t(notificationKey).replace(/\(.+\)/, ""),
+                    this.$t(notificationKey).replace(/\(.+\)/, "").trim(),
                     index++
                 ]);
             } while (this.$root.notificationList.find(it => it.name === name))
diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue
index 30abfc900..f485405b4 100644
--- a/src/components/notifications/Apprise.vue
+++ b/src/components/notifications/Apprise.vue
@@ -11,8 +11,10 @@
     </div>
     <div class="mb-3">
         <i18n-t keypath="Status:" tag="p">
-            <span v-if="appriseInstalled" class="text-primary">{{ $t("Apprise is installed.") }}</span>
-            <span v-else class="text-danger">{{ $t("Apprise is not installed.") }}<a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a></span>
+            <span v-if="appriseInstalled" class="text-primary">{{ $t("appriseInstalled") }}</span>
+            <i18n-t v-else keypath="appriseNotInstalled" tag="span" class="text-danger">
+                <a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a>
+            </i18n-t>
         </i18n-t>
     </div>
 </template>
diff --git a/src/languages/en.js b/src/languages/en.js
index 948ed437a..645467978 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -259,8 +259,8 @@ export default {
     "Read more:": "Read more: {0}",
     "Status:": "Status: {0}",
     "Read more": "Read more",
-    "Apprise is installed.": "Apprise is installed. ",
-    "Apprise is not installed.": "Apprise is not installed. ",
+    appriseInstalled: "Apprise is installed.",
+    appriseNotInstalled: "Apprise is not installed. {0}",
 
     "pushbullet": "Pushbullet",
     "Access Token": "Access Token",

From 58aa83331e55db31f30b2d6ef595c832826478f2 Mon Sep 17 00:00:00 2001
From: Adam Stachowicz <adam.stachowicz@fingo.info>
Date: Tue, 28 Sep 2021 06:53:23 +0200
Subject: [PATCH 06/48] Fix Polish language, add missing `Status Page` i18n

---
 src/i18n.js              | 6 +++---
 src/languages/da-DK.js   | 5 +++--
 src/languages/de-DE.js   | 3 ++-
 src/languages/en.js      | 3 ++-
 src/languages/es-ES.js   | 3 ++-
 src/languages/et-EE.js   | 3 ++-
 src/languages/fr-FR.js   | 3 ++-
 src/languages/it-IT.js   | 3 ++-
 src/languages/ja.js      | 3 ++-
 src/languages/ko-KR.js   | 3 ++-
 src/languages/nl-NL.js   | 3 ++-
 src/languages/pl.js      | 7 ++++---
 src/languages/pt-BR.js   | 2 +-
 src/languages/ru-RU.js   | 2 +-
 src/languages/sr-latn.js | 3 ++-
 src/languages/sr.js      | 3 ++-
 src/languages/sv-SE.js   | 3 ++-
 src/languages/tr-TR.js   | 3 ++-
 src/languages/zh-CN.js   | 3 ++-
 src/languages/zh-HK.js   | 3 ++-
 20 files changed, 42 insertions(+), 25 deletions(-)

diff --git a/src/i18n.js b/src/i18n.js
index 633a53acb..bd1811f61 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -3,7 +3,6 @@ import daDK from "./languages/da-DK";
 import deDE from "./languages/de-DE";
 import en from "./languages/en";
 import esEs from "./languages/es-ES";
-import ptBR from "./languages/pt-BR";
 import etEE from "./languages/et-EE";
 import frFR from "./languages/fr-FR";
 import itIT from "./languages/it-IT";
@@ -11,11 +10,12 @@ import ja from "./languages/ja";
 import koKR from "./languages/ko-KR";
 import nlNL from "./languages/nl-NL";
 import pl from "./languages/pl";
+import ptBR from "./languages/pt-BR";
 import ruRU from "./languages/ru-RU";
 import sr from "./languages/sr";
 import srLatn from "./languages/sr-latn";
-import trTR from "./languages/tr-TR";
 import svSE from "./languages/sv-SE";
+import trTR from "./languages/tr-TR";
 import zhCN from "./languages/zh-CN";
 import zhHK from "./languages/zh-HK";
 
@@ -45,6 +45,6 @@ export const i18n = createI18n({
     locale: localStorage.locale || "en",
     fallbackLocale: "en",
     silentFallbackWarn: true,
-    silentTranslationWarn: true,
+    silentTranslationWarn: false,
     messages: languageList,
 });
diff --git a/src/languages/da-DK.js b/src/languages/da-DK.js
index e3d375ce2..b9ce20eca 100644
--- a/src/languages/da-DK.js
+++ b/src/languages/da-DK.js
@@ -143,7 +143,7 @@ export default {
     "Show URI": "Vis URI",
     "Clear all statistics": "Ryd alle Statistikker",
     retryCheckEverySecond: "Prøv igen hvert {0} sekund.",
-    importHandleDescription: "Vælg 'Spring over eksisterende', hvis du vil springe over hver overvåger eller underretning med samme navn. 'Overskriv' sletter alle eksisterende overvågere og underretninger.", 
+    importHandleDescription: "Vælg 'Spring over eksisterende', hvis du vil springe over hver overvåger eller underretning med samme navn. 'Overskriv' sletter alle eksisterende overvågere og underretninger.",
     confirmImportMsg: "Er du sikker på at importere sikkerhedskopien? Sørg for, at du har valgt den rigtige importindstilling.",
     "Heartbeat Retry Interval": "Heartbeat Gentagelsesinterval",
     "Import Backup": "Importer Backup",
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Gns. Ping",
     "Avg. Response": "Gns. Respons",
     "Entry Page": "Entry Side",
-    "statusPageNothing": "Intet her, tilføj venligst en Gruppe eller en Overvåger.",
+    statusPageNothing: "Intet her, tilføj venligst en Gruppe eller en Overvåger.",
     "No Services": "Ingen Tjenester",
     "All Systems Operational": "Alle Systemer i Drift",
     "Partially Degraded Service": "Delvist Forringet Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Tilføj en Overvåger",
     "Edit Status Page": "Rediger Statusside",
     "Go to Dashboard": "Gå til Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index ef2f7653a..96cd8a283 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -169,7 +169,7 @@ export default {
     "Avg. Ping": "Durchsch. Ping",
     "Avg. Response": "Durchsch. Antwort",
     "Entry Page": "Einstiegsseite",
-    "statusPageNothing": "Nichts ist hier, bitte füge eine Gruppe oder Monitor hinzu.",
+    statusPageNothing: "Nichts ist hier, bitte füge eine Gruppe oder Monitor hinzu.",
     "No Services": "Keine Dienste",
     "All Systems Operational": "Alle Systeme Betriebsbereit",
     "Partially Degraded Service": "Teilweise beeinträchtigter Dienst",
@@ -178,4 +178,5 @@ export default {
     "Add a monitor": "Monitor hinzufügen",
     "Edit Status Page": "Bearbeite Statusseite",
     "Go to Dashboard": "Gehe zum Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/en.js b/src/languages/en.js
index 75d8f30c3..280c29b94 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -169,7 +169,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -178,4 +178,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/es-ES.js b/src/languages/es-ES.js
index 7571b1308..366291db9 100644
--- a/src/languages/es-ES.js
+++ b/src/languages/es-ES.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/et-EE.js b/src/languages/et-EE.js
index b7d9722c3..fe637de5d 100644
--- a/src/languages/et-EE.js
+++ b/src/languages/et-EE.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/fr-FR.js b/src/languages/fr-FR.js
index bc3c5d5d4..f10600775 100644
--- a/src/languages/fr-FR.js
+++ b/src/languages/fr-FR.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Ping moyen",
     "Avg. Response": "Réponse moyenne",
     "Entry Page": "Page d'accueil",
-    "statusPageNothing": "Rien ici, veuillez ajouter un groupe ou une sonde.",
+    statusPageNothing: "Rien ici, veuillez ajouter un groupe ou une sonde.",
     "No Services": "Aucun service",
     "All Systems Operational": "Tous les systèmes sont opérationnels",
     "Partially Degraded Service": "Service partiellement dégradé",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Ajouter une sonde",
     "Edit Status Page": "Modifier la page de statut",
     "Go to Dashboard": "Accéder au tableau de bord",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/it-IT.js b/src/languages/it-IT.js
index 9beb9db57..10c2df89b 100644
--- a/src/languages/it-IT.js
+++ b/src/languages/it-IT.js
@@ -169,7 +169,7 @@ export default {
     "Avg. Ping": "Ping medio",
     "Avg. Response": "Risposta media",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.",
+    statusPageNothing: "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.",
     "No Services": "Nessun Servizio",
     "All Systems Operational": "Tutti i sistemi sono operativi",
     "Partially Degraded Service": "Servizio parzialmente degradato",
@@ -178,4 +178,5 @@ export default {
     "Add a monitor": "Aggiungi un monitoraggio",
     "Edit Status Page": "Modifica pagina di stato",
     "Go to Dashboard": "Vai al Cruscotto",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/ja.js b/src/languages/ja.js
index bf8e3129a..3462166ba 100644
--- a/src/languages/ja.js
+++ b/src/languages/ja.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/ko-KR.js b/src/languages/ko-KR.js
index fb3d65ea3..881d609e0 100644
--- a/src/languages/ko-KR.js
+++ b/src/languages/ko-KR.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/nl-NL.js b/src/languages/nl-NL.js
index 78bbe5a81..b55e259b6 100644
--- a/src/languages/nl-NL.js
+++ b/src/languages/nl-NL.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/pl.js b/src/languages/pl.js
index 006039144..ad10ceb07 100644
--- a/src/languages/pl.js
+++ b/src/languages/pl.js
@@ -169,14 +169,15 @@ export default {
     "Search...": "Szukaj...",
     "Avg. Ping": "Średni ping",
     "Avg. Response": "Średnia odpowiedź",
-    "Entry Page": "Wejdź na stronę",
-    "statusPageNothing": "Nic tu nie ma, dodaj monitor lub grupę.",
+    "Entry Page": "Strona główna",
+    statusPageNothing: "Nic tu nie ma, dodaj grupę lub monitor.",
     "No Services": "Brak usług",
     "All Systems Operational": "Wszystkie systemy działają",
     "Partially Degraded Service": "Częściowy błąd usługi",
     "Degraded Service": "Błąd usługi",
     "Add Group": "Dodaj grupę",
-    "Add a monitor": "Dodaj monitoe",
+    "Add a monitor": "Dodaj monitor",
     "Edit Status Page": "Edytuj stronę statusu",
     "Go to Dashboard": "Idź do panelu",
+    "Status Page": "Strona statusu",
 };
diff --git a/src/languages/pt-BR.js b/src/languages/pt-BR.js
index 4fc22fefd..4f65f6038 100644
--- a/src/languages/pt-BR.js
+++ b/src/languages/pt-BR.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Response": "Resposta Média. ",
     "Status Page": "Página de Status",
     "Entry Page": "Página de entrada",
-    "statusPageNothing": "Nada aqui, por favor, adicione um grupo ou monitor.",
+    statusPageNothing: "Nada aqui, por favor, adicione um grupo ou monitor.",
     "No Services": "Nenhum Serviço",
     "All Systems Operational": "Todos os Serviços Operacionais",
     "Partially Degraded Service": "Serviço parcialmente degradado",
diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js
index 1686aec49..18d97305f 100644
--- a/src/languages/ru-RU.js
+++ b/src/languages/ru-RU.js
@@ -180,7 +180,7 @@ export default {
     "Edit Status Page": "Редактировать",
     "Go to Dashboard": "Панель мониторов",
     "Status Page": "Статус сервисов",
-    "Discard": "Отмена",
+    Discard: "Отмена",
     "Create Incident": "Создать инцидент",
     "Switch to Dark Theme": "Тёмная тема",
     "Switch to Light Theme": "Светлая тема",
diff --git a/src/languages/sr-latn.js b/src/languages/sr-latn.js
index 51cb7f8f0..3542e1d6f 100644
--- a/src/languages/sr-latn.js
+++ b/src/languages/sr-latn.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/sr.js b/src/languages/sr.js
index 0c8b6ef44..9cb1379e6 100644
--- a/src/languages/sr.js
+++ b/src/languages/sr.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/sv-SE.js b/src/languages/sv-SE.js
index de4fbcc7d..ef66aca5e 100644
--- a/src/languages/sv-SE.js
+++ b/src/languages/sv-SE.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/tr-TR.js b/src/languages/tr-TR.js
index c73775bb1..908afe6dc 100644
--- a/src/languages/tr-TR.js
+++ b/src/languages/tr-TR.js
@@ -169,7 +169,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -178,4 +178,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index e9b1c20e9..a784cd34b 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "平均Ping",
     "Avg. Response": "平均响应",
     "Entry Page": "入口页面",
-    "statusPageNothing": "这里什么也没有,请添加一个分组或一个监控项。",
+    statusPageNothing: "这里什么也没有,请添加一个分组或一个监控项。",
     "No Services": "无服务",
     "All Systems Operational": "所有服务运行正常",
     "Partially Degraded Service": "部分服务出现故障",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "添加监控项",
     "Edit Status Page": "编辑状态页",
     "Go to Dashboard": "前往仪表盘",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/zh-HK.js b/src/languages/zh-HK.js
index 92714d713..df3cc4165 100644
--- a/src/languages/zh-HK.js
+++ b/src/languages/zh-HK.js
@@ -170,7 +170,7 @@ export default {
     "Avg. Ping": "Avg. Ping",
     "Avg. Response": "Avg. Response",
     "Entry Page": "Entry Page",
-    "statusPageNothing": "Nothing here, please add a group or a monitor.",
+    statusPageNothing: "Nothing here, please add a group or a monitor.",
     "No Services": "No Services",
     "All Systems Operational": "All Systems Operational",
     "Partially Degraded Service": "Partially Degraded Service",
@@ -179,4 +179,5 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
+    "Status Page": "Status Page",
 };

From fa9d26416c4696aec6b520f7b05de71500a4fa84 Mon Sep 17 00:00:00 2001
From: Adam Stachowicz <adam.stachowicz@fingo.info>
Date: Tue, 28 Sep 2021 07:02:19 +0200
Subject: [PATCH 07/48] `silentTranslationWarn` if not development

---
 src/i18n.js | 2 +-
 src/main.js | 9 ++++-----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/i18n.js b/src/i18n.js
index bd1811f61..a8a1a0cb9 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -45,6 +45,6 @@ export const i18n = createI18n({
     locale: localStorage.locale || "en",
     fallbackLocale: "en",
     silentFallbackWarn: true,
-    silentTranslationWarn: false,
+    silentTranslationWarn: process.env.NODE_ENV !== "development",
     messages: languageList,
 });
diff --git a/src/main.js b/src/main.js
index 94807b44b..79ba2d1dd 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,7 +1,7 @@
 import "bootstrap";
 import { createApp, h } from "vue";
+import contenteditable from "vue-contenteditable";
 import Toast from "vue-toastification";
-import contenteditable from "vue-contenteditable"
 import "vue-toastification/dist/index.css";
 import App from "./App.vue";
 import "./assets/app.scss";
@@ -9,10 +9,9 @@ import { i18n } from "./i18n";
 import { FontAwesomeIcon } from "./icon.js";
 import datetime from "./mixins/datetime";
 import mobile from "./mixins/mobile";
+import publicMixin from "./mixins/public";
 import socket from "./mixins/socket";
 import theme from "./mixins/theme";
-import publicMixin from "./mixins/public";
-
 import { router } from "./router";
 import { appName } from "./util.ts";
 
@@ -27,10 +26,10 @@ const app = createApp({
     data() {
         return {
             appName: appName
-        }
+        };
     },
     render: () => h(App),
-})
+});
 
 app.use(router);
 app.use(i18n);

From 138ddf56089d7dea14669ff7a62458a7ad264f2a Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Thu, 30 Sep 2021 19:22:17 +0800
Subject: [PATCH 08/48] Move attribute `tag` to start of tag

---
 src/components/notifications/Apprise.vue    | 12 ++++++------
 src/components/notifications/Line.vue       |  8 ++++----
 src/components/notifications/Mattermost.vue |  4 ++--
 src/components/notifications/Octopush.vue   |  6 +++---
 src/components/notifications/Pushbullet.vue |  4 ++--
 src/components/notifications/Pushover.vue   |  2 +-
 src/components/notifications/Pushy.vue      |  4 ++--
 src/components/notifications/RocketChat.vue |  4 ++--
 src/components/notifications/Slack.vue      |  4 ++--
 src/components/notifications/Teams.vue      |  2 +-
 src/components/notifications/Webhook.vue    |  2 +-
 src/languages/en.js                         | 17 -----------------
 12 files changed, 26 insertions(+), 43 deletions(-)

diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue
index f485405b4..c10e23cf9 100644
--- a/src/components/notifications/Apprise.vue
+++ b/src/components/notifications/Apprise.vue
@@ -4,15 +4,15 @@
         <input id="apprise-url" v-model="$parent.notification.appriseURL" type="text" class="form-control" required>
         <div class="form-text">
             <p>{{ $t("Example:", ["twilio://AccountSid:AuthToken@FromPhoneNo"]) }}</p>
-            <i18n-t keypath="Read more:" tag="p">
+            <i18n-t tag="p" keypath="Read more:">
                 <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
             </i18n-t>
         </div>
     </div>
     <div class="mb-3">
-        <i18n-t keypath="Status:" tag="p">
+        <i18n-t tag="p" keypath="Status:">
             <span v-if="appriseInstalled" class="text-primary">{{ $t("appriseInstalled") }}</span>
-            <i18n-t v-else keypath="appriseNotInstalled" tag="span" class="text-danger">
+            <i18n-t v-else tag="span" keypath="appriseNotInstalled" class="text-danger">
                 <a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a>
             </i18n-t>
         </i18n-t>
@@ -24,12 +24,12 @@ export default {
     data() {
         return {
             appriseInstalled: false
-        }
+        };
     },
     mounted() {
         this.$root.getSocket().emit("checkApprise", (installed) => {
             this.appriseInstalled = installed;
-        })
+        });
     },
-}
+};
 </script>
diff --git a/src/components/notifications/Line.vue b/src/components/notifications/Line.vue
index 0d7da26a4..cb52c0c1f 100644
--- a/src/components/notifications/Line.vue
+++ b/src/components/notifications/Line.vue
@@ -3,17 +3,17 @@
         <label for="line-channel-access-token" class="form-label">{{ $t("Channel access token") }}</label>
         <HiddenInput id="line-channel-access-token" v-model="$parent.notification.lineChannelAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
-    <i18n-t keypath="lineDevConsoleTo" tag="div" class="form-text">
+    <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
         <b>{{ $t("Basic Settings") }}</b>
     </i18n-t>
     <div class="mb-3" style="margin-top: 12px;">
         <label for="line-user-id" class="form-label">User ID</label>
         <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
     </div>
-    <i18n-t keypath="lineDevConsoleTo" tag="div" class="form-text">
+    <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
         <b>{{ $t("Messaging API") }}</b>
     </i18n-t>
-    <i18n-t keypath="wayToGetLineChannelToken" tag="div" class="form-text" style="margin-top: 8px;">
+    <i18n-t tag="div" keypath="wayToGetLineChannelToken" class="form-text" style="margin-top: 8px;">
         <a href="https://developers.line.biz/console/" target="_blank">{{ $t("Line Developers Console") }}</a>
     </i18n-t>
 </template>
@@ -25,5 +25,5 @@ export default {
     components: {
         HiddenInput,
     },
-}
+};
 </script>
diff --git a/src/components/notifications/Mattermost.vue b/src/components/notifications/Mattermost.vue
index 4c6e69189..2d174146a 100644
--- a/src/components/notifications/Mattermost.vue
+++ b/src/components/notifications/Mattermost.vue
@@ -12,7 +12,7 @@
         <input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control">
         <div class="form-text">
             <span style="color:red;"><sup>*</sup></span>{{ $t("Required") }}
-            <i18n-t keypath="aboutWebhooks" tag="p" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
                 <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a>
             </i18n-t>
             <p style="margin-top: 8px;">
@@ -24,7 +24,7 @@
             <p style="margin-top: 8px;">
                 {{ $t("aboutIconURL") }}
             </p>
-            <i18n-t keypath="emojiCheatSheet" tag="p" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="emojiCheatSheet" style="margin-top: 8px;">
                 <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
             </i18n-t>
         </div>
diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue
index b8ea6dfac..fb0eb7f47 100644
--- a/src/components/notifications/Octopush.vue
+++ b/src/components/notifications/Octopush.vue
@@ -11,7 +11,7 @@
             <option value="sms_premium">{{ $t("octopushTypePremium") }}</option>
             <option value="sms_low_cost">{{ $t("octopushTypeLowCost") }}</option>
         </select>
-        <i18n-t keypath="Check octopush prices" tag="div" class="form-text">
+        <i18n-t tag="div" keypath="Check octopush prices" class="form-text">
             <a href="https://octopush.com/tarifs-sms-international/" target="_blank">https://octopush.com/tarifs-sms-international/</a>
         </i18n-t>
     </div>
@@ -24,7 +24,7 @@
         <input id="octopush-sender-name" v-model="$parent.notification.octopushSenderName" type="text" minlength="3" maxlength="11" class="form-control">
     </div>
 
-    <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+    <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
         <a href="https://octopush.com/api-sms-documentation/envoi-de-sms/" target="_blank">https://octopush.com/api-sms-documentation/envoi-de-sms/</a>
     </i18n-t>
 </template>
@@ -36,5 +36,5 @@ export default {
     components: {
         HiddenInput,
     },
-}
+};
 </script>
diff --git a/src/components/notifications/Pushbullet.vue b/src/components/notifications/Pushbullet.vue
index ea848222d..37a2e095c 100644
--- a/src/components/notifications/Pushbullet.vue
+++ b/src/components/notifications/Pushbullet.vue
@@ -4,7 +4,7 @@
         <HiddenInput id="pushbullet-access-token" v-model="$parent.notification.pushbulletAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
 
-    <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+    <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
         <a href="https://docs.pushbullet.com" target="_blank">https://docs.pushbullet.com</a>
     </i18n-t>
 </template>
@@ -16,5 +16,5 @@ export default {
     components: {
         HiddenInput,
     },
-}
+};
 </script>
diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue
index 7f0a94173..af6e9d83f 100644
--- a/src/components/notifications/Pushover.vue
+++ b/src/components/notifications/Pushover.vue
@@ -43,7 +43,7 @@
         </select>
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
-            <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
                 <a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a>
             </i18n-t>
             <p style="margin-top: 8px;">
diff --git a/src/components/notifications/Pushy.vue b/src/components/notifications/Pushy.vue
index b980f546d..26f404d29 100644
--- a/src/components/notifications/Pushy.vue
+++ b/src/components/notifications/Pushy.vue
@@ -10,7 +10,7 @@
             <HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
         </div>
     </div>
-    <i18n-t keypath="More info on:" tag="p" style="margin-top: 8px;">
+    <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
         <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a>
     </i18n-t>
 </template>
@@ -22,5 +22,5 @@ export default {
     components: {
         HiddenInput,
     },
-}
+};
 </script>
diff --git a/src/components/notifications/RocketChat.vue b/src/components/notifications/RocketChat.vue
index 328da8bd7..8f8082101 100644
--- a/src/components/notifications/RocketChat.vue
+++ b/src/components/notifications/RocketChat.vue
@@ -12,7 +12,7 @@
         <input id="rocket-button" v-model="$parent.notification.rocketbutton" type="text" class="form-control">
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
-            <i18-t keypath="aboutWebhooks" tag="p" style="margin-top: 8px;">
+            <i18-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
                 <a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a>
             </i18-t>
             <p style="margin-top: 8px;">
@@ -21,7 +21,7 @@
             <p style="margin-top: 8px;">
                 {{ $t("aboutKumaURL") }}
             </p>
-            <i18n-t keypath="emojiCheatSheet" tag="p" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="emojiCheatSheet" style="margin-top: 8px;">
                 <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
             </i18n-t>
         </div>
diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue
index 9cd05fb59..8675922be 100644
--- a/src/components/notifications/Slack.vue
+++ b/src/components/notifications/Slack.vue
@@ -12,7 +12,7 @@
         <input id="slack-button" v-model="$parent.notification.slackbutton" type="text" class="form-control">
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
-            <i18-t keypath="aboutWebhooks" tag="p" style="margin-top: 8px;">
+            <i18-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
                 <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
             </i18-t>
             <p style="margin-top: 8px;">
@@ -21,7 +21,7 @@
             <p style="margin-top: 8px;">
                 {{ $t("aboutKumaURL") }}
             </p>
-            <i18n-t keypath="emojiCheatSheet" tag="p" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="emojiCheatSheet" style="margin-top: 8px;">
                 <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
             </i18n-t>
         </div>
diff --git a/src/components/notifications/Teams.vue b/src/components/notifications/Teams.vue
index 0c36cfed0..2bc76486a 100644
--- a/src/components/notifications/Teams.vue
+++ b/src/components/notifications/Teams.vue
@@ -8,7 +8,7 @@
             class="form-control"
             required
         />
-        <i18n-t keypath="wayToGetTeamsURL" class="form-text" tag="div">
+        <i18n-t tag="div" keypath="wayToGetTeamsURL" class="form-text">
             <a
                 href="https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook"
                 target="_blank"
diff --git a/src/components/notifications/Webhook.vue b/src/components/notifications/Webhook.vue
index 3423910f8..c7031f748 100644
--- a/src/components/notifications/Webhook.vue
+++ b/src/components/notifications/Webhook.vue
@@ -17,7 +17,7 @@
 
         <div class="form-text">
             <p>"application/json" is good for any modern http servers such as express.js</p>
-            <i18n-t keypath="webhookFormDataDesc" tag="p">
+            <i18n-t tag="p" keypath="webhookFormDataDesc">
                 "multipart/form-data"
                 <strong>json_decode($_POST['data'])</strong>
             </i18n-t>
diff --git a/src/languages/en.js b/src/languages/en.js
index 39464c3f0..b4b7769de 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -182,7 +182,6 @@ export default {
     defaultNotificationName: "My {0} Alert ({1})",
     here: "here",
     "Required": "Required",
-
     "telegram": "Telegram",
     "Bot Token": "Bot Token",
     "You can get a token from": "You can get a token from",
@@ -191,13 +190,11 @@ export default {
     wayToGetTelegramChatID: "You can get your chat id by sending message to the bot and go to this url to view the chat_id:",
     "YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
     chatIDNotFound: "Chat ID is not found, please send a message to this bot first",
-
     "webhook": "Webhook",
     "Post URL": "Post URL",
     "Content Type": "Content Type",
     webhookJsonDesc: "{0} is good for any modern http servers such as express.js",
     webhookFormDataDesc: "{0} is good for PHP, you just need to parse the json by {1}",
-
     "smtp": "Email (SMTP)",
     secureOptionNone: "None / STARTTLS (25, 587)",
     secureOptionTLS: "TLS (465)",
@@ -206,30 +203,25 @@ export default {
     "To Email": "To Email",
     smtpCC: "CC",
     smtpBCC: "BCC",
-
     "discord": "Discord",
     "Discord Webhook URL": "Discord Webhook URL",
     wayToGetDiscordURL: "You can get this by going to Server Settings -> Integrations -> Create Webhook",
     "Bot Display Name": "Bot Display Name",
     "Prefix Custom Message": "Prefix Custom Message",
     "Hello @everyone is...": "Hello @everyone is...",
-
     "teams": "Microsoft Teams",
     "Webhook URL": "Webhook URL",
     wayToGetTeamsURL: "You can learn how to create a webhook url {0}.",
-
     "signal": "Signal",
     "Number": "Number",
     "Recipients": "Recipients",
     needSignalAPI: "You need to have a signal client with REST API.",
     wayToCheckSignalURL: "You can check this url to view how to setup one:",
     signalImportant: "IMPORTANT: You cannot mix groups and numbers in recipients!",
-
     "gotify": "Gotify",
     "Application Token": "Application Token",
     "Server URL": "Server URL",
     "Priority": "Priority",
-
     "slack": "Slack",
     "Icon Emoji": "Icon Emoji",
     "Channel Name": "Channel Name",
@@ -238,9 +230,7 @@ export default {
     aboutChannelName: "Enter the channel name on {0} Channel Name field if you want to bypass the webhook channel. Ex: #other-channel",
     aboutKumaURL: "If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.",
     emojiCheatSheet: "Emoji cheat sheet: {0}",
-
     "rocket.chat": "Rocket.chat",
-
     "pushover": "Pushover",
     "User Key": "User Key",
     "Device": "Device",
@@ -249,9 +239,7 @@ export default {
     "More info on:": "More info on: {0}",
     pushoverDesc1: "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.",
     pushoverDesc2: "If you want to send notifications to different devices, fill out Device field.",
-
     "pushy": "Pushy",
-
     "octopush": "Octopush",
     "SMS Type": "SMS Type",
     octopushTypePremium: "Premium (Fast - recommended for alerting)",
@@ -259,10 +247,8 @@ export default {
     "Check octopush prices": "Check octopush prices {0}.",
     octopushPhoneNumber: "Phone number (intl format, eg : +33612345678) ",
     octopushSMSSender: "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)",
-
     "lunasea": "LunaSea",
     "LunaSea Device ID": "LunaSea Device ID",
-
     "apprise": "Apprise (Support 50+ Notification services)",
     "Apprise URL": "Apprise URL",
     "Example:": "Example: {0}",
@@ -271,10 +257,8 @@ export default {
     "Read more": "Read more",
     appriseInstalled: "Apprise is installed.",
     appriseNotInstalled: "Apprise is not installed. {0}",
-
     "pushbullet": "Pushbullet",
     "Access Token": "Access Token",
-
     "line": "Line Messenger",
     "Channel access token": "Channel access token",
     "Line Developers Console": "Line Developers Console",
@@ -283,7 +267,6 @@ export default {
     "User ID": "User ID",
     "Messaging API": "Messaging API",
     wayToGetLineChannelToken: "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items.",
-
     "mattermost": "Mattermost",
     "Icon URL": "Icon URL",
     aboutIconURL: "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.",

From 0f2059cde00e87d0a88ca052a4741b06c7a16294 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Thu, 30 Sep 2021 19:48:24 +0800
Subject: [PATCH 09/48] Use named slot translation when has multi-slot

---
 src/components/NotificationDialog.vue    | 52 ++++++++++++------------
 src/components/notifications/Webhook.vue |  6 ++-
 src/languages/en.js                      |  4 +-
 3 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index 0c2035def..659f57267 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -68,11 +68,11 @@
 </template>
 
 <script lang="ts">
-import { Modal } from "bootstrap"
-import { ucfirst } from "../util.ts"
+import { Modal } from "bootstrap";
+import { ucfirst } from "../util.ts";
 
 import Confirm from "./Confirm.vue";
-import NotificationFormList from "./notifications"
+import NotificationFormList from "./notifications";
 
 export default {
     components: {
@@ -93,15 +93,15 @@ export default {
                 isDefault: false,
                 // Do not set default value here, please scroll to show()
             }
-        }
+        };
     },
 
     computed: {
         currentForm() {
             if (!this.notification.type) {
-                return null
+                return null;
             }
-            return NotificationFormList[this.notification.type]
+            return NotificationFormList[this.notification.type];
         }
     },
 
@@ -120,13 +120,13 @@ export default {
         },
     },
     mounted() {
-        this.modal = new Modal(this.$refs.modal)
+        this.modal = new Modal(this.$refs.modal);
     },
     methods: {
 
         deleteConfirm() {
             this.modal.hide();
-            this.$refs.confirmDelete.show()
+            this.$refs.confirmDelete.show();
         },
 
         show(notificationID) {
@@ -145,19 +145,19 @@ export default {
                     name: "",
                     type: null,
                     isDefault: false,
-                }
+                };
 
                 // Set Default value here
                 this.notification.type = this.notificationTypes[0];
             }
 
-            this.modal.show()
+            this.modal.show();
         },
 
         submit() {
             this.processing = true;
             this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
-                this.$root.toastRes(res)
+                this.$root.toastRes(res);
                 this.processing = false;
 
                 if (res.ok) {
@@ -169,45 +169,45 @@ export default {
                     }
 
                 }
-            })
+            });
         },
 
         test() {
             this.processing = true;
             this.$root.getSocket().emit("testNotification", this.notification, (res) => {
-                this.$root.toastRes(res)
+                this.$root.toastRes(res);
                 this.processing = false;
-            })
+            });
         },
 
         deleteNotification() {
             this.processing = true;
             this.$root.getSocket().emit("deleteNotification", this.id, (res) => {
-                this.$root.toastRes(res)
+                this.$root.toastRes(res);
                 this.processing = false;
 
                 if (res.ok) {
-                    this.modal.hide()
+                    this.modal.hide();
                 }
-            })
+            });
         },
         /**
          * @param {keyof NotificationFormList} notificationKey
          * @return {string}
          */
         getUniqueDefaultName(notificationKey) {
-            let index = 1
-            let name = ""
+            let index = 1;
+            let name = "";
             do {
-                name = this.$t("defaultNotificationName", [
-                    this.$t(notificationKey).replace(/\(.+\)/, "").trim(),
-                    index++
-                ]);
-            } while (this.$root.notificationList.find(it => it.name === name))
-            return name
+                name = this.$t("defaultNotificationName", {
+                    notification: this.$t(notificationKey).replace(/\(.+\)/, "").trim(),
+                    number: index++
+                });
+            } while (this.$root.notificationList.find(it => it.name === name));
+            return name;
         }
     },
-}
+};
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/notifications/Webhook.vue b/src/components/notifications/Webhook.vue
index c7031f748..3cfc940e9 100644
--- a/src/components/notifications/Webhook.vue
+++ b/src/components/notifications/Webhook.vue
@@ -18,8 +18,10 @@
         <div class="form-text">
             <p>"application/json" is good for any modern http servers such as express.js</p>
             <i18n-t tag="p" keypath="webhookFormDataDesc">
-                "multipart/form-data"
-                <strong>json_decode($_POST['data'])</strong>
+                <template #multipart>"multipart/form-data"</template>
+                <template #decodeFunction>
+                    <strong>json_decode($_POST['data'])</strong>
+                </template>
             </i18n-t>
         </div>
     </div>
diff --git a/src/languages/en.js b/src/languages/en.js
index b4b7769de..4cefa73c1 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -179,7 +179,7 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     // Start notification form
-    defaultNotificationName: "My {0} Alert ({1})",
+    defaultNotificationName: "My {notification} Alert ({number})",
     here: "here",
     "Required": "Required",
     "telegram": "Telegram",
@@ -194,7 +194,7 @@ export default {
     "Post URL": "Post URL",
     "Content Type": "Content Type",
     webhookJsonDesc: "{0} is good for any modern http servers such as express.js",
-    webhookFormDataDesc: "{0} is good for PHP, you just need to parse the json by {1}",
+    webhookFormDataDesc: "{multipart} is good for PHP, you just need to parse the json by {decodeFunction}",
     "smtp": "Email (SMTP)",
     secureOptionNone: "None / STARTTLS (25, 587)",
     secureOptionTLS: "TLS (465)",

From 8ae64843fcb2c9c5772bf47007486f4bb80f3564 Mon Sep 17 00:00:00 2001
From: Adam Stachowicz <adam.stachowicz@fingo.info>
Date: Mon, 4 Oct 2021 23:32:16 +0200
Subject: [PATCH 10/48] run update-language-files

---
 src/languages/bg-BG.js   | 18 ++++++++++++++++++
 src/languages/da-DK.js   | 17 +++++++++++++++++
 src/languages/de-DE.js   | 17 +++++++++++++++++
 src/languages/en.js      | 32 ++++++++++++++++----------------
 src/languages/es-ES.js   | 17 +++++++++++++++++
 src/languages/et-EE.js   | 35 ++++++++++++++++++++++++++---------
 src/languages/fa.js      | 17 +++++++++++++++++
 src/languages/fr-FR.js   | 17 +++++++++++++++++
 src/languages/hu.js      | 18 ++++++++++++++++++
 src/languages/it-IT.js   | 17 +++++++++++++++++
 src/languages/ja.js      | 17 +++++++++++++++++
 src/languages/ko-KR.js   | 17 +++++++++++++++++
 src/languages/nl-NL.js   | 17 +++++++++++++++++
 src/languages/pl.js      | 17 +++++++++++++++++
 src/languages/pt-BR.js   | 17 +++++++++++++++++
 src/languages/ru-RU.js   | 17 +++++++++++++++++
 src/languages/sr-latn.js | 17 +++++++++++++++++
 src/languages/sr.js      | 17 +++++++++++++++++
 src/languages/sv-SE.js   | 17 +++++++++++++++++
 src/languages/tr-TR.js   | 17 +++++++++++++++++
 src/languages/zh-CN.js   | 17 +++++++++++++++++
 src/languages/zh-HK.js   | 17 +++++++++++++++++
 22 files changed, 384 insertions(+), 25 deletions(-)

diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js
index fcf266063..ba910bb0a 100644
--- a/src/languages/bg-BG.js
+++ b/src/languages/bg-BG.js
@@ -178,4 +178,22 @@ export default {
     "Add a monitor": "Добави монитор",
     "Edit Status Page": "Редактирай статус страница",
     "Go to Dashboard": "Към Таблото",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/da-DK.js b/src/languages/da-DK.js
index b9ce20eca..8df3627b2 100644
--- a/src/languages/da-DK.js
+++ b/src/languages/da-DK.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Rediger Statusside",
     "Go to Dashboard": "Gå til Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index 96cd8a283..c4e903b24 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -179,4 +179,21 @@ export default {
     "Edit Status Page": "Bearbeite Statusseite",
     "Go to Dashboard": "Gehe zum Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/en.js b/src/languages/en.js
index f8c9e0f52..d5d61c225 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -178,22 +178,22 @@ export default {
     "Add a monitor": "Add a monitor",
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
-    "telegram": "Telegram",
-    "webhook": "Webhook",
-    "smtp": "Email (SMTP)",
-    "discord": "Discord",
-    "teams": "Microsoft Teams",
-    "signal": "Signal",
-    "gotify": "Gotify",
-    "slack": "Slack",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
     "rocket.chat": "Rocket.chat",
-    "pushover": "Pushover",
-    "pushy": "Pushy",
-    "octopush": "Octopush",
-    "lunasea": "LunaSea",
-    "apprise": "Apprise (Support 50+ Notification services)",
-    "pushbullet": "Pushbullet",
-    "line": "Line Messenger",
-    "mattermost": "Mattermost",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
     "Status Page": "Status Page",
 };
diff --git a/src/languages/es-ES.js b/src/languages/es-ES.js
index 366291db9..2558a6863 100644
--- a/src/languages/es-ES.js
+++ b/src/languages/es-ES.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/et-EE.js b/src/languages/et-EE.js
index e9c7a217e..8d98edae9 100644
--- a/src/languages/et-EE.js
+++ b/src/languages/et-EE.js
@@ -16,7 +16,7 @@ export default {
     rrtypeDescription: "Vali kirje tüüp, mida soovid jälgida.",
     pauseMonitorMsg: "Kas soovid peatada seire?",
     Settings: "Seaded",
-    "Status Page": "Ülevaade", // hääletuse tulemus, teine: seisundileht, kolmas: Olukord/Olek
+    "Status Page": "Status Page",
     Dashboard: "Töölaud",
     "New Update": "Uuem tarkvara versioon on saadaval.",
     Language: "Keel",
@@ -44,7 +44,7 @@ export default {
     Edit: "Muuda",
     Delete: "Eemalda",
     Current: "Hetkeseisund",
-    Uptime: "Eluiga", // todo: launchpad?
+    Uptime: "Eluiga",
     "Cert Exp.": "Sert. aegumine",
     days: "päeva",
     day: "päev",
@@ -109,7 +109,7 @@ export default {
     "Create your admin account": "Admininstraatori konto loomine",
     "Repeat Password": "korda salasõna",
     respTime: "Reageerimisaeg (ms)",
-    notAvailableShort: "N/A", // tõlkimata, umbkaudu rahvusvaheline termin peaks sobima piisavalt
+    notAvailableShort: "N/A",
     enableDefaultNotificationDescription: "Kõik järgnevalt lisatud seired kasutavad seda teavitusteenuset. Seiretelt võib teavitusteenuse ühekaupa eemaldada.",
     clearEventsMsg: "Kas soovid seire kõik sündmused kustutada?",
     clearHeartbeatsMsg: "Kas soovid seire kõik tuksed kustutada?",
@@ -122,7 +122,7 @@ export default {
     "Clear Data": "Eemalda andmed",
     Events: "Sündmused",
     Heartbeats: "Tuksed",
-    "Auto Get": "Hangi automaatselt", // hangi? kõlab liiga otsetõlge
+    "Auto Get": "Hangi automaatselt",
     backupDescription: "Varunda kõik seired ja teavitused JSON faili.",
     backupDescription2: "PS: Varukoopia EI sisalda seirete ajalugu ja sündmustikku.",
     backupDescription3: "Varukoopiad sisaldavad teavitusteenusete pääsuvõtmeid.",
@@ -140,7 +140,7 @@ export default {
     "Two Factor Authentication": "Kaksikautentimine",
     Active: "kasutusel",
     Inactive: "seadistamata",
-    Token: "kaksikautentimise kood", // needs to compensate for no title
+    Token: "kaksikautentimise kood",
     "Show URI": "Näita URId",
     "Clear all statistics": "Tühjenda ajalugu",
     importHandleDescription: "'kombineeri' täiendab varukoopiast ja kirjutab üle samanimelised seireid ja teavitusteenused; 'lisa praegustele' jätab olemasolevad puutumata; 'asenda' kustutab ja asendab kõik seired ja teavitusteenused.",
@@ -150,14 +150,14 @@ export default {
     "Export Backup": "Varukoopia eksportimine",
     "Skip existing": "lisa praegustele",
     Overwrite: "asenda",
-    Options: "Mestimisviis", // reusal of key would be chaos
+    Options: "Mestimisviis",
     "Keep both": "kombineeri",
     Tags: "Sildid",
     "Add New below or Select...": "Leia või lisa all uus…",
     "Tag with this name already exist.": "Selle nimega silt on juba olemas.",
     "Tag with this value already exist.": "Selle väärtusega silt on juba olemas.",
     color: "värvus",
-    "value (optional)": "väärtus (fakultatiivne)", // milline sõna!
+    "value (optional)": "väärtus (fakultatiivne)",
     Gray: "hall",
     Red: "punane",
     Orange: "oranž",
@@ -167,7 +167,7 @@ export default {
     Purple: "lilla",
     Pink: "roosa",
     "Search...": "Otsi…",
-    "Avg. Ping": "Keskmine ping", // pikk, aga nagunii kahel real
+    "Avg. Ping": "Keskmine ping",
     "Avg. Response": "Keskmine reaktsiooniaeg",
     "Entry Page": "Avaleht",
     statusPageNothing: "Kippu ega kõppu; siia saab lisada seireid või -gruppe.",
@@ -178,5 +178,22 @@ export default {
     "Add Group": "Lisa grupp",
     "Edit Status Page": "Muuda lehte",
     "Go to Dashboard": "Töölauale",
-    "Status Page": "Status Page",
+    checkEverySecond: "Check every {0} seconds.",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/fa.js b/src/languages/fa.js
index 154786668..100577f4d 100644
--- a/src/languages/fa.js
+++ b/src/languages/fa.js
@@ -187,4 +187,21 @@ export default {
     Last: "آخرین",
     Info: "اطلاعات",
     "Powered By": "نیرو گرفته از",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/fr-FR.js b/src/languages/fr-FR.js
index f10600775..6ef69f0a5 100644
--- a/src/languages/fr-FR.js
+++ b/src/languages/fr-FR.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Modifier la page de statut",
     "Go to Dashboard": "Accéder au tableau de bord",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/hu.js b/src/languages/hu.js
index 9d3dbf6c3..039cf6707 100644
--- a/src/languages/hu.js
+++ b/src/languages/hu.js
@@ -178,4 +178,22 @@ export default {
     "Add a monitor": "Figyelő hozzáadása",
     "Edit Status Page": "Sátusz oldal szerkesztése",
     "Go to Dashboard": "Menj az irányítópulthoz",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
+    "Status Page": "Status Page",
 };
diff --git a/src/languages/it-IT.js b/src/languages/it-IT.js
index 10c2df89b..b318c97e4 100644
--- a/src/languages/it-IT.js
+++ b/src/languages/it-IT.js
@@ -179,4 +179,21 @@ export default {
     "Edit Status Page": "Modifica pagina di stato",
     "Go to Dashboard": "Vai al Cruscotto",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/ja.js b/src/languages/ja.js
index 3462166ba..5042d18b9 100644
--- a/src/languages/ja.js
+++ b/src/languages/ja.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/ko-KR.js b/src/languages/ko-KR.js
index 881d609e0..6cb86ed20 100644
--- a/src/languages/ko-KR.js
+++ b/src/languages/ko-KR.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/nl-NL.js b/src/languages/nl-NL.js
index b55e259b6..1303a0960 100644
--- a/src/languages/nl-NL.js
+++ b/src/languages/nl-NL.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/pl.js b/src/languages/pl.js
index ad10ceb07..9a079a9ce 100644
--- a/src/languages/pl.js
+++ b/src/languages/pl.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edytuj stronę statusu",
     "Go to Dashboard": "Idź do panelu",
     "Status Page": "Strona statusu",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (obsługuje 50+ usług powiadamiania)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/pt-BR.js b/src/languages/pt-BR.js
index 4f65f6038..201314bb6 100644
--- a/src/languages/pt-BR.js
+++ b/src/languages/pt-BR.js
@@ -179,4 +179,21 @@ export default {
     "Add a monitor": "Adicionar um monitor",
     "Edit Status Page": "Editar Página de Status",
     "Go to Dashboard": "Ir para a dashboard",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js
index 18d97305f..a326a006f 100644
--- a/src/languages/ru-RU.js
+++ b/src/languages/ru-RU.js
@@ -184,4 +184,21 @@ export default {
     "Create Incident": "Создать инцидент",
     "Switch to Dark Theme": "Тёмная тема",
     "Switch to Light Theme": "Светлая тема",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/sr-latn.js b/src/languages/sr-latn.js
index 3542e1d6f..f249f568d 100644
--- a/src/languages/sr-latn.js
+++ b/src/languages/sr-latn.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/sr.js b/src/languages/sr.js
index 9cb1379e6..8ea651b74 100644
--- a/src/languages/sr.js
+++ b/src/languages/sr.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/sv-SE.js b/src/languages/sv-SE.js
index ef66aca5e..997d94a7a 100644
--- a/src/languages/sv-SE.js
+++ b/src/languages/sv-SE.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/tr-TR.js b/src/languages/tr-TR.js
index 908afe6dc..614ab0ee5 100644
--- a/src/languages/tr-TR.js
+++ b/src/languages/tr-TR.js
@@ -179,4 +179,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index a784cd34b..65e8909ac 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "编辑状态页",
     "Go to Dashboard": "前往仪表盘",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };
diff --git a/src/languages/zh-HK.js b/src/languages/zh-HK.js
index df3cc4165..3b12177d9 100644
--- a/src/languages/zh-HK.js
+++ b/src/languages/zh-HK.js
@@ -180,4 +180,21 @@ export default {
     "Edit Status Page": "Edit Status Page",
     "Go to Dashboard": "Go to Dashboard",
     "Status Page": "Status Page",
+    telegram: "Telegram",
+    webhook: "Webhook",
+    smtp: "Email (SMTP)",
+    discord: "Discord",
+    teams: "Microsoft Teams",
+    signal: "Signal",
+    gotify: "Gotify",
+    slack: "Slack",
+    "rocket.chat": "Rocket.chat",
+    pushover: "Pushover",
+    pushy: "Pushy",
+    octopush: "Octopush",
+    lunasea: "LunaSea",
+    apprise: "Apprise (Support 50+ Notification services)",
+    pushbullet: "Pushbullet",
+    line: "Line Messenger",
+    mattermost: "Mattermost",
 };

From 4da63c5fb88b0628639fe34b5aeef5621018cf73 Mon Sep 17 00:00:00 2001
From: Adam Stachowicz <adam.stachowicz@fingo.info>
Date: Mon, 4 Oct 2021 23:33:52 +0200
Subject: [PATCH 11/48] Revert `silentTranslationWarn` change

---
 src/i18n.js | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/i18n.js b/src/i18n.js
index 17fa2e91a..5c17475e1 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -3,9 +3,9 @@ import bgBG from "./languages/bg-BG";
 import daDK from "./languages/da-DK";
 import deDE from "./languages/de-DE";
 import en from "./languages/en";
-import fa from "./languages/fa";
 import esEs from "./languages/es-ES";
 import etEE from "./languages/et-EE";
+import fa from "./languages/fa";
 import frFR from "./languages/fr-FR";
 import hu from "./languages/hu";
 import itIT from "./languages/it-IT";
@@ -48,16 +48,17 @@ const languageList = {
 };
 
 const rtlLangs = ["fa"];
-    
+
 export const currentLocale = () => localStorage.locale || "en";
 
 export const localeDirection = () => {
-    return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr"
-}
+    return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr";
+};
+
 export const i18n = createI18n({
     locale: currentLocale(),
     fallbackLocale: "en",
     silentFallbackWarn: true,
-    silentTranslationWarn: process.env.NODE_ENV !== "development",
+    silentTranslationWarn: true,
     messages: languageList,
 });

From 707e05c330ae56b34f934e2e68cf0462f0638ad2 Mon Sep 17 00:00:00 2001
From: Adam Stachowicz <adam.stachowicz@fingo.info>
Date: Mon, 4 Oct 2021 23:39:56 +0200
Subject: [PATCH 12/48] Fix after merge

---
 src/languages/et-EE.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/languages/et-EE.js b/src/languages/et-EE.js
index 8d98edae9..7a660cb3f 100644
--- a/src/languages/et-EE.js
+++ b/src/languages/et-EE.js
@@ -16,7 +16,7 @@ export default {
     rrtypeDescription: "Vali kirje tüüp, mida soovid jälgida.",
     pauseMonitorMsg: "Kas soovid peatada seire?",
     Settings: "Seaded",
-    "Status Page": "Status Page",
+    "Status Page": "Ülevaade",
     Dashboard: "Töölaud",
     "New Update": "Uuem tarkvara versioon on saadaval.",
     Language: "Keel",

From 22227be408a6be84d98d745c12b38c08b03408f1 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Wed, 6 Oct 2021 02:06:59 +0800
Subject: [PATCH 13/48] update version in wiki too

---
 extra/update-version.js | 47 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 43 insertions(+), 4 deletions(-)

diff --git a/extra/update-version.js b/extra/update-version.js
index ca810a40a..2e3b42da8 100644
--- a/extra/update-version.js
+++ b/extra/update-version.js
@@ -19,6 +19,7 @@ if (! newVersion) {
 const exists = tagExists(newVersion);
 
 if (! exists) {
+
     // Process package.json
     pkg.version = newVersion;
     pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion);
@@ -29,8 +30,11 @@ if (! exists) {
 
     commit(newVersion);
     tag(newVersion);
+
+    updateWiki(oldVersion, newVersion);
+
 } else {
-    console.log("version exists")
+    console.log("version exists");
 }
 
 function commit(version) {
@@ -38,16 +42,16 @@ function commit(version) {
 
     let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]);
     let stdout = res.stdout.toString().trim();
-    console.log(stdout)
+    console.log(stdout);
 
     if (stdout.includes("no changes added to commit")) {
-        throw new Error("commit error")
+        throw new Error("commit error");
     }
 }
 
 function tag(version) {
     let res = child_process.spawnSync("git", ["tag", version]);
-    console.log(res.stdout.toString().trim())
+    console.log(res.stdout.toString().trim());
 }
 
 function tagExists(version) {
@@ -59,3 +63,38 @@ function tagExists(version) {
 
     return res.stdout.toString().trim() === version;
 }
+
+function updateWiki(oldVersion, newVersion) {
+    const wikiDir = "./tmp/wiki";
+    const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
+
+    safeDelete(wikiDir);
+
+    child_process.spawnSync("git", ["clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir]);
+    let content = fs.readFileSync(howToUpdateFilename).toString();
+    content = content.replaceAll(`git checkout ${oldVersion}`, `git checkout ${newVersion}`);
+    fs.writeFileSync(howToUpdateFilename, content);
+
+    child_process.spawnSync("git", ["add", "-A"], {
+        cwd: wikiDir,
+    });
+
+    child_process.spawnSync("git", ["commit", "-m", `Update to ${newVersion} from ${oldVersion}`], {
+        cwd: wikiDir,
+    });
+
+    console.log("Pushing to Github");
+    child_process.spawnSync("git", ["push"], {
+        cwd: wikiDir,
+    });
+
+    safeDelete(wikiDir);
+}
+
+function safeDelete(dir) {
+    if (fs.existsSync(dir)) {
+        fs.rmdirSync(dir, {
+            recursive: true,
+        });
+    }
+}

From 7a8b6a03e0159a0b1aab9ec649db41d560d8c33d Mon Sep 17 00:00:00 2001
From: Brainpitcher <53742575+Brainpitcher@users.noreply.github.com>
Date: Wed, 6 Oct 2021 09:49:44 +0500
Subject: [PATCH 14/48] Update ru-RU.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

"Cert Exp.": "Сертификат истекает" now it sounds in russian
---
 src/languages/ru-RU.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js
index 1686aec49..4e56900da 100644
--- a/src/languages/ru-RU.js
+++ b/src/languages/ru-RU.js
@@ -43,7 +43,7 @@ export default {
     Delete: "Удалить",
     Current: "Текущий",
     Uptime: "Аптайм",
-    "Cert Exp.": "Сертификат просрочен",
+    "Cert Exp.": "Сертификат истекает",
     days: "дней",
     day: "день",
     "-day": " дней",

From 4ded0c073af8a81b73d84bc439eb3442d02fc172 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Wed, 6 Oct 2021 13:26:43 +0800
Subject: [PATCH 15/48] [test] fix timeout issue

---
 package.json      | 2 +-
 test/test.spec.js | 8 ++------
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/package.json b/package.json
index 7202f20de..7558d6822 100644
--- a/package.json
+++ b/package.json
@@ -123,6 +123,6 @@
         },
         "testRegex": "./test/*.spec.js",
         "rootDir": ".",
-        "testTimeout": 15000
+        "testTimeout": 30000
     }
 }
diff --git a/test/test.spec.js b/test/test.spec.js
index 67505b7d2..2a45c37c3 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -75,14 +75,10 @@ describe("Init", () => {
         it("Change Theme", async () => {
             // Light
             await page.click(".btn[for=btncheck1]");
-            await page.waitForSelector("div.light", {
-                timeout: 2000
-            });
+            await page.waitForSelector("div.light");
 
             await page.click(".btn[for=btncheck2]");
-            await page.waitForSelector("div.dark", {
-                timeout: 2000
-            });
+            await page.waitForSelector("div.dark");
         });
     });
 

From 2b00e59c7a94cafce78d2dd94d6dd546fdf574be Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:25:00 +0800
Subject: [PATCH 16/48] [test] update

---
 test/test.spec.js | 165 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 152 insertions(+), 13 deletions(-)

diff --git a/test/test.spec.js b/test/test.spec.js
index 2a45c37c3..8077ec3d8 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -1,6 +1,7 @@
 // eslint-disable-next-line no-unused-vars
-const { Page } = require("puppeteer");
+const { Page, Browser } = require("puppeteer");
 const { sleep } = require("../src/util");
+const axios = require("axios");
 
 /**
  * Set back the correct data type for page object
@@ -8,14 +9,17 @@ const { sleep } = require("../src/util");
  */
 page;
 
-beforeAll(() => {
-    if (process.env.JUST_FOR_TEST) {
-        console.log(process.env.JUST_FOR_TEST);
+/**
+ * @type {Browser}
+ */
+browser;
 
-        if (process.env.JUST_FOR_TEST === "JUST_FOR_TEST_HELLO") {
-            console.log("secret ok");
-        }
-    }
+beforeAll(async () => {
+    await page.setViewport({
+        width: 1280,
+        height: 720,
+        deviceScaleFactor: 1,
+    });
 });
 
 afterAll(() => {
@@ -35,6 +39,7 @@ describe("Init", () => {
         await expect(page.title()).resolves.toMatch(title);
     });
 
+    // Setup Page
     it("Setup", async () => {
         // Create an Admin
         await page.waitForSelector("#floatingInput");
@@ -49,14 +54,17 @@ describe("Init", () => {
         // Go to /setup again
         await page.goto(baseURL + "/setup");
         await sleep(3000);
-        const pathname = await page.evaluate(() => location.pathname);
+        let pathname = await page.evaluate(() => location.pathname);
         expect(pathname).toEqual("/dashboard");
 
         // Go to /
         await page.goto(baseURL);
+        await sleep(3000);
+        pathname = await page.evaluate(() => location.pathname);
         expect(pathname).toEqual("/dashboard");
     });
 
+    // Settings Page
     describe("Settings", () => {
         beforeAll(async () => {
             await page.goto(baseURL + "/settings");
@@ -73,15 +81,128 @@ describe("Init", () => {
         });
 
         it("Change Theme", async () => {
-            // Light
-            await page.click(".btn[for=btncheck1]");
-            await page.waitForSelector("div.light");
+            await sleep(1000);
 
-            await page.click(".btn[for=btncheck2]");
+            // Dark
+            await click(page, ".btn[for=btncheck2]");
             await page.waitForSelector("div.dark");
+
+            await sleep(1000);
+
+            // Light
+            await click(page, ".btn[for=btncheck1]");
+            await page.waitForSelector("div.light");
         });
+
+        // TODO: Heartbeat Bar Style
+
+        // TODO: Timezone
+
+        it("Search Engine Visibility", async () => {
+            // Default
+            let res = await axios.get(baseURL + "/robots.txt");
+            expect(res.data).toMatch("Disallow: /");
+
+            // Yes
+            await click(page, "#searchEngineIndexYes");
+            await click(page, "form > div > .btn[type=submit]");
+            await sleep(2000);
+            res = await axios.get(baseURL + "/robots.txt");
+            expect(res.data).not.toMatch("Disallow: /");
+
+            // No
+            await click(page, "#searchEngineIndexNo");
+            await click(page, "form > div > .btn[type=submit]");
+            await sleep(2000);
+            res = await axios.get(baseURL + "/robots.txt");
+            expect(res.data).toMatch("Disallow: /");
+        });
+
+        it("Entry Page", async () => {
+            const newPage = await browser.newPage();
+
+            // Default
+            await newPage.goto(baseURL);
+            await sleep(3000);
+            let pathname = await newPage.evaluate(() => location.pathname);
+            expect(pathname).toEqual("/dashboard");
+
+            // Status Page
+            await click(page, "#entryPageNo");
+            await click(page, "form > div > .btn[type=submit]");
+            await sleep(2000);
+            await newPage.goto(baseURL);
+            await sleep(3000);
+            pathname = await newPage.evaluate(() => location.pathname);
+            expect(pathname).toEqual("/status");
+
+            // Back to Dashboard
+            await click(page, "#entryPageYes");
+            await click(page, "form > div > .btn[type=submit]");
+            await sleep(2000);
+            await newPage.goto(baseURL);
+            await sleep(3000);
+            pathname = await newPage.evaluate(() => location.pathname);
+            expect(pathname).toEqual("/dashboard");
+
+            await newPage.close();
+        });
+
+        it("Change Password (wrong current password)", async () => {
+            await page.type("#current-password", "wrong_passw$$d");
+            await page.type("#new-password", "new_password123");
+            await page.type("#repeat-new-password", "new_password123");
+            await click(page, "form > div > .btn[type=submit]", 1);
+            await sleep(3000);
+            await click(page, ".btn-danger.btn.me-1");
+            await sleep(2000);
+            await login("admin", "new_password123");
+            await sleep(2000);
+            let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
+            expect(elementCount).toEqual(1);
+
+            await login("admin", "admin123");
+            await sleep(3000);
+        });
+
+        it("Change Password (wrong repeat)", async () => {
+            await page.type("#current-password", "admin123");
+            await page.type("#new-password", "new_password123");
+            await page.type("#repeat-new-password", "new_password1234567898797898");
+            await click(page, "form > div > .btn[type=submit]", 1);
+            await sleep(3000);
+            await click(page, ".btn-danger.btn.me-1");
+            await sleep(2000);
+            await login("admin", "new_password123");
+            await sleep(2000);
+            let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
+            expect(elementCount).toEqual(1);
+
+            await login("admin", "admin123");
+            await sleep(3000);
+        });
+
+        // TODO: 2FA
+
+        // TODO: Export Backup
+
+        // TODO: Import Backup
+
+        // TODO: Disable Auth
+
+        // TODO: Clear Stats
     });
 
+    /*
+     * TODO
+     * Create Monitor - All type
+     * Edit Monitor
+     * Delete Monitor
+     *
+     * Create Notification (token problem, maybe hard to test)
+     *
+     */
+
     describe("Status Page", () => {
         const title = "Uptime Kuma";
         beforeAll(async () => {
@@ -93,3 +214,21 @@ describe("Init", () => {
     });
 });
 
+async function login(username, password) {
+    await input(page, "#floatingInput", username);
+    await input(page, "#floatingPassword", password);
+    await page.click(".btn-primary[type=submit]");
+}
+
+async function click(page, selector, elementIndex = 0) {
+    return await page.evaluate((s, i) => {
+        return document.querySelectorAll(s)[i].click();
+    }, selector, elementIndex);
+}
+
+async function input(page, selector, text) {
+    const element = await page.$(selector);
+    await element.click({ clickCount: 3 });
+    await page.keyboard.press("Backspace");
+    await page.type(selector, text);
+}

From cb263f2a08861d0a4ba3094c019ebdc313762c71 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:34:57 +0800
Subject: [PATCH 17/48] [test] wait for #language

---
 test/test.spec.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/test/test.spec.js b/test/test.spec.js
index 8077ec3d8..e1fd28904 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -71,6 +71,8 @@ describe("Init", () => {
         });
 
         it("Change Language", async () => {
+            await page.waitForSelector("#language");
+
             await page.select("#language", "zh-HK");
             let languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText);
             expect(languageTitle).toMatch("語言");

From 54d78308133c1089240b7b08e3c3af398ab16176 Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:36:45 +0800
Subject: [PATCH 18/48] Update CONTRIBUTING.md

---
 CONTRIBUTING.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 562b8cd38..93b03ac6a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -38,6 +38,7 @@ If you are not sure, feel free to create an empty pull request draft first.
 - Add a new notification
 - Add a chart
 - Fix a bug
+- Translations
 
 ### *️⃣ Requires one more reviewer
 
@@ -172,3 +173,7 @@ npm install
 Since previously updating vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only. 
 
 Patch release = the third digit
+
+# Translations
+
+Please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages

From 54d2fbcc022431baad5f0c7883ad63ae8f89d310 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Wed, 6 Oct 2021 18:21:31 +0800
Subject: [PATCH 19/48] Fix i18n

Prevent use esm-bundler build vue-i18n
Escape keyword:  '@'
---
 src/i18n.js         | 2 +-
 src/languages/en.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/i18n.js b/src/i18n.js
index 5c17475e1..755103970 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1,4 +1,4 @@
-import { createI18n } from "vue-i18n";
+import { createI18n } from "vue-i18n/index";
 import bgBG from "./languages/bg-BG";
 import daDK from "./languages/da-DK";
 import deDE from "./languages/de-DE";
diff --git a/src/languages/en.js b/src/languages/en.js
index 6d91d2b5d..3395bb30c 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -209,7 +209,7 @@ export default {
     wayToGetDiscordURL: "You can get this by going to Server Settings -> Integrations -> Create Webhook",
     "Bot Display Name": "Bot Display Name",
     "Prefix Custom Message": "Prefix Custom Message",
-    "Hello @everyone is...": "Hello @everyone is...",
+    "Hello @everyone is...": "Hello {'@'}everyone is...",
     "teams": "Microsoft Teams",
     "Webhook URL": "Webhook URL",
     wayToGetTeamsURL: "You can learn how to create a webhook url {0}.",

From 7002a778f06c9fc52773a969cfd3ff19c90448a9 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Wed, 6 Oct 2021 19:12:23 +0800
Subject: [PATCH 20/48] Rollback vue-i18n version to 9.1.7

---
 package-lock.json | 170 +++++++++++++++++++++++-----------------------
 package.json      |   2 +-
 2 files changed, 86 insertions(+), 86 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 628ccd3a9..6a1e55db8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48,7 +48,7 @@
                 "vue-chart-3": "~0.5.8",
                 "vue-confirm-dialog": "~1.0.2",
                 "vue-contenteditable": "~3.0.4",
-                "vue-i18n": "~9.1.8",
+                "vue-i18n": "9.1.7",
                 "vue-image-crop-upload": "~3.0.3",
                 "vue-multiselect": "~3.0.0-alpha.2",
                 "vue-qrcode": "~1.0.0",
@@ -844,39 +844,39 @@
             "dev": true
         },
         "node_modules/@intlify/core-base": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.8.tgz",
-            "integrity": "sha512-k+q6nUOD9HPTMr8AKEn4d/EgoNSCMVuc/dG97tFXrifT4+QbxoLNRjXovyC60rb4q+7D/cGF+5R6Tjby4t5gng==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.7.tgz",
+            "integrity": "sha512-q1W2j81xbHyfKrNcca/CeJyf0Bcx4u9UDu05l7AaiJbqOseTme2o2I3wp1hDDCtmC7k7HgX0sAygyHNJH9swuQ==",
             "dependencies": {
-                "@intlify/devtools-if": "9.1.8",
-                "@intlify/message-compiler": "9.1.8",
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/runtime": "9.1.8",
-                "@intlify/shared": "9.1.8",
-                "@intlify/vue-devtools": "9.1.8"
+                "@intlify/devtools-if": "9.1.7",
+                "@intlify/message-compiler": "9.1.7",
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/runtime": "9.1.7",
+                "@intlify/shared": "9.1.7",
+                "@intlify/vue-devtools": "9.1.7"
             },
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/devtools-if": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.8.tgz",
-            "integrity": "sha512-17REiNoQ5dWnYECbKgkMHtX8GnNCkMNxfImXrJNnUhSH3GitheNkYn0o14AZmZWk7Fw/1IOdV5v7K0QVnYx5fw==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.7.tgz",
+            "integrity": "sha512-/DcN5FUySSkQhDqx5y1RvxfuCXO3Ot/dUEIOs472qbM7Hyb2qif+eXCnwHBzlI4+wEfQVT6L0PiM1a7Er/ro9g==",
             "dependencies": {
-                "@intlify/shared": "9.1.8"
+                "@intlify/shared": "9.1.7"
             },
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/message-compiler": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.8.tgz",
-            "integrity": "sha512-x/vfvHqz2v/ngE0slaD/QnJORHzrlKt6p6TdoY+/1zBQ/TOH+6BlkdtrSIrSfwJfP+3Qe6C8uw6yJknQ6cpabA==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.7.tgz",
+            "integrity": "sha512-JZNkAhr3O7tnbdbRBcpYfqr/Ai26WTzX0K/lV8Y1KVdOIj/dGiamaffdWUdFiDXUnbJRNbPiOaKxy7Pwip3KxQ==",
             "dependencies": {
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/shared": "9.1.8",
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/shared": "9.1.7",
                 "source-map": "0.6.1"
             },
             "engines": {
@@ -884,42 +884,42 @@
             }
         },
         "node_modules/@intlify/message-resolver": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.8.tgz",
-            "integrity": "sha512-4tHBo5U2/oDG85tNv9z8bS/5ThMw+wADPLyBVOLplUqw8Q+N5tkrTL23Pa8hg5Ekd2crvyIxFHFwt1gbT8TT6w==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.7.tgz",
+            "integrity": "sha512-WTK+OaXJYjyquLGhuCyDvU2WHkG+kXzXeHagmVFHn+s118Jf2143zzkLLUrapP5CtZ/csuyjmYg7b3xQRQAmvw==",
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/runtime": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.8.tgz",
-            "integrity": "sha512-Q9WvSjRFhxxCen5cj3jOZEKAYlXjYZ+wZbTEfBQhDtcBwrS7xd9tyFos4ZRNNvF7G0H0sNDzXmSdZkoCpoU0iA==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.7.tgz",
+            "integrity": "sha512-QURPSlzhOVnRwS2XMGpCDsDkP42kfVBh94aAORxh/gVGzdgJip2vagrIFij/J69aEqdB476WJkMhVjP8VSHmiA==",
             "dependencies": {
-                "@intlify/message-compiler": "9.1.8",
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/shared": "9.1.8"
+                "@intlify/message-compiler": "9.1.7",
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/shared": "9.1.7"
             },
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/shared": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.8.tgz",
-            "integrity": "sha512-o9nksOx3yIMDNvYzcPv87NR+U62ka775/Ufjl3U2g4NsMORN8+VacbVJ/oAF6CYfzZALpArBBZdk5jafzcLkvw==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.7.tgz",
+            "integrity": "sha512-zt0zlUdalumvT9AjQNxPXA36UgOndUyvBMplh8uRZU0fhWHAwhnJTcf0NaG9Qvr8I1n3HPSs96+kLb/YdwTavQ==",
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/vue-devtools": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.8.tgz",
-            "integrity": "sha512-SypF7tpWFxIQzKrqv6O8JIk5xrU53E0/xNVR5LFCHLIDIKe62uMTrzXvjW5zoYcJjRTZ87BZLXEM1n2CaLSBsg==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.7.tgz",
+            "integrity": "sha512-DI5Wc0aOiohtBUGUkKAcryCWbbuaO4/PK4Pa/LaNCsFNxbtgR5qkIDmhBv9xVPYGTUhySXxaDDAMvOpBjhPJjw==",
             "dependencies": {
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/runtime": "9.1.8",
-                "@intlify/shared": "9.1.8"
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/runtime": "9.1.7",
+                "@intlify/shared": "9.1.7"
             },
             "engines": {
                 "node": ">= 10"
@@ -10207,13 +10207,13 @@
             }
         },
         "node_modules/vue-i18n": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.8.tgz",
-            "integrity": "sha512-Gmjkt/4ZQtKyCLTjJNGVp+w/dhMpuwuPtgW/Xm9DY0ppSwHRgqMRR6GspnCIYWBxQjzsTT8tIWLPc3SUfKifLA==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.7.tgz",
+            "integrity": "sha512-ujuuDanoHqtEd4GejWrbG/fXE9nrP51ElsEGxp0WBHfv+/ki0/wyUqkO+4fLikki2obGtXdviTPH0VNpas5K6g==",
             "dependencies": {
-                "@intlify/core-base": "9.1.8",
-                "@intlify/shared": "9.1.8",
-                "@intlify/vue-devtools": "9.1.8",
+                "@intlify/core-base": "9.1.7",
+                "@intlify/shared": "9.1.7",
+                "@intlify/vue-devtools": "9.1.7",
                 "@vue/devtools-api": "^6.0.0-beta.7"
             },
             "engines": {
@@ -11229,64 +11229,64 @@
             "dev": true
         },
         "@intlify/core-base": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.8.tgz",
-            "integrity": "sha512-k+q6nUOD9HPTMr8AKEn4d/EgoNSCMVuc/dG97tFXrifT4+QbxoLNRjXovyC60rb4q+7D/cGF+5R6Tjby4t5gng==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.7.tgz",
+            "integrity": "sha512-q1W2j81xbHyfKrNcca/CeJyf0Bcx4u9UDu05l7AaiJbqOseTme2o2I3wp1hDDCtmC7k7HgX0sAygyHNJH9swuQ==",
             "requires": {
-                "@intlify/devtools-if": "9.1.8",
-                "@intlify/message-compiler": "9.1.8",
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/runtime": "9.1.8",
-                "@intlify/shared": "9.1.8",
-                "@intlify/vue-devtools": "9.1.8"
+                "@intlify/devtools-if": "9.1.7",
+                "@intlify/message-compiler": "9.1.7",
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/runtime": "9.1.7",
+                "@intlify/shared": "9.1.7",
+                "@intlify/vue-devtools": "9.1.7"
             }
         },
         "@intlify/devtools-if": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.8.tgz",
-            "integrity": "sha512-17REiNoQ5dWnYECbKgkMHtX8GnNCkMNxfImXrJNnUhSH3GitheNkYn0o14AZmZWk7Fw/1IOdV5v7K0QVnYx5fw==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.7.tgz",
+            "integrity": "sha512-/DcN5FUySSkQhDqx5y1RvxfuCXO3Ot/dUEIOs472qbM7Hyb2qif+eXCnwHBzlI4+wEfQVT6L0PiM1a7Er/ro9g==",
             "requires": {
-                "@intlify/shared": "9.1.8"
+                "@intlify/shared": "9.1.7"
             }
         },
         "@intlify/message-compiler": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.8.tgz",
-            "integrity": "sha512-x/vfvHqz2v/ngE0slaD/QnJORHzrlKt6p6TdoY+/1zBQ/TOH+6BlkdtrSIrSfwJfP+3Qe6C8uw6yJknQ6cpabA==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.7.tgz",
+            "integrity": "sha512-JZNkAhr3O7tnbdbRBcpYfqr/Ai26WTzX0K/lV8Y1KVdOIj/dGiamaffdWUdFiDXUnbJRNbPiOaKxy7Pwip3KxQ==",
             "requires": {
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/shared": "9.1.8",
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/shared": "9.1.7",
                 "source-map": "0.6.1"
             }
         },
         "@intlify/message-resolver": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.8.tgz",
-            "integrity": "sha512-4tHBo5U2/oDG85tNv9z8bS/5ThMw+wADPLyBVOLplUqw8Q+N5tkrTL23Pa8hg5Ekd2crvyIxFHFwt1gbT8TT6w=="
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.7.tgz",
+            "integrity": "sha512-WTK+OaXJYjyquLGhuCyDvU2WHkG+kXzXeHagmVFHn+s118Jf2143zzkLLUrapP5CtZ/csuyjmYg7b3xQRQAmvw=="
         },
         "@intlify/runtime": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.8.tgz",
-            "integrity": "sha512-Q9WvSjRFhxxCen5cj3jOZEKAYlXjYZ+wZbTEfBQhDtcBwrS7xd9tyFos4ZRNNvF7G0H0sNDzXmSdZkoCpoU0iA==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.7.tgz",
+            "integrity": "sha512-QURPSlzhOVnRwS2XMGpCDsDkP42kfVBh94aAORxh/gVGzdgJip2vagrIFij/J69aEqdB476WJkMhVjP8VSHmiA==",
             "requires": {
-                "@intlify/message-compiler": "9.1.8",
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/shared": "9.1.8"
+                "@intlify/message-compiler": "9.1.7",
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/shared": "9.1.7"
             }
         },
         "@intlify/shared": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.8.tgz",
-            "integrity": "sha512-o9nksOx3yIMDNvYzcPv87NR+U62ka775/Ufjl3U2g4NsMORN8+VacbVJ/oAF6CYfzZALpArBBZdk5jafzcLkvw=="
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.7.tgz",
+            "integrity": "sha512-zt0zlUdalumvT9AjQNxPXA36UgOndUyvBMplh8uRZU0fhWHAwhnJTcf0NaG9Qvr8I1n3HPSs96+kLb/YdwTavQ=="
         },
         "@intlify/vue-devtools": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.8.tgz",
-            "integrity": "sha512-SypF7tpWFxIQzKrqv6O8JIk5xrU53E0/xNVR5LFCHLIDIKe62uMTrzXvjW5zoYcJjRTZ87BZLXEM1n2CaLSBsg==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.7.tgz",
+            "integrity": "sha512-DI5Wc0aOiohtBUGUkKAcryCWbbuaO4/PK4Pa/LaNCsFNxbtgR5qkIDmhBv9xVPYGTUhySXxaDDAMvOpBjhPJjw==",
             "requires": {
-                "@intlify/message-resolver": "9.1.8",
-                "@intlify/runtime": "9.1.8",
-                "@intlify/shared": "9.1.8"
+                "@intlify/message-resolver": "9.1.7",
+                "@intlify/runtime": "9.1.7",
+                "@intlify/shared": "9.1.7"
             }
         },
         "@istanbuljs/load-nyc-config": {
@@ -18469,13 +18469,13 @@
             }
         },
         "vue-i18n": {
-            "version": "9.1.8",
-            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.8.tgz",
-            "integrity": "sha512-Gmjkt/4ZQtKyCLTjJNGVp+w/dhMpuwuPtgW/Xm9DY0ppSwHRgqMRR6GspnCIYWBxQjzsTT8tIWLPc3SUfKifLA==",
+            "version": "9.1.7",
+            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.7.tgz",
+            "integrity": "sha512-ujuuDanoHqtEd4GejWrbG/fXE9nrP51ElsEGxp0WBHfv+/ki0/wyUqkO+4fLikki2obGtXdviTPH0VNpas5K6g==",
             "requires": {
-                "@intlify/core-base": "9.1.8",
-                "@intlify/shared": "9.1.8",
-                "@intlify/vue-devtools": "9.1.8",
+                "@intlify/core-base": "9.1.7",
+                "@intlify/shared": "9.1.7",
+                "@intlify/vue-devtools": "9.1.7",
                 "@vue/devtools-api": "^6.0.0-beta.7"
             }
         },
diff --git a/package.json b/package.json
index 7558d6822..619189709 100644
--- a/package.json
+++ b/package.json
@@ -87,7 +87,7 @@
         "vue-chart-3": "~0.5.8",
         "vue-confirm-dialog": "~1.0.2",
         "vue-contenteditable": "~3.0.4",
-        "vue-i18n": "~9.1.8",
+        "vue-i18n": "9.1.7",
         "vue-image-crop-upload": "~3.0.3",
         "vue-multiselect": "~3.0.0-alpha.2",
         "vue-qrcode": "~1.0.0",

From 7f1edb49bca56ca726186ed501e4133278b42294 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Thu, 7 Oct 2021 00:04:13 +0800
Subject: [PATCH 21/48] Fix i18n

Upgrade vue-i18n to 9.1.9.
Fix wrong tag name.
---
 package-lock.json                           | 170 ++++++++++----------
 package.json                                |   2 +-
 src/components/notifications/RocketChat.vue |   4 +-
 src/components/notifications/Slack.vue      |   4 +-
 4 files changed, 90 insertions(+), 90 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 6a1e55db8..a4625c3e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48,7 +48,7 @@
                 "vue-chart-3": "~0.5.8",
                 "vue-confirm-dialog": "~1.0.2",
                 "vue-contenteditable": "~3.0.4",
-                "vue-i18n": "9.1.7",
+                "vue-i18n": "~9.1.9",
                 "vue-image-crop-upload": "~3.0.3",
                 "vue-multiselect": "~3.0.0-alpha.2",
                 "vue-qrcode": "~1.0.0",
@@ -844,39 +844,39 @@
             "dev": true
         },
         "node_modules/@intlify/core-base": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.7.tgz",
-            "integrity": "sha512-q1W2j81xbHyfKrNcca/CeJyf0Bcx4u9UDu05l7AaiJbqOseTme2o2I3wp1hDDCtmC7k7HgX0sAygyHNJH9swuQ==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.9.tgz",
+            "integrity": "sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==",
             "dependencies": {
-                "@intlify/devtools-if": "9.1.7",
-                "@intlify/message-compiler": "9.1.7",
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/runtime": "9.1.7",
-                "@intlify/shared": "9.1.7",
-                "@intlify/vue-devtools": "9.1.7"
+                "@intlify/devtools-if": "9.1.9",
+                "@intlify/message-compiler": "9.1.9",
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/runtime": "9.1.9",
+                "@intlify/shared": "9.1.9",
+                "@intlify/vue-devtools": "9.1.9"
             },
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/devtools-if": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.7.tgz",
-            "integrity": "sha512-/DcN5FUySSkQhDqx5y1RvxfuCXO3Ot/dUEIOs472qbM7Hyb2qif+eXCnwHBzlI4+wEfQVT6L0PiM1a7Er/ro9g==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.9.tgz",
+            "integrity": "sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==",
             "dependencies": {
-                "@intlify/shared": "9.1.7"
+                "@intlify/shared": "9.1.9"
             },
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/message-compiler": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.7.tgz",
-            "integrity": "sha512-JZNkAhr3O7tnbdbRBcpYfqr/Ai26WTzX0K/lV8Y1KVdOIj/dGiamaffdWUdFiDXUnbJRNbPiOaKxy7Pwip3KxQ==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.9.tgz",
+            "integrity": "sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==",
             "dependencies": {
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/shared": "9.1.7",
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/shared": "9.1.9",
                 "source-map": "0.6.1"
             },
             "engines": {
@@ -884,42 +884,42 @@
             }
         },
         "node_modules/@intlify/message-resolver": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.7.tgz",
-            "integrity": "sha512-WTK+OaXJYjyquLGhuCyDvU2WHkG+kXzXeHagmVFHn+s118Jf2143zzkLLUrapP5CtZ/csuyjmYg7b3xQRQAmvw==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.9.tgz",
+            "integrity": "sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==",
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/runtime": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.7.tgz",
-            "integrity": "sha512-QURPSlzhOVnRwS2XMGpCDsDkP42kfVBh94aAORxh/gVGzdgJip2vagrIFij/J69aEqdB476WJkMhVjP8VSHmiA==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.9.tgz",
+            "integrity": "sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==",
             "dependencies": {
-                "@intlify/message-compiler": "9.1.7",
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/shared": "9.1.7"
+                "@intlify/message-compiler": "9.1.9",
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/shared": "9.1.9"
             },
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/shared": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.7.tgz",
-            "integrity": "sha512-zt0zlUdalumvT9AjQNxPXA36UgOndUyvBMplh8uRZU0fhWHAwhnJTcf0NaG9Qvr8I1n3HPSs96+kLb/YdwTavQ==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.9.tgz",
+            "integrity": "sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==",
             "engines": {
                 "node": ">= 10"
             }
         },
         "node_modules/@intlify/vue-devtools": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.7.tgz",
-            "integrity": "sha512-DI5Wc0aOiohtBUGUkKAcryCWbbuaO4/PK4Pa/LaNCsFNxbtgR5qkIDmhBv9xVPYGTUhySXxaDDAMvOpBjhPJjw==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz",
+            "integrity": "sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==",
             "dependencies": {
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/runtime": "9.1.7",
-                "@intlify/shared": "9.1.7"
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/runtime": "9.1.9",
+                "@intlify/shared": "9.1.9"
             },
             "engines": {
                 "node": ">= 10"
@@ -10207,13 +10207,13 @@
             }
         },
         "node_modules/vue-i18n": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.7.tgz",
-            "integrity": "sha512-ujuuDanoHqtEd4GejWrbG/fXE9nrP51ElsEGxp0WBHfv+/ki0/wyUqkO+4fLikki2obGtXdviTPH0VNpas5K6g==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.9.tgz",
+            "integrity": "sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==",
             "dependencies": {
-                "@intlify/core-base": "9.1.7",
-                "@intlify/shared": "9.1.7",
-                "@intlify/vue-devtools": "9.1.7",
+                "@intlify/core-base": "9.1.9",
+                "@intlify/shared": "9.1.9",
+                "@intlify/vue-devtools": "9.1.9",
                 "@vue/devtools-api": "^6.0.0-beta.7"
             },
             "engines": {
@@ -11229,64 +11229,64 @@
             "dev": true
         },
         "@intlify/core-base": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.7.tgz",
-            "integrity": "sha512-q1W2j81xbHyfKrNcca/CeJyf0Bcx4u9UDu05l7AaiJbqOseTme2o2I3wp1hDDCtmC7k7HgX0sAygyHNJH9swuQ==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.9.tgz",
+            "integrity": "sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==",
             "requires": {
-                "@intlify/devtools-if": "9.1.7",
-                "@intlify/message-compiler": "9.1.7",
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/runtime": "9.1.7",
-                "@intlify/shared": "9.1.7",
-                "@intlify/vue-devtools": "9.1.7"
+                "@intlify/devtools-if": "9.1.9",
+                "@intlify/message-compiler": "9.1.9",
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/runtime": "9.1.9",
+                "@intlify/shared": "9.1.9",
+                "@intlify/vue-devtools": "9.1.9"
             }
         },
         "@intlify/devtools-if": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.7.tgz",
-            "integrity": "sha512-/DcN5FUySSkQhDqx5y1RvxfuCXO3Ot/dUEIOs472qbM7Hyb2qif+eXCnwHBzlI4+wEfQVT6L0PiM1a7Er/ro9g==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.9.tgz",
+            "integrity": "sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==",
             "requires": {
-                "@intlify/shared": "9.1.7"
+                "@intlify/shared": "9.1.9"
             }
         },
         "@intlify/message-compiler": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.7.tgz",
-            "integrity": "sha512-JZNkAhr3O7tnbdbRBcpYfqr/Ai26WTzX0K/lV8Y1KVdOIj/dGiamaffdWUdFiDXUnbJRNbPiOaKxy7Pwip3KxQ==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.9.tgz",
+            "integrity": "sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==",
             "requires": {
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/shared": "9.1.7",
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/shared": "9.1.9",
                 "source-map": "0.6.1"
             }
         },
         "@intlify/message-resolver": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.7.tgz",
-            "integrity": "sha512-WTK+OaXJYjyquLGhuCyDvU2WHkG+kXzXeHagmVFHn+s118Jf2143zzkLLUrapP5CtZ/csuyjmYg7b3xQRQAmvw=="
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.9.tgz",
+            "integrity": "sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA=="
         },
         "@intlify/runtime": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.7.tgz",
-            "integrity": "sha512-QURPSlzhOVnRwS2XMGpCDsDkP42kfVBh94aAORxh/gVGzdgJip2vagrIFij/J69aEqdB476WJkMhVjP8VSHmiA==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.9.tgz",
+            "integrity": "sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==",
             "requires": {
-                "@intlify/message-compiler": "9.1.7",
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/shared": "9.1.7"
+                "@intlify/message-compiler": "9.1.9",
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/shared": "9.1.9"
             }
         },
         "@intlify/shared": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.7.tgz",
-            "integrity": "sha512-zt0zlUdalumvT9AjQNxPXA36UgOndUyvBMplh8uRZU0fhWHAwhnJTcf0NaG9Qvr8I1n3HPSs96+kLb/YdwTavQ=="
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.9.tgz",
+            "integrity": "sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw=="
         },
         "@intlify/vue-devtools": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.7.tgz",
-            "integrity": "sha512-DI5Wc0aOiohtBUGUkKAcryCWbbuaO4/PK4Pa/LaNCsFNxbtgR5qkIDmhBv9xVPYGTUhySXxaDDAMvOpBjhPJjw==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz",
+            "integrity": "sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==",
             "requires": {
-                "@intlify/message-resolver": "9.1.7",
-                "@intlify/runtime": "9.1.7",
-                "@intlify/shared": "9.1.7"
+                "@intlify/message-resolver": "9.1.9",
+                "@intlify/runtime": "9.1.9",
+                "@intlify/shared": "9.1.9"
             }
         },
         "@istanbuljs/load-nyc-config": {
@@ -18469,13 +18469,13 @@
             }
         },
         "vue-i18n": {
-            "version": "9.1.7",
-            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.7.tgz",
-            "integrity": "sha512-ujuuDanoHqtEd4GejWrbG/fXE9nrP51ElsEGxp0WBHfv+/ki0/wyUqkO+4fLikki2obGtXdviTPH0VNpas5K6g==",
+            "version": "9.1.9",
+            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.9.tgz",
+            "integrity": "sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==",
             "requires": {
-                "@intlify/core-base": "9.1.7",
-                "@intlify/shared": "9.1.7",
-                "@intlify/vue-devtools": "9.1.7",
+                "@intlify/core-base": "9.1.9",
+                "@intlify/shared": "9.1.9",
+                "@intlify/vue-devtools": "9.1.9",
                 "@vue/devtools-api": "^6.0.0-beta.7"
             }
         },
diff --git a/package.json b/package.json
index 619189709..a1a0bb259 100644
--- a/package.json
+++ b/package.json
@@ -87,7 +87,7 @@
         "vue-chart-3": "~0.5.8",
         "vue-confirm-dialog": "~1.0.2",
         "vue-contenteditable": "~3.0.4",
-        "vue-i18n": "9.1.7",
+        "vue-i18n": "~9.1.9",
         "vue-image-crop-upload": "~3.0.3",
         "vue-multiselect": "~3.0.0-alpha.2",
         "vue-qrcode": "~1.0.0",
diff --git a/src/components/notifications/RocketChat.vue b/src/components/notifications/RocketChat.vue
index 8f8082101..465b0dc9d 100644
--- a/src/components/notifications/RocketChat.vue
+++ b/src/components/notifications/RocketChat.vue
@@ -12,9 +12,9 @@
         <input id="rocket-button" v-model="$parent.notification.rocketbutton" type="text" class="form-control">
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
-            <i18-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
                 <a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a>
-            </i18-t>
+            </i18n-t>
             <p style="margin-top: 8px;">
                 {{ $t("aboutChannelName", [$t("rocket.chat")]) }}
             </p>
diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue
index 8675922be..0e1e7cb08 100644
--- a/src/components/notifications/Slack.vue
+++ b/src/components/notifications/Slack.vue
@@ -12,9 +12,9 @@
         <input id="slack-button" v-model="$parent.notification.slackbutton" type="text" class="form-control">
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
-            <i18-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
+            <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
                 <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
-            </i18-t>
+            </i18n-t>
             <p style="margin-top: 8px;">
                 {{ $t("aboutChannelName", [$t("slack")]) }}
             </p>

From 7626e1f2e4e38dbcbc21ea2bce6dd38e17e6c42e Mon Sep 17 00:00:00 2001
From: jtagcat <git-12dbd862@jtag.cat>
Date: Wed, 6 Oct 2021 21:02:34 +0300
Subject: [PATCH 22/48] l10n: update et

---
 src/languages/et-EE.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/languages/et-EE.js b/src/languages/et-EE.js
index 7a660cb3f..a6be8232e 100644
--- a/src/languages/et-EE.js
+++ b/src/languages/et-EE.js
@@ -178,10 +178,10 @@ export default {
     "Add Group": "Lisa grupp",
     "Edit Status Page": "Muuda lehte",
     "Go to Dashboard": "Töölauale",
-    checkEverySecond: "Check every {0} seconds.",
+    checkEverySecond: "Kontrolli peale tõrget {0} sekundilise vahega.",
     telegram: "Telegram",
     webhook: "Webhook",
-    smtp: "Email (SMTP)",
+    smtp: "elektronpost (SMTP)",
     discord: "Discord",
     teams: "Microsoft Teams",
     signal: "Signal",
@@ -192,8 +192,8 @@ export default {
     pushy: "Pushy",
     octopush: "Octopush",
     lunasea: "LunaSea",
-    apprise: "Apprise (Support 50+ Notification services)",
+    apprise: "Apprise (vahendab üle 65 teavitusteenust)",
     pushbullet: "Pushbullet",
-    line: "Line Messenger",
+    line: "LINE",
     mattermost: "Mattermost",
 };

From a87595a849dd4ff0151793e0cfb08ea86b695325 Mon Sep 17 00:00:00 2001
From: Nelson Chan <chakflying@hotmail.com>
Date: Thu, 7 Oct 2021 03:29:42 +0800
Subject: [PATCH 23/48] Fix: Allow underscore in hostname

---
 src/pages/EditMonitor.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 96f221e8b..6a72dc82e 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -256,7 +256,7 @@ export default {
             // Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
             ipRegexPattern: "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))",
             // Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
-            hostnameRegexPattern: "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"
+            hostnameRegexPattern: "^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$"
         };
     },
 

From 0c6b434d79d9fd212e85dd0cdebdc432eaa1b12b Mon Sep 17 00:00:00 2001
From: MrEddX <66828538+MrEddX@users.noreply.github.com>
Date: Thu, 7 Oct 2021 08:00:53 +0300
Subject: [PATCH 24/48] Moved Bulgarian to the Cyrillic family languages

---
 src/i18n.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/i18n.js b/src/i18n.js
index 5c17475e1..c3e1eca59 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1,5 +1,4 @@
 import { createI18n } from "vue-i18n";
-import bgBG from "./languages/bg-BG";
 import daDK from "./languages/da-DK";
 import deDE from "./languages/de-DE";
 import en from "./languages/en";
@@ -14,6 +13,7 @@ import koKR from "./languages/ko-KR";
 import nlNL from "./languages/nl-NL";
 import pl from "./languages/pl";
 import ptBR from "./languages/pt-BR";
+import bgBG from "./languages/bg-BG";
 import ruRU from "./languages/ru-RU";
 import sr from "./languages/sr";
 import srLatn from "./languages/sr-latn";

From a8badb027db309f02f53d0fd5e1dd543a0abfa5e Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 14:09:50 +0800
Subject: [PATCH 25/48] update modded node-sqlite3 to 6.0.0

---
 package-lock.json | 14 +++++++-------
 package.json      |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 628ccd3a9..43b5479eb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,7 +13,7 @@
                 "@fortawesome/free-regular-svg-icons": "~5.15.4",
                 "@fortawesome/free-solid-svg-icons": "~5.15.4",
                 "@fortawesome/vue-fontawesome": "~3.0.0-4",
-                "@louislam/sqlite3": "~5.0.6",
+                "@louislam/sqlite3": "~6.0.0",
                 "@popperjs/core": "~2.10.2",
                 "args-parser": "~1.3.0",
                 "axios": "~0.21.4",
@@ -1199,9 +1199,9 @@
             }
         },
         "node_modules/@louislam/sqlite3": {
-            "version": "5.0.6",
-            "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-5.0.6.tgz",
-            "integrity": "sha512-uitL0jdbki5XSrmGKGgvHVMHEe00O6GAMoPrVOnh4KTcFOJ1T8SWypbnyqSxBr7PrjAVfgnIGu3kzYCCqIxd4g==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-6.0.0.tgz",
+            "integrity": "sha512-jKNkg7olyL4vM0yqVBiyPrtHALfWkCLAASASDJpghBE5Ri6qOh9bXmzaKyTrYH5cSsFB7R39XGC4O9XdAmTe4Q==",
             "hasInstallScript": true,
             "dependencies": {
                 "@mapbox/node-pre-gyp": "^1.0.0",
@@ -11507,9 +11507,9 @@
             }
         },
         "@louislam/sqlite3": {
-            "version": "5.0.6",
-            "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-5.0.6.tgz",
-            "integrity": "sha512-uitL0jdbki5XSrmGKGgvHVMHEe00O6GAMoPrVOnh4KTcFOJ1T8SWypbnyqSxBr7PrjAVfgnIGu3kzYCCqIxd4g==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-6.0.0.tgz",
+            "integrity": "sha512-jKNkg7olyL4vM0yqVBiyPrtHALfWkCLAASASDJpghBE5Ri6qOh9bXmzaKyTrYH5cSsFB7R39XGC4O9XdAmTe4Q==",
             "requires": {
                 "@mapbox/node-pre-gyp": "^1.0.0",
                 "node-addon-api": "^3.0.0",
diff --git a/package.json b/package.json
index 7558d6822..8c22401b9 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
         "@fortawesome/free-regular-svg-icons": "~5.15.4",
         "@fortawesome/free-solid-svg-icons": "~5.15.4",
         "@fortawesome/vue-fontawesome": "~3.0.0-4",
-        "@louislam/sqlite3": "~5.0.6",
+        "@louislam/sqlite3": "~6.0.0",
         "@popperjs/core": "~2.10.2",
         "args-parser": "~1.3.0",
         "axios": "~0.21.4",

From 1eb3f63a8295d51fa155ea6f274aed9d066f0b8d Mon Sep 17 00:00:00 2001
From: MrEddX <66828538+MrEddX@users.noreply.github.com>
Date: Thu, 7 Oct 2021 09:27:05 +0300
Subject: [PATCH 26/48] Update bg-BG.js

---
 src/languages/bg-BG.js | 58 +++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js
index ba910bb0a..0e915255d 100644
--- a/src/languages/bg-BG.js
+++ b/src/languages/bg-BG.js
@@ -1,6 +1,6 @@
 export default {
     languageName: "Български",
-    checkEverySecond: "Проверявай на всеки {0} секунди.",
+    checkEverySecond: "Ще се извършва на всеки {0} секунди.",
     retryCheckEverySecond: "Повторен опит на всеки {0} секунди.",
     retriesDescription: "Максимакен брой опити преди услугата да бъде маркирана като недостъпна и да бъде изпратено известие",
     ignoreTLSError: "Игнорирай TLS/SSL грешки за HTTPS уебсайтове",
@@ -9,19 +9,19 @@ export default {
     acceptedStatusCodesDescription: "Изберете статус кодове, които се считат за успешен отговор.",
     passwordNotMatchMsg: "Повторената парола не съвпада.",
     notificationDescription: "Моля, задайте известието към монитор(и), за да функционира.",
-    keywordDescription: "Търсете ключова дума в обикновен html или JSON отговор - чувствителна е към регистъра",
+    keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра",
     pauseDashboardHome: "Пауза",
     deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?",
-    deleteNotificationMsg: "Наистина ли желаете да изтриете известието за всички монитори?",
-    resoverserverDescription: "Cloudflare е сървърът по подразбиране, можете да промените сървъра по всяко време.",
+    deleteNotificationMsg: "Наистина ли желаете да изтриете това известяване за всички монитори?",
+    resoverserverDescription: "Cloudflare е сървърът по подразбиране, но можете да го промените по всяко време.",
     rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате",
     pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?",
-    enableDefaultNotificationDescription: "За всеки нов монитор това известие ще бъде активирано по подразбиране. Можете да изключите известието за всеки отделен монитор.",
+    enableDefaultNotificationDescription: "За всеки нов монитор това известяване ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.",
     clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?",
     clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?",
     confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?",
-    importHandleDescription: "Изберете 'Пропусни съществуващите', ако искате да пропуснете всеки монитор или известие със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известие.",
-    confirmImportMsg: "Сигурни ли сте за импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.",
+    importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известяване със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известяване.",
+    confirmImportMsg: "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.",
     twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи",
     tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.",
     confirmEnableTwoFAMsg: "Сигурни ли сте, че желаете да активирате 2FA?",
@@ -34,32 +34,32 @@ export default {
     Theme: "Тема",
     General: "Общи",
     Version: "Версия",
-    "Check Update On GitHub": "Провери за актуализация в GitHub",
+    "Check Update On GitHub": "Проверка за актуализация в GitHub",
     List: "Списък",
     Add: "Добави",
     "Add New Monitor": "Добави монитор",
     "Quick Stats": "Кратка статистика",
-    Up: "Достъпни",
-    Down: "Недостъпни",
-    Pending: "В изчакване",
-    Unknown: "Неизвестни",
-    Pause: "В пауза",
+    Up: "Достъпен",
+    Down: "Недостъпен",
+    Pending: "Изчаква",
+    Unknown: "Неизвестен",
+    Pause: "Пауза",
     Name: "Име",
     Status: "Статус",
     DateTime: "Дата и час",
-    Message: "Съобщение",
+    Message: "Отговор",
     "No important events": "Няма важни събития",
     Resume: "Възобнови",
     Edit: "Редактирай",
     Delete: "Изтрий",
     Current: "Текущ",
-    Uptime: "Време на работа",
+    Uptime: "Достъпност",
     "Cert Exp.": "Вал. сертификат",
     days: "дни",
     day: "ден",
-    "-day": "-ден",
+    "-day": "-денa",
     hour: "час",
-    "-hour": "-час",
+    "-hour": "-часa",
     Response: "Отговор",
     Ping: "Пинг",
     "Monitor Type": "Монитор тип",
@@ -78,7 +78,7 @@ export default {
     Save: "Запази",
     Notifications: "Известявания",
     "Not available, please setup.": "Не е налично. Моля, настройте.",
-    "Setup Notification": "Настройка за известяване",
+    "Setup Notification": "Настройки за известявания",
     Light: "Светла",
     Dark: "Тъмна",
     Auto: "Автоматично",
@@ -98,9 +98,9 @@ export default {
     "Disable Auth": "Изключи удостоверяване",
     "Enable Auth": "Включи удостоверяване",
     Logout: "Изход от профила",
-    Leave: "Напускам",
+    Leave: "Отказ",
     "I understand, please disable": "Разбирам. Моля, изключи",
-    Confirm: "Потвърди",
+    Confirm: "Потвърдете",
     Yes: "Да",
     No: "Не",
     Username: "Потребител",
@@ -127,7 +127,7 @@ export default {
     "Default enabled": "Включен по подразбиране",
     "Apply on all existing monitors": "Приложи върху всички съществуващи монитори",
     Create: "Създай",
-    "Clear Data": "Изчисти данни",
+    "Clear Data": "Изтрий данни",
     Events: "Събития",
     Heartbeats: "Проверки",
     "Auto Get": "Автоматияно получаване",
@@ -136,7 +136,7 @@ export default {
     backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.",
     alertNoFile: "Моля, изберете файл за импортиране.",
     alertWrongFileType: "Моля, изберете JSON файл.",
-    "Clear all statistics": "Изчисти всички статистики",
+    "Clear all statistics": "Изтрий цялата статистика",
     "Skip existing": "Пропусни съществуващите",
     Overwrite: "Презапиши",
     Options: "Опции",
@@ -152,7 +152,7 @@ export default {
     Token: "Токен код",
     "Show URI": "Покажи URI",
     Tags: "Етикети",
-    "Add New below or Select...": "Добави нов по-долу или избери...",
+    "Add New below or Select...": "Добавете нов по-долу или изберете...",
     "Tag with this name already exist.": "Етикет с това име вече съществува.",
     "Tag with this value already exist.": "Етикет с тази стойност вече съществува.",
     color: "цвят",
@@ -171,12 +171,12 @@ export default {
     "Entry Page": "Основна страница",
     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": "Редактирай статус страница",
+    "Edit Status Page": "Редактиране Статус страница",
     "Go to Dashboard": "Към Таблото",
     telegram: "Telegram",
     webhook: "Webhook",
@@ -191,9 +191,9 @@ export default {
     pushy: "Pushy",
     octopush: "Octopush",
     lunasea: "LunaSea",
-    apprise: "Apprise (Support 50+ Notification services)",
+    apprise: "Apprise (Поддържа 50+ услуги за инвестяване)",
     pushbullet: "Pushbullet",
     line: "Line Messenger",
     mattermost: "Mattermost",
-    "Status Page": "Status Page",
+    "Status Page": "Статус страница",
 };

From 18e47023759a04029521ce5cd6d30735d569fc20 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 14:34:30 +0800
Subject: [PATCH 27/48] ignore .env

---
 .dockerignore | 2 +-
 .gitignore    | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/.dockerignore b/.dockerignore
index 539e93280..62925caa2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -27,7 +27,7 @@ CNAME
 install.sh
 SECURITY.md
 tsconfig.json
-
+.env
 
 ### .gitignore content (commented rules are duplicated)
 
diff --git a/.gitignore b/.gitignore
index 0863015db..cd654d903 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ dist-ssr
 /private
 /out
 /tmp
+.env

From 9f5bf37a967918e7a72aea42d6dcd1b06bb94d8f Mon Sep 17 00:00:00 2001
From: MrEddX <66828538+MrEddX@users.noreply.github.com>
Date: Thu, 7 Oct 2021 09:37:13 +0300
Subject: [PATCH 28/48] Update Settings.vue

---
 src/pages/Settings.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue
index 22bb586c2..3e98595cc 100644
--- a/src/pages/Settings.vue
+++ b/src/pages/Settings.vue
@@ -325,7 +325,7 @@
                 <template v-else-if="$i18n.locale === 'bg-BG' ">
                     <p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p>
                     <p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p>
-                    <p>Моля, използвайте внимателно.</p>
+                    <p>Моля, използвайте с повишено внимание.</p>
                 </template>
 
                 <template v-else-if="$i18n.locale === 'hu' ">

From cccf393ee5956ffda97e32a10a12ab1de5087c90 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 14:55:32 +0800
Subject: [PATCH 29/48] update zh-HK.js

---
 src/languages/zh-HK.js | 64 +++++++++++++++++++++---------------------
 1 file changed, 32 insertions(+), 32 deletions(-)

diff --git a/src/languages/zh-HK.js b/src/languages/zh-HK.js
index 3b12177d9..ea81f7f33 100644
--- a/src/languages/zh-HK.js
+++ b/src/languages/zh-HK.js
@@ -1,7 +1,7 @@
 export default {
     languageName: "繁體中文 (香港)",
     Settings: "設定",
-    Dashboard: "錶板",
+    Dashboard: "主控台",
     "New Update": "有更新",
     Language: "語言",
     Appearance: "外觀",
@@ -104,7 +104,7 @@ export default {
     rrtypeDescription: "請選擇 DNS 記錄類型",
     pauseMonitorMsg: "是否確定暫停?",
     "Last Result": "最後結果",
-    "Create your admin account": "製作你的管理員帳號",
+    "Create your admin account": "建立管理員帳號",
     "Repeat Password": "重複密碼",
     respTime: "反應時間 (ms)",
     notAvailableShort: "N/A",
@@ -146,43 +146,43 @@ export default {
     importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.",
     confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.",
     "Heartbeat Retry Interval": "Heartbeat Retry Interval",
-    "Import Backup": "Import Backup",
-    "Export Backup": "Export Backup",
-    "Skip existing": "Skip existing",
-    Overwrite: "Overwrite",
-    Options: "Options",
-    "Keep both": "Keep both",
-    Tags: "Tags",
+    "Import Backup": "匯入備份",
+    "Export Backup": "匯出備份",
+    "Skip existing": "略過已存在的",
+    Overwrite: "覆蓋",
+    Options: "選項",
+    "Keep both": "兩者並存",
+    Tags: "標籤",
     "Add New below or Select...": "Add New below or Select...",
     "Tag with this name already exist.": "Tag with this name already exist.",
     "Tag with this value already exist.": "Tag with this value already exist.",
-    color: "color",
-    "value (optional)": "value (optional)",
-    Gray: "Gray",
-    Red: "Red",
-    Orange: "Orange",
-    Green: "Green",
-    Blue: "Blue",
-    Indigo: "Indigo",
-    Purple: "Purple",
-    Pink: "Pink",
-    "Search...": "Search...",
-    "Avg. Ping": "Avg. Ping",
-    "Avg. Response": "Avg. Response",
+    color: "顏色",
+    "value (optional)": "值 (非必需)",
+    Gray: "灰",
+    Red: "紅",
+    Orange: "橙",
+    Green: "綠",
+    Blue: "藍",
+    Indigo: "靛",
+    Purple: "紫",
+    Pink: "粉紅",
+    "Search...": "搜尋...",
+    "Avg. Ping": "平均反應時間",
+    "Avg. Response": "平均反應時間",
     "Entry Page": "Entry Page",
     statusPageNothing: "Nothing here, please add a group or a monitor.",
-    "No Services": "No Services",
-    "All Systems Operational": "All Systems Operational",
-    "Partially Degraded Service": "Partially Degraded Service",
-    "Degraded Service": "Degraded Service",
-    "Add Group": "Add Group",
-    "Add a monitor": "Add a monitor",
-    "Edit Status Page": "Edit Status Page",
-    "Go to Dashboard": "Go to Dashboard",
+    "No Services": "沒有服務",
+    "All Systems Operational": "一切正常",
+    "Partially Degraded Service": "部份服務受阻",
+    "Degraded Service": "服務受阻",
+    "Add Group": "新增群組",
+    "Add a monitor": " 新增監測器",
+    "Edit Status Page": "編輯 Status Page",
+    "Go to Dashboard": "前往主控台",
     "Status Page": "Status Page",
     telegram: "Telegram",
     webhook: "Webhook",
-    smtp: "Email (SMTP)",
+    smtp: "電郵 (SMTP)",
     discord: "Discord",
     teams: "Microsoft Teams",
     signal: "Signal",
@@ -193,7 +193,7 @@ export default {
     pushy: "Pushy",
     octopush: "Octopush",
     lunasea: "LunaSea",
-    apprise: "Apprise (Support 50+ Notification services)",
+    apprise: "Apprise (支援 50 多種通知)",
     pushbullet: "Pushbullet",
     line: "Line Messenger",
     mattermost: "Mattermost",

From 8233f3b87554023803a6b56d66a68e30cd347a68 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 15:06:16 +0800
Subject: [PATCH 30/48] try to standardize the language name list

---
 src/languages/da-DK.js | 2 +-
 src/languages/de-DE.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/languages/da-DK.js b/src/languages/da-DK.js
index 8df3627b2..ad156aefe 100644
--- a/src/languages/da-DK.js
+++ b/src/languages/da-DK.js
@@ -1,5 +1,5 @@
 export default {
-    languageName: "Danish",
+    languageName: "Danish (Danmark)",
     Settings: "Indstillinger",
     Dashboard: "Dashboard",
     "New Update": "Opdatering tilgængelig",
diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index c4e903b24..a6cc42b3b 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -1,5 +1,5 @@
 export default {
-    languageName: "German",
+    languageName: "Deutsch (Deutschland)",
     Settings: "Einstellungen",
     Dashboard: "Dashboard",
     "New Update": "Update Verfügbar",

From ed67803af8a16cb82eae4a5fd8e96770b8189a3f Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 16:10:21 +0800
Subject: [PATCH 31/48] improve minor style

---
 src/components/TagsManager.vue | 20 ++++++++++----------
 src/pages/Settings.vue         |  8 ++++----
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/components/TagsManager.vue b/src/components/TagsManager.vue
index 7fc78a340..2412f7e8e 100644
--- a/src/components/TagsManager.vue
+++ b/src/components/TagsManager.vue
@@ -1,7 +1,7 @@
 <template>
     <div>
-        <h4 class="mb-3">{{ $t("Tags") }}</h4>
-        <div class="mb-3 p-1">
+        <h4 class="mt-5 mb-3">{{ $t("Tags") }}</h4>
+        <div v-if="selectedTags.length > 0" class="mb-2 p-1">
             <tag
                 v-for="item in selectedTags"
                 :key="item.id"
@@ -124,8 +124,8 @@
 import { Modal } from "bootstrap";
 import VueMultiselect from "vue-multiselect";
 import Tag from "../components/Tag.vue";
-import { useToast } from "vue-toastification"
-const toast = useToast()
+import { useToast } from "vue-toastification";
+const toast = useToast();
 
 export default {
     components: {
@@ -186,7 +186,7 @@ export default {
                     color: "#7C3AED" },
                 { name: this.$t("Pink"),
                     color: "#DB2777" },
-            ]
+            ];
         },
         validateDraftTag() {
             let nameInvalid = false;
@@ -227,7 +227,7 @@ export default {
                 invalid,
                 nameInvalid,
                 valueInvalid,
-            }
+            };
         },
     },
     mounted() {
@@ -243,7 +243,7 @@ export default {
                 if (res.ok) {
                     this.existingTags = res.tags;
                 } else {
-                    toast.error(res.msg)
+                    toast.error(res.msg);
                 }
             });
         },
@@ -277,7 +277,7 @@ export default {
                         name: this.newDraftTag.select.name,
                         value: this.newDraftTag.value,
                         new: true,
-                    })
+                    });
                 }
             } else {
                 // Add new Tag
@@ -286,7 +286,7 @@ export default {
                     name: this.newDraftTag.name.trim(),
                     value: this.newDraftTag.value,
                     new: true,
-                })
+                });
             }
             this.clearDraftTag();
         },
@@ -348,7 +348,7 @@ export default {
                         if (tag.name == newTag.name && tag.color == newTag.color) {
                             tag.id = newTagResult.id;
                         }
-                    })
+                    });
                 } else {
                     tagId = newTag.id;
                 }
diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue
index 3e98595cc..e17dd96bd 100644
--- a/src/pages/Settings.vue
+++ b/src/pages/Settings.vue
@@ -196,9 +196,9 @@
                             <h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
 
                             <div class="mb-3">
-                                <button v-if="settings.disableAuth" class="btn btn-outline-primary me-1 mb-1" @click="enableAuth">{{ $t("Enable Auth") }}</button>
-                                <button v-if="! settings.disableAuth" class="btn btn-primary me-1 mb-1" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button>
-                                <button v-if="! settings.disableAuth" class="btn btn-danger me-1 mb-1" @click="$root.logout">{{ $t("Logout") }}</button>
+                                <button v-if="settings.disableAuth" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button>
+                                <button v-if="! settings.disableAuth" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button>
+                                <button v-if="! settings.disableAuth" class="btn btn-danger me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button>
                                 <button class="btn btn-outline-danger me-1 mb-1" @click="confirmClearStatistics">{{ $t("Clear all statistics") }}</button>
                             </div>
                         </template>
@@ -400,7 +400,7 @@ export default {
 
         "$i18n.locale"() {
             localStorage.locale = this.$i18n.locale;
-            setPageLocale()
+            setPageLocale();
         },
     },
 

From d08a71ab4998c638eb35a74f612c86e533ea8767 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 16:30:16 +0800
Subject: [PATCH 32/48] Set primary base url in settings page

---
 src/pages/Settings.vue | 67 +++++++++++++++++++++++++++++-------------
 1 file changed, 47 insertions(+), 20 deletions(-)

diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue
index e17dd96bd..6068f116d 100644
--- a/src/pages/Settings.vue
+++ b/src/pages/Settings.vue
@@ -52,9 +52,12 @@
                             </div>
                         </div>
 
+                        <!-- General Settings -->
                         <h2 class="mt-5 mb-2">{{ $t("General") }}</h2>
+
                         <form class="mb-3" @submit.prevent="saveGeneral">
-                            <div class="mb-3">
+                            <!-- Timezone -->
+                            <div class="mb-4">
                                 <label for="timezone" class="form-label">{{ $t("Timezone") }}</label>
                                 <select id="timezone" v-model="$root.userTimezone" class="form-select">
                                     <option value="auto">
@@ -66,7 +69,8 @@
                                 </select>
                             </div>
 
-                            <div class="mb-3">
+                            <!-- Search Engine -->
+                            <div class="mb-4">
                                 <label class="form-label">{{ $t("Search Engine Visibility") }}</label>
 
                                 <div class="form-check">
@@ -83,7 +87,8 @@
                                 </div>
                             </div>
 
-                            <div class="mb-3">
+                            <!-- Entry Page -->
+                            <div class="mb-4">
                                 <label class="form-label">{{ $t("Entry Page") }}</label>
 
                                 <div class="form-check">
@@ -101,6 +106,19 @@
                                 </div>
                             </div>
 
+                            <!-- Primary Base URL -->
+                            <div class="mb-4">
+                                <label class="form-label" for="primaryBaseURL">Primary Base URL</label>
+
+                                <div class="input-group mb-3">
+                                    <input id="primaryBaseURL" v-model="settings.primaryBaseURL" class="form-control" name="primaryBaseURL" placeholder="https://">
+                                    <button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">Auto Get</button>
+                                </div>
+
+                                <div class="form-text">
+                                </div>
+                            </div>
+
                             <div>
                                 <button class="btn btn-primary" type="submit">
                                     {{ $t("Save") }}
@@ -109,6 +127,7 @@
                         </form>
 
                         <template v-if="loaded">
+                            <!-- Change Password -->
                             <template v-if="! settings.disableAuth">
                                 <h2 class="mt-5 mb-2">{{ $t("Change Password") }}</h2>
                                 <form class="mb-3" @submit.prevent="savePassword">
@@ -204,28 +223,32 @@
                         </template>
                     </div>
 
-                    <div class="notification-list col-md-6">
+                    <div class="col-md-6">
                         <div v-if="$root.isMobile" class="mt-3" />
 
-                        <h2>{{ $t("Notifications") }}</h2>
-                        <p v-if="$root.notificationList.length === 0">
-                            {{ $t("Not available, please setup.") }}
-                        </p>
-                        <p v-else>
-                            {{ $t("notificationDescription") }}
-                        </p>
+                        <!-- Notifications -->
+                        <div class="notification-list ">
+                            <h2>{{ $t("Notifications") }}</h2>
+                            <p v-if="$root.notificationList.length === 0">
+                                {{ $t("Not available, please setup.") }}
+                            </p>
+                            <p v-else>
+                                {{ $t("notificationDescription") }}
+                            </p>
 
-                        <ul class="list-group mb-3" style="border-radius: 1rem;">
-                            <li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
-                                {{ notification.name }}<br>
-                                <a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
-                            </li>
-                        </ul>
+                            <ul class="list-group mb-3" style="border-radius: 1rem;">
+                                <li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
+                                    {{ notification.name }}<br>
+                                    <a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
+                                </li>
+                            </ul>
 
-                        <button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
-                            {{ $t("Setup Notification") }}
-                        </button>
+                            <button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
+                                {{ $t("Setup Notification") }}
+                            </button>
+                        </div>
 
+                        <!-- Info -->
                         <h2 class="mt-5">{{ $t("Info") }}</h2>
 
                         {{ $t("Version") }}: {{ $root.info.version }} <br />
@@ -531,6 +554,10 @@ export default {
                 }
             });
         },
+
+        autoGetPrimaryBaseURL() {
+            this.settings.primaryBaseURL = location.protocol + "//" + location.host;
+        }
     },
 };
 </script>

From b69550f5b9280889dafbdfd148166070526af35e Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 17:01:17 +0800
Subject: [PATCH 33/48] Improve the test

---
 test/test.spec.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/test/test.spec.js b/test/test.spec.js
index e1fd28904..079b472c8 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -223,12 +223,14 @@ async function login(username, password) {
 }
 
 async function click(page, selector, elementIndex = 0) {
+    await page.waitForSelector(selector);
     return await page.evaluate((s, i) => {
         return document.querySelectorAll(s)[i].click();
     }, selector, elementIndex);
 }
 
 async function input(page, selector, text) {
+    await page.waitForSelector(selector);
     const element = await page.$(selector);
     await element.click({ clickCount: 3 });
     await page.keyboard.press("Backspace");

From e5f6d7f0479ee35cb19e266014bfd357770d1209 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 17:39:58 +0800
Subject: [PATCH 34/48] slack and rocket.chat use the primary base url env var
 to show time logger

---
 server/model/monitor.js                      |  5 +-
 server/notification-providers/rocket-chat.js | 34 +++++++---
 server/notification-providers/slack.js       | 66 ++++++++++++++------
 src/components/MonitorList.vue               |  3 +-
 src/components/notifications/RocketChat.vue  |  2 -
 src/components/notifications/Slack.vue       |  3 +-
 src/pages/Settings.vue                       |  2 +-
 src/util.js                                  |  8 ++-
 src/util.ts                                  |  6 +-
 9 files changed, 93 insertions(+), 36 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index c551fa7d7..0e2d84ede 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -166,7 +166,9 @@ class Monitor extends BeanModel {
                         }
                     }
 
-                    debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
+                    if (process.env.TIMELOGGER === "1") {
+                        debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
+                    }
 
                     if (this.type === "http") {
                         bean.status = UP;
@@ -530,6 +532,7 @@ class Monitor extends BeanModel {
         const uptime = await this.calcUptime(duration, monitorID);
         io.to(userID).emit("uptime", monitorID, duration, uptime);
     }
+
 }
 
 module.exports = Monitor;
diff --git a/server/notification-providers/rocket-chat.js b/server/notification-providers/rocket-chat.js
index 149189650..a9bd40091 100644
--- a/server/notification-providers/rocket-chat.js
+++ b/server/notification-providers/rocket-chat.js
@@ -1,5 +1,8 @@
 const NotificationProvider = require("./notification-provider");
 const axios = require("axios");
+const Slack = require("./slack");
+const { setting } = require("../util-server");
+const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util");
 
 class RocketChat extends NotificationProvider {
 
@@ -10,16 +13,17 @@ class RocketChat extends NotificationProvider {
         try {
             if (heartbeatJSON == null) {
                 let data = {
-                    "text": "Uptime Kuma Rocket.chat testing successful.",
+                    "text": msg,
                     "channel": notification.rocketchannel,
                     "username": notification.rocketusername,
                     "icon_emoji": notification.rocketiconemo,
-                }
-                await axios.post(notification.rocketwebhookURL, data)
+                };
+                await axios.post(notification.rocketwebhookURL, data);
                 return okMsg;
             }
 
             const time = heartbeatJSON["time"];
+
             let data = {
                 "text": "Uptime Kuma Alert",
                 "channel": notification.rocketchannel,
@@ -28,16 +32,32 @@ class RocketChat extends NotificationProvider {
                 "attachments": [
                     {
                         "title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
-                        "title_link": notification.rocketbutton,
                         "text": "*Message*\n" + msg,
-                        "color": "#32cd32"
                     }
                 ]
+            };
+
+            // Color
+            if (heartbeatJSON.status === DOWN) {
+                data.attachments[0].color = "#ff0000";
+            } else {
+                data.attachments[0].color = "#32cd32";
             }
-            await axios.post(notification.rocketwebhookURL, data)
+
+            if (notification.rocketbutton) {
+                await Slack.deprecateURL(notification.rocketbutton);
+            }
+
+            const baseURL = await setting("primaryBaseURL");
+
+            if (baseURL) {
+                data.attachments[0].title_link = baseURL + getMonitorRelativeURL(monitorJSON.id);
+            }
+
+            await axios.post(notification.rocketwebhookURL, data);
             return okMsg;
         } catch (error) {
-            this.throwGeneralAxiosError(error)
+            this.throwGeneralAxiosError(error);
         }
 
     }
diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js
index 661df5a0f..271ee3dc0 100644
--- a/server/notification-providers/slack.js
+++ b/server/notification-providers/slack.js
@@ -1,21 +1,40 @@
 const NotificationProvider = require("./notification-provider");
 const axios = require("axios");
+const { setSettings, setting } = require("../util-server");
+const { getMonitorRelativeURL } = require("../../src/util");
 
 class Slack extends NotificationProvider {
 
     name = "slack";
 
+    /**
+     * Deprecated property notification.slackbutton
+     * Set it as primary base url if this is not yet set.
+     */
+    static async deprecateURL(url) {
+        let currentPrimaryBaseURL = await setting("primaryBaseURL");
+
+        if (!currentPrimaryBaseURL) {
+            console.log("Move the url to be the primary base URL");
+            await setSettings("general", {
+                primaryBaseURL: url,
+            });
+        } else {
+            console.log("Already there, no need to move the primary base URL");
+        }
+    }
+
     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
         let okMsg = "Sent Successfully. ";
         try {
             if (heartbeatJSON == null) {
                 let data = {
-                    "text": "Uptime Kuma Slack testing successful.",
+                    "text": msg,
                     "channel": notification.slackchannel,
                     "username": notification.slackusername,
                     "icon_emoji": notification.slackiconemo,
-                }
-                await axios.post(notification.slackwebhookURL, data)
+                };
+                await axios.post(notification.slackwebhookURL, data);
                 return okMsg;
             }
 
@@ -42,26 +61,35 @@ class Slack extends NotificationProvider {
                         "type": "mrkdwn",
                         "text": "*Time (UTC)*\n" + time,
                     }],
-                },
-                {
-                    "type": "actions",
-                    "elements": [
-                        {
-                            "type": "button",
-                            "text": {
-                                "type": "plain_text",
-                                "text": "Visit Uptime Kuma",
-                            },
-                            "value": "Uptime-Kuma",
-                            "url": notification.slackbutton || "https://github.com/louislam/uptime-kuma",
-                        },
-                    ],
                 }],
+            };
+
+            if (notification.slackbutton) {
+                await Slack.deprecateURL(notification.slackbutton);
             }
-            await axios.post(notification.slackwebhookURL, data)
+
+            const baseURL = await setting("primaryBaseURL");
+
+            // Button
+            if (baseURL) {
+                data.blocks.push({
+                    "type": "actions",
+                    "elements": [{
+                        "type": "button",
+                        "text": {
+                            "type": "plain_text",
+                            "text": "Visit Uptime Kuma",
+                        },
+                        "value": "Uptime-Kuma",
+                        "url": baseURL + getMonitorRelativeURL(monitorJSON.id),
+                    }],
+                });
+            }
+
+            await axios.post(notification.slackwebhookURL, data);
             return okMsg;
         } catch (error) {
-            this.throwGeneralAxiosError(error)
+            this.throwGeneralAxiosError(error);
         }
 
     }
diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue
index 54c5873ef..77efe564a 100644
--- a/src/components/MonitorList.vue
+++ b/src/components/MonitorList.vue
@@ -47,6 +47,7 @@
 import HeartbeatBar from "../components/HeartbeatBar.vue";
 import Uptime from "../components/Uptime.vue";
 import Tag from "../components/Tag.vue";
+import { getMonitorRelativeURL } from "../util.ts";
 
 export default {
     components: {
@@ -109,7 +110,7 @@ export default {
     },
     methods: {
         monitorURL(id) {
-            return "/dashboard/" + id;
+            return getMonitorRelativeURL(id);
         },
         clearSearchText() {
             this.searchText = "";
diff --git a/src/components/notifications/RocketChat.vue b/src/components/notifications/RocketChat.vue
index 465b0dc9d..ed90fb75d 100644
--- a/src/components/notifications/RocketChat.vue
+++ b/src/components/notifications/RocketChat.vue
@@ -8,8 +8,6 @@
         <input id="rocket-iconemo" v-model="$parent.notification.rocketiconemo" type="text" class="form-control">
         <label for="rocket-channel" class="form-label">{{ $t("Channel Name") }}</label>
         <input id="rocket-channel-name" v-model="$parent.notification.rocketchannel" type="text" class="form-control">
-        <label for="rocket-button-url" class="form-label">{{ $t("Uptime Kuma URL") }}</label>
-        <input id="rocket-button" v-model="$parent.notification.rocketbutton" type="text" class="form-control">
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
             <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue
index 0e1e7cb08..6d220caad 100644
--- a/src/components/notifications/Slack.vue
+++ b/src/components/notifications/Slack.vue
@@ -8,8 +8,7 @@
         <input id="slack-iconemo" v-model="$parent.notification.slackiconemo" type="text" class="form-control">
         <label for="slack-channel" class="form-label">{{ $t("Channel Name") }}</label>
         <input id="slack-channel-name" v-model="$parent.notification.slackchannel" type="text" class="form-control">
-        <label for="slack-button-url" class="form-label">{{ $t("Uptime Kuma URL") }}</label>
-        <input id="slack-button" v-model="$parent.notification.slackbutton" type="text" class="form-control">
+
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
             <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue
index 6068f116d..259f334b7 100644
--- a/src/pages/Settings.vue
+++ b/src/pages/Settings.vue
@@ -111,7 +111,7 @@
                                 <label class="form-label" for="primaryBaseURL">Primary Base URL</label>
 
                                 <div class="input-group mb-3">
-                                    <input id="primaryBaseURL" v-model="settings.primaryBaseURL" class="form-control" name="primaryBaseURL" placeholder="https://">
+                                    <input id="primaryBaseURL" v-model="settings.primaryBaseURL" class="form-control" name="primaryBaseURL" placeholder="https://" pattern="https?://.+">
                                     <button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">Auto Get</button>
                                 </div>
 
diff --git a/src/util.js b/src/util.js
index 54649bd58..64ac2f6f6 100644
--- a/src/util.js
+++ b/src/util.js
@@ -7,7 +7,7 @@
 // Backend uses the compiled file util.js
 // Frontend uses util.ts
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
+exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
 const _dayjs = require("dayjs");
 const dayjs = _dayjs;
 exports.isDev = process.env.NODE_ENV === "development";
@@ -74,7 +74,7 @@ class TimeLogger {
         this.startTime = dayjs().valueOf();
     }
     print(name) {
-        if (exports.isDev) {
+        if (exports.isDev && process && process.env.TIMELOGGER === "1") {
             console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
         }
     }
@@ -112,3 +112,7 @@ function genSecret(length = 64) {
     return secret;
 }
 exports.genSecret = genSecret;
+function getMonitorRelativeURL(id) {
+    return "/dashboard/" + id;
+}
+exports.getMonitorRelativeURL = getMonitorRelativeURL;
diff --git a/src/util.ts b/src/util.ts
index 69dbe68a6..450050490 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -86,7 +86,7 @@ export class TimeLogger {
     }
 
     print(name: string) {
-        if (isDev) {
+        if (isDev && process && process.env.TIMELOGGER === "1") {
             console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms")
         }
     }
@@ -123,3 +123,7 @@ export function genSecret(length = 64) {
     }
     return secret;
 }
+
+export function getMonitorRelativeURL(id: string) {
+    return "/dashboard/" + id;
+}

From 67a623be189542e433f5bae4069d35eeb9d9cc2f Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 17:48:13 +0800
Subject: [PATCH 35/48] e2e testing, it's hard

---
 test/test.spec.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/test/test.spec.js b/test/test.spec.js
index 079b472c8..f59faf155 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -151,6 +151,9 @@ describe("Init", () => {
         });
 
         it("Change Password (wrong current password)", async () => {
+            await page.goto(baseURL + "/settings");
+            await page.waitForSelector("#current-password");
+
             await page.type("#current-password", "wrong_passw$$d");
             await page.type("#new-password", "new_password123");
             await page.type("#repeat-new-password", "new_password123");
@@ -168,6 +171,9 @@ describe("Init", () => {
         });
 
         it("Change Password (wrong repeat)", async () => {
+            await page.goto(baseURL + "/settings");
+            await page.waitForSelector("#current-password");
+
             await page.type("#current-password", "admin123");
             await page.type("#new-password", "new_password123");
             await page.type("#repeat-new-password", "new_password1234567898797898");

From 2c19aef4dc424ac21b801b71de0a56ee8d58aa3b Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 18:06:43 +0800
Subject: [PATCH 36/48] minor

---
 src/languages/fa.js      | 2 +-
 src/pages/StatusPage.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/languages/fa.js b/src/languages/fa.js
index 100577f4d..d686488ff 100644
--- a/src/languages/fa.js
+++ b/src/languages/fa.js
@@ -186,7 +186,7 @@ export default {
     First: "اولین",
     Last: "آخرین",
     Info: "اطلاعات",
-    "Powered By": "نیرو گرفته از",
+    "Powered by": "نیرو گرفته از",
     telegram: "Telegram",
     webhook: "Webhook",
     smtp: "Email (SMTP)",
diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue
index 7050313e6..87634f35a 100644
--- a/src/pages/StatusPage.vue
+++ b/src/pages/StatusPage.vue
@@ -197,7 +197,7 @@
         </div>
 
         <footer class="mt-5 mb-4">
-            {{ $t("Powered By") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" )}}</a>
+            {{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
         </footer>
     </div>
 </template>

From 34586d7b8f548b4117fcc067167c1f689cf4a833 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 18:10:35 +0800
Subject: [PATCH 37/48] [test] github action please ok

---
 test/test.spec.js | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/test/test.spec.js b/test/test.spec.js
index f59faf155..88e632725 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -132,18 +132,18 @@ describe("Init", () => {
             // Status Page
             await click(page, "#entryPageNo");
             await click(page, "form > div > .btn[type=submit]");
-            await sleep(2000);
+            await sleep(4000);
             await newPage.goto(baseURL);
-            await sleep(3000);
+            await sleep(4000);
             pathname = await newPage.evaluate(() => location.pathname);
             expect(pathname).toEqual("/status");
 
             // Back to Dashboard
             await click(page, "#entryPageYes");
             await click(page, "form > div > .btn[type=submit]");
-            await sleep(2000);
+            await sleep(4000);
             await newPage.goto(baseURL);
-            await sleep(3000);
+            await sleep(4000);
             pathname = await newPage.evaluate(() => location.pathname);
             expect(pathname).toEqual("/dashboard");
 
@@ -157,17 +157,17 @@ describe("Init", () => {
             await page.type("#current-password", "wrong_passw$$d");
             await page.type("#new-password", "new_password123");
             await page.type("#repeat-new-password", "new_password123");
+
+            // Save
             await click(page, "form > div > .btn[type=submit]", 1);
-            await sleep(3000);
+            await sleep(4000);
+
             await click(page, ".btn-danger.btn.me-1");
-            await sleep(2000);
             await login("admin", "new_password123");
-            await sleep(2000);
             let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
             expect(elementCount).toEqual(1);
 
             await login("admin", "admin123");
-            await sleep(3000);
         });
 
         it("Change Password (wrong repeat)", async () => {
@@ -177,12 +177,13 @@ describe("Init", () => {
             await page.type("#current-password", "admin123");
             await page.type("#new-password", "new_password123");
             await page.type("#repeat-new-password", "new_password1234567898797898");
+
             await click(page, "form > div > .btn[type=submit]", 1);
-            await sleep(3000);
+            await sleep(4000);
+
             await click(page, ".btn-danger.btn.me-1");
-            await sleep(2000);
             await login("admin", "new_password123");
-            await sleep(2000);
+
             let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
             expect(elementCount).toEqual(1);
 
@@ -226,17 +227,22 @@ async function login(username, password) {
     await input(page, "#floatingInput", username);
     await input(page, "#floatingPassword", password);
     await page.click(".btn-primary[type=submit]");
+    await sleep(5000);
 }
 
 async function click(page, selector, elementIndex = 0) {
-    await page.waitForSelector(selector);
+    await page.waitForSelector(selector, {
+        timeout: 5000,
+    });
     return await page.evaluate((s, i) => {
         return document.querySelectorAll(s)[i].click();
     }, selector, elementIndex);
 }
 
 async function input(page, selector, text) {
-    await page.waitForSelector(selector);
+    await page.waitForSelector(selector, {
+        timeout: 5000,
+    });
     const element = await page.$(selector);
     await element.click({ clickCount: 3 });
     await page.keyboard.press("Backspace");

From 3c60800eabd8fe463ee0819faed63ce7eeaf587a Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 18:47:44 +0800
Subject: [PATCH 38/48] =?UTF-8?q?[test]=20github=20action=20please=20ok?=
 =?UTF-8?q?=F0=9F=99=8F=F0=9F=8F=BB=F0=9F=99=8F=F0=9F=8F=BB=F0=9F=99=8F?=
 =?UTF-8?q?=F0=9F=8F=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 test/test.spec.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/test.spec.js b/test/test.spec.js
index 88e632725..91c8459bd 100644
--- a/test/test.spec.js
+++ b/test/test.spec.js
@@ -162,7 +162,7 @@ describe("Init", () => {
             await click(page, "form > div > .btn[type=submit]", 1);
             await sleep(4000);
 
-            await click(page, ".btn-danger.btn.me-1");
+            await click(page, ".btn-danger.btn.me-2");
             await login("admin", "new_password123");
             let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
             expect(elementCount).toEqual(1);
@@ -181,7 +181,7 @@ describe("Init", () => {
             await click(page, "form > div > .btn[type=submit]", 1);
             await sleep(4000);
 
-            await click(page, ".btn-danger.btn.me-1");
+            await click(page, ".btn-danger.btn.me-2");
             await login("admin", "new_password123");
 
             let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);

From 20af179a829dfd5b8b2b4f1fc4dc1abd93548e23 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 20:01:33 +0800
Subject: [PATCH 39/48] [test] try to auto test Windows and MacOS

---
 .github/workflows/auto-test.yml | 3 ++-
 package.json                    | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml
index 83d887e40..467344dd2 100644
--- a/.github/workflows/auto-test.yml
+++ b/.github/workflows/auto-test.yml
@@ -11,10 +11,11 @@ on:
 
 jobs:
   build:
-    runs-on: ubuntu-latest
+    runs-on: ${{ matrix.os }}
 
     strategy:
       matrix:
+        os: [macos-latest, ubuntu-latest, ubuntu-latest]
         node-version: [14.x, 15.x, 16.x]
         # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
 
diff --git a/package.json b/package.json
index 6698b522b..df8ac601f 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
         "start-server-dev": "cross-env NODE_ENV=development node server/server.js",
         "build": "vite build",
         "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test",
+        "test-with-build": "npm run build && npm test",
         "jest": "node test/prepare-jest.js && jest",
         "tsc": "tsc",
         "vite-preview-dist": "vite preview --host",

From 3423cb5d8e75d0869c086a1648917a4497ffee6c Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 20:05:12 +0800
Subject: [PATCH 40/48] [test] try to auto test Windows and MacOS

---
 .github/workflows/auto-test.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml
index 467344dd2..f78a7ed1c 100644
--- a/.github/workflows/auto-test.yml
+++ b/.github/workflows/auto-test.yml
@@ -15,7 +15,7 @@ jobs:
 
     strategy:
       matrix:
-        os: [macos-latest, ubuntu-latest, ubuntu-latest]
+        os: [macos-latest, ubuntu-latest, windows-latest]
         node-version: [14.x, 15.x, 16.x]
         # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
 

From deec15c09e0b4bda30d93c7b9f4293b0d6aa5421 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 20:14:24 +0800
Subject: [PATCH 41/48] [test] better job name

---
 .github/workflows/auto-test.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml
index f78a7ed1c..b5240d775 100644
--- a/.github/workflows/auto-test.yml
+++ b/.github/workflows/auto-test.yml
@@ -10,7 +10,7 @@ on:
     branches: [ master ]
 
 jobs:
-  build:
+  auto-test:
     runs-on: ${{ matrix.os }}
 
     strategy:

From 3d30ed3d3bea63537586ba30fe829d4fcd502d7d Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 20:16:15 +0800
Subject: [PATCH 42/48] update security policy

---
 SECURITY.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/SECURITY.md b/SECURITY.md
index 1271565a2..d2f000ed4 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -7,7 +7,8 @@ currently being supported with security updates.
 
 | Version | Supported          |
 | ------- | ------------------ |
-| 1.x.x  | :white_check_mark: |
+| 1.7.X  | :white_check_mark: |
+| < 1.7  | ❌ |
 
 ## Reporting a Vulnerability
 Please report security issues to uptime@kuma.pet.

From c0846124c2c8f23a565e4c9ef625c9a8ddff3953 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 21:23:28 +0800
Subject: [PATCH 43/48] update vite to 1.6.4, since it fixed the issue

---
 package-lock.json                             | 438 ++++++++++++++++--
 package.json                                  |   4 +-
 .../test_install_script/ubuntu1604.dockerfile |   8 +-
 3 files changed, 411 insertions(+), 39 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f18d58aac..762cfd98a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -59,7 +59,7 @@
             "devDependencies": {
                 "@babel/eslint-parser": "~7.15.7",
                 "@types/bootstrap": "~5.1.6",
-                "@vitejs/plugin-legacy": "~1.5.3",
+                "@vitejs/plugin-legacy": "~1.6.1",
                 "@vitejs/plugin-vue": "~1.9.2",
                 "@vue/compiler-sfc": "~3.2.19",
                 "core-js": "~3.18.1",
@@ -74,7 +74,7 @@
                 "stylelint": "~13.13.1",
                 "stylelint-config-standard": "~22.0.0",
                 "typescript": "~4.4.3",
-                "vite": "~2.5.10"
+                "vite": "~2.6.4"
             },
             "engines": {
                 "node": "14.*"
@@ -1685,16 +1685,16 @@
             }
         },
         "node_modules/@vitejs/plugin-legacy": {
-            "version": "1.5.3",
-            "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.5.3.tgz",
-            "integrity": "sha512-/b2x6dU+BbdW7C7KWxh9kMrVzv1JlUi1ucPQpSzWUUUVJjihbG+GRlpqcvfQ0p/TnAKl2d/VecbTLByVJJHORg==",
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.6.1.tgz",
+            "integrity": "sha512-isBi2ti+AlCZUpfA1P6L8gseltBy/qi6Rsi92aDzeL2elpwXgN4Hv/xLS2UUSSj9F0mFmxXCYPWlBPaJnlYamQ==",
             "dev": true,
             "dependencies": {
-                "@babel/standalone": "^7.14.9",
-                "core-js": "^3.16.0",
+                "@babel/standalone": "^7.15.7",
+                "core-js": "^3.18.1",
                 "magic-string": "^0.25.7",
                 "regenerator-runtime": "^0.13.9",
-                "systemjs": "^6.10.2"
+                "systemjs": "^6.10.3"
             },
             "engines": {
                 "node": ">=12.0.0"
@@ -3512,15 +3512,241 @@
             }
         },
         "node_modules/esbuild": {
-            "version": "0.12.29",
-            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz",
-            "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==",
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.4.tgz",
+            "integrity": "sha512-wMA5eUwpavTBiNl+It6j8OQuKVh69l6z4DKDLzoTIqC+gChnPpcmqdA8WNHptUHRnfyML+mKEQPlW7Mybj8gHg==",
             "dev": true,
             "hasInstallScript": true,
             "bin": {
                 "esbuild": "bin/esbuild"
+            },
+            "optionalDependencies": {
+                "esbuild-android-arm64": "0.13.4",
+                "esbuild-darwin-64": "0.13.4",
+                "esbuild-darwin-arm64": "0.13.4",
+                "esbuild-freebsd-64": "0.13.4",
+                "esbuild-freebsd-arm64": "0.13.4",
+                "esbuild-linux-32": "0.13.4",
+                "esbuild-linux-64": "0.13.4",
+                "esbuild-linux-arm": "0.13.4",
+                "esbuild-linux-arm64": "0.13.4",
+                "esbuild-linux-mips64le": "0.13.4",
+                "esbuild-linux-ppc64le": "0.13.4",
+                "esbuild-openbsd-64": "0.13.4",
+                "esbuild-sunos-64": "0.13.4",
+                "esbuild-windows-32": "0.13.4",
+                "esbuild-windows-64": "0.13.4",
+                "esbuild-windows-arm64": "0.13.4"
             }
         },
+        "node_modules/esbuild-android-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.4.tgz",
+            "integrity": "sha512-elDJt+jNyoHFId0/dKsuVYUPke3EcquIyUwzJCH17a3ERglN3A9aMBI5zbz+xNZ+FbaDNdpn0RaJHCFLbZX+fA==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "android"
+            ]
+        },
+        "node_modules/esbuild-darwin-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.4.tgz",
+            "integrity": "sha512-zJQGyHRAdZUXlRzbN7W+7ykmEiGC+bq3Gc4GxKYjjWTgDRSEly98ym+vRNkDjXwXYD3gGzSwvH35+MiHAtWvLA==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "darwin"
+            ]
+        },
+        "node_modules/esbuild-darwin-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.4.tgz",
+            "integrity": "sha512-r8oYvAtqSGq8HNTZCAx4TdLE7jZiGhX9ooGi5AQAey37MA6XNaP8ZNlw9OCpcgpx3ryU2WctXwIqPzkHO7a8dg==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "darwin"
+            ]
+        },
+        "node_modules/esbuild-freebsd-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.4.tgz",
+            "integrity": "sha512-u9DRGkn09EN8+lCh6z7FKle7awi17PJRBuAKdRNgSo5ZrH/3m+mYaJK2PR2URHMpAfXiwJX341z231tSdVe3Yw==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "freebsd"
+            ]
+        },
+        "node_modules/esbuild-freebsd-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.4.tgz",
+            "integrity": "sha512-q3B2k68Uf6gfjATjcK16DqxvjqRQkHL8aPoOfj4op+lSqegdXvBacB1d8jw8PxbWJ8JHpdTLdAVUYU80kotQXA==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "freebsd"
+            ]
+        },
+        "node_modules/esbuild-linux-32": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.4.tgz",
+            "integrity": "sha512-UUYJPHSiKAO8KoN3Ls/iZtgDLZvK5HarES96aolDPWZnq9FLx4dIHM/x2z4Rxv9IYqQ/DxlPoE2Co1UPBIYYeA==",
+            "cpu": [
+                "ia32"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/esbuild-linux-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.4.tgz",
+            "integrity": "sha512-+RnohAKiiUW4UHLGRkNR1AnENW1gCuDWuygEtd4jxTNPIoeC7lbXGor7rtgjj9AdUzFgOEvAXyNNX01kJ8NueQ==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/esbuild-linux-arm": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.4.tgz",
+            "integrity": "sha512-BH5gKve4jglS7UPSsfwHSX79I5agC/lm4eKoRUEyo8lwQs89frQSRp2Xup+6SFQnxt3md5EsKcd2Dbkqeb3gPA==",
+            "cpu": [
+                "arm"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/esbuild-linux-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.4.tgz",
+            "integrity": "sha512-+A188cAdd6QuSRxMIwRrWLjgphQA0LDAQ/ECVlrPVJwnx+1i64NjDZivoqPYLOTkSPIKntiWwMhhf0U5/RrPHQ==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/esbuild-linux-mips64le": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.4.tgz",
+            "integrity": "sha512-0xkwtPaUkG5xMTFGaQPe1AadSe5QAiQuD4Gix1O9k5Xo/U8xGIkw9UFUTvfEUeu71vFb6ZgsIacfP1NLoFjWNw==",
+            "cpu": [
+                "mips64el"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/esbuild-linux-ppc64le": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.4.tgz",
+            "integrity": "sha512-E1+oJPP7A+j23GPo3CEpBhGwG1bni4B8IbTA3/3rvzjURwUMZdcN3Fhrz24rnjzdLSHmULtOE4VsbT42h1Om4Q==",
+            "cpu": [
+                "ppc64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/esbuild-openbsd-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.4.tgz",
+            "integrity": "sha512-xEkI1o5HYxDzbv9jSox0EsDxpwraG09SRiKKv0W8pH6O3bt+zPSlnoK7+I7Q69tkvONkpIq5n2o+c55uq0X7cw==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "openbsd"
+            ]
+        },
+        "node_modules/esbuild-sunos-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.4.tgz",
+            "integrity": "sha512-bjXUMcODMnB6hQicLBBmmnBl7OMDyVpFahKvHGXJfDChIi5udiIRKCmFUFIRn+AUAKVlfrofRKdyPC7kBsbvGQ==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "sunos"
+            ]
+        },
+        "node_modules/esbuild-windows-32": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.4.tgz",
+            "integrity": "sha512-z4CH07pfyVY0XF98TCsGmLxKCl0kyvshKDbdpTekW9f2d+dJqn5mmoUyWhpSVJ0SfYWJg86FoD9nMbbaMVyGdg==",
+            "cpu": [
+                "ia32"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
+        "node_modules/esbuild-windows-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.4.tgz",
+            "integrity": "sha512-uVL11vORRPjocGLYam67rwFLd0LvkrHEs+JG+1oJN4UD9MQmNGZPa4gBHo6hDpF+kqRJ9kXgQSeDqUyRy0tj/Q==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
+        "node_modules/esbuild-windows-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.4.tgz",
+            "integrity": "sha512-vA6GLvptgftRcDcWngD5cMlL4f4LbL8JjU2UMT9yJ0MT5ra6hdZNFWnOeOoEtY4GtJ6OjZ0i+81sTqhAB0fMkg==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
         "node_modules/escalade": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -10066,15 +10292,15 @@
             }
         },
         "node_modules/vite": {
-            "version": "2.5.10",
-            "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.10.tgz",
-            "integrity": "sha512-0ObiHTi5AHyXdJcvZ67HMsDgVpjT5RehvVKv6+Q0jFZ7zDI28PF5zK9mYz2avxdA+4iJMdwCz6wnGNnn4WX5Gg==",
+            "version": "2.6.4",
+            "resolved": "https://registry.npmjs.org/vite/-/vite-2.6.4.tgz",
+            "integrity": "sha512-zNGZgjKGprdLKJ1g1taAvNt51JbGAdrAUU9hpLzgtlks+cXBxTZUsEAGEtLbF3UvlYOVAPXS8r9E9gxYAv6z+A==",
             "dev": true,
             "dependencies": {
-                "esbuild": "^0.12.17",
-                "postcss": "^8.3.6",
+                "esbuild": "^0.13.2",
+                "postcss": "^8.3.8",
                 "resolve": "^1.20.0",
-                "rollup": "^2.38.5"
+                "rollup": "^2.57.0"
             },
             "bin": {
                 "vite": "bin/vite.js"
@@ -10084,6 +10310,22 @@
             },
             "optionalDependencies": {
                 "fsevents": "~2.3.2"
+            },
+            "peerDependencies": {
+                "less": "*",
+                "sass": "*",
+                "stylus": "*"
+            },
+            "peerDependenciesMeta": {
+                "less": {
+                    "optional": true
+                },
+                "sass": {
+                    "optional": true
+                },
+                "stylus": {
+                    "optional": true
+                }
             }
         },
         "node_modules/vue": {
@@ -11951,16 +12193,16 @@
             }
         },
         "@vitejs/plugin-legacy": {
-            "version": "1.5.3",
-            "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.5.3.tgz",
-            "integrity": "sha512-/b2x6dU+BbdW7C7KWxh9kMrVzv1JlUi1ucPQpSzWUUUVJjihbG+GRlpqcvfQ0p/TnAKl2d/VecbTLByVJJHORg==",
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.6.1.tgz",
+            "integrity": "sha512-isBi2ti+AlCZUpfA1P6L8gseltBy/qi6Rsi92aDzeL2elpwXgN4Hv/xLS2UUSSj9F0mFmxXCYPWlBPaJnlYamQ==",
             "dev": true,
             "requires": {
-                "@babel/standalone": "^7.14.9",
-                "core-js": "^3.16.0",
+                "@babel/standalone": "^7.15.7",
+                "core-js": "^3.18.1",
                 "magic-string": "^0.25.7",
                 "regenerator-runtime": "^0.13.9",
-                "systemjs": "^6.10.2"
+                "systemjs": "^6.10.3"
             }
         },
         "@vitejs/plugin-vue": {
@@ -13373,10 +13615,140 @@
             }
         },
         "esbuild": {
-            "version": "0.12.29",
-            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz",
-            "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==",
-            "dev": true
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.4.tgz",
+            "integrity": "sha512-wMA5eUwpavTBiNl+It6j8OQuKVh69l6z4DKDLzoTIqC+gChnPpcmqdA8WNHptUHRnfyML+mKEQPlW7Mybj8gHg==",
+            "dev": true,
+            "requires": {
+                "esbuild-android-arm64": "0.13.4",
+                "esbuild-darwin-64": "0.13.4",
+                "esbuild-darwin-arm64": "0.13.4",
+                "esbuild-freebsd-64": "0.13.4",
+                "esbuild-freebsd-arm64": "0.13.4",
+                "esbuild-linux-32": "0.13.4",
+                "esbuild-linux-64": "0.13.4",
+                "esbuild-linux-arm": "0.13.4",
+                "esbuild-linux-arm64": "0.13.4",
+                "esbuild-linux-mips64le": "0.13.4",
+                "esbuild-linux-ppc64le": "0.13.4",
+                "esbuild-openbsd-64": "0.13.4",
+                "esbuild-sunos-64": "0.13.4",
+                "esbuild-windows-32": "0.13.4",
+                "esbuild-windows-64": "0.13.4",
+                "esbuild-windows-arm64": "0.13.4"
+            }
+        },
+        "esbuild-android-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.4.tgz",
+            "integrity": "sha512-elDJt+jNyoHFId0/dKsuVYUPke3EcquIyUwzJCH17a3ERglN3A9aMBI5zbz+xNZ+FbaDNdpn0RaJHCFLbZX+fA==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-darwin-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.4.tgz",
+            "integrity": "sha512-zJQGyHRAdZUXlRzbN7W+7ykmEiGC+bq3Gc4GxKYjjWTgDRSEly98ym+vRNkDjXwXYD3gGzSwvH35+MiHAtWvLA==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-darwin-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.4.tgz",
+            "integrity": "sha512-r8oYvAtqSGq8HNTZCAx4TdLE7jZiGhX9ooGi5AQAey37MA6XNaP8ZNlw9OCpcgpx3ryU2WctXwIqPzkHO7a8dg==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-freebsd-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.4.tgz",
+            "integrity": "sha512-u9DRGkn09EN8+lCh6z7FKle7awi17PJRBuAKdRNgSo5ZrH/3m+mYaJK2PR2URHMpAfXiwJX341z231tSdVe3Yw==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-freebsd-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.4.tgz",
+            "integrity": "sha512-q3B2k68Uf6gfjATjcK16DqxvjqRQkHL8aPoOfj4op+lSqegdXvBacB1d8jw8PxbWJ8JHpdTLdAVUYU80kotQXA==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-linux-32": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.4.tgz",
+            "integrity": "sha512-UUYJPHSiKAO8KoN3Ls/iZtgDLZvK5HarES96aolDPWZnq9FLx4dIHM/x2z4Rxv9IYqQ/DxlPoE2Co1UPBIYYeA==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-linux-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.4.tgz",
+            "integrity": "sha512-+RnohAKiiUW4UHLGRkNR1AnENW1gCuDWuygEtd4jxTNPIoeC7lbXGor7rtgjj9AdUzFgOEvAXyNNX01kJ8NueQ==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-linux-arm": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.4.tgz",
+            "integrity": "sha512-BH5gKve4jglS7UPSsfwHSX79I5agC/lm4eKoRUEyo8lwQs89frQSRp2Xup+6SFQnxt3md5EsKcd2Dbkqeb3gPA==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-linux-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.4.tgz",
+            "integrity": "sha512-+A188cAdd6QuSRxMIwRrWLjgphQA0LDAQ/ECVlrPVJwnx+1i64NjDZivoqPYLOTkSPIKntiWwMhhf0U5/RrPHQ==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-linux-mips64le": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.4.tgz",
+            "integrity": "sha512-0xkwtPaUkG5xMTFGaQPe1AadSe5QAiQuD4Gix1O9k5Xo/U8xGIkw9UFUTvfEUeu71vFb6ZgsIacfP1NLoFjWNw==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-linux-ppc64le": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.4.tgz",
+            "integrity": "sha512-E1+oJPP7A+j23GPo3CEpBhGwG1bni4B8IbTA3/3rvzjURwUMZdcN3Fhrz24rnjzdLSHmULtOE4VsbT42h1Om4Q==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-openbsd-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.4.tgz",
+            "integrity": "sha512-xEkI1o5HYxDzbv9jSox0EsDxpwraG09SRiKKv0W8pH6O3bt+zPSlnoK7+I7Q69tkvONkpIq5n2o+c55uq0X7cw==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-sunos-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.4.tgz",
+            "integrity": "sha512-bjXUMcODMnB6hQicLBBmmnBl7OMDyVpFahKvHGXJfDChIi5udiIRKCmFUFIRn+AUAKVlfrofRKdyPC7kBsbvGQ==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-windows-32": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.4.tgz",
+            "integrity": "sha512-z4CH07pfyVY0XF98TCsGmLxKCl0kyvshKDbdpTekW9f2d+dJqn5mmoUyWhpSVJ0SfYWJg86FoD9nMbbaMVyGdg==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-windows-64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.4.tgz",
+            "integrity": "sha512-uVL11vORRPjocGLYam67rwFLd0LvkrHEs+JG+1oJN4UD9MQmNGZPa4gBHo6hDpF+kqRJ9kXgQSeDqUyRy0tj/Q==",
+            "dev": true,
+            "optional": true
+        },
+        "esbuild-windows-arm64": {
+            "version": "0.13.4",
+            "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.4.tgz",
+            "integrity": "sha512-vA6GLvptgftRcDcWngD5cMlL4f4LbL8JjU2UMT9yJ0MT5ra6hdZNFWnOeOoEtY4GtJ6OjZ0i+81sTqhAB0fMkg==",
+            "dev": true,
+            "optional": true
         },
         "escalade": {
             "version": "3.1.1",
@@ -18379,16 +18751,16 @@
             }
         },
         "vite": {
-            "version": "2.5.10",
-            "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.10.tgz",
-            "integrity": "sha512-0ObiHTi5AHyXdJcvZ67HMsDgVpjT5RehvVKv6+Q0jFZ7zDI28PF5zK9mYz2avxdA+4iJMdwCz6wnGNnn4WX5Gg==",
+            "version": "2.6.4",
+            "resolved": "https://registry.npmjs.org/vite/-/vite-2.6.4.tgz",
+            "integrity": "sha512-zNGZgjKGprdLKJ1g1taAvNt51JbGAdrAUU9hpLzgtlks+cXBxTZUsEAGEtLbF3UvlYOVAPXS8r9E9gxYAv6z+A==",
             "dev": true,
             "requires": {
-                "esbuild": "^0.12.17",
+                "esbuild": "^0.13.2",
                 "fsevents": "~2.3.2",
-                "postcss": "^8.3.6",
+                "postcss": "^8.3.8",
                 "resolve": "^1.20.0",
-                "rollup": "^2.38.5"
+                "rollup": "^2.57.0"
             }
         },
         "vue": {
diff --git a/package.json b/package.json
index df8ac601f..7511e93d0 100644
--- a/package.json
+++ b/package.json
@@ -99,7 +99,7 @@
     "devDependencies": {
         "@babel/eslint-parser": "~7.15.7",
         "@types/bootstrap": "~5.1.6",
-        "@vitejs/plugin-legacy": "~1.5.3",
+        "@vitejs/plugin-legacy": "~1.6.1",
         "@vitejs/plugin-vue": "~1.9.2",
         "@vue/compiler-sfc": "~3.2.19",
         "core-js": "~3.18.1",
@@ -114,7 +114,7 @@
         "stylelint": "~13.13.1",
         "stylelint-config-standard": "~22.0.0",
         "typescript": "~4.4.3",
-        "vite": "~2.5.10"
+        "vite": "~2.6.4"
     },
     "jest": {
         "verbose": true,
diff --git a/test/test_install_script/ubuntu1604.dockerfile b/test/test_install_script/ubuntu1604.dockerfile
index d689aead8..34230a144 100644
--- a/test/test_install_script/ubuntu1604.dockerfile
+++ b/test/test_install_script/ubuntu1604.dockerfile
@@ -1,10 +1,10 @@
 FROM ubuntu:16.04
+RUN apt-get update
+RUN apt --yes install curl
 
 # Test invalid node version, these commands install nodejs 10
-RUN apt-get update
-RUN apt --yes install nodejs
+#RUN apt --yes install nodejs
 # RUN ln -s /usr/bin/nodejs /usr/bin/node
 # RUN node -v
 
-COPY ./install.sh .
-RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0
+RUN curl -o kuma_install.sh http://git.kuma.pet/install.sh && bash kuma_install.sh local /opt/uptime-kuma 3000 0.0.0.0

From bf4ac0cf175639a983072c9d112afc4f46141e96 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 21:24:10 +0800
Subject: [PATCH 44/48] fix dockerfile issue on arm

---
 dockerfile        | 2 ++
 dockerfile-alpine | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/dockerfile b/dockerfile
index 969d5ff38..b6c48b281 100644
--- a/dockerfile
+++ b/dockerfile
@@ -1,6 +1,8 @@
 FROM louislam/uptime-kuma:base-debian AS build
 WORKDIR /app
 
+ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
+
 COPY . .
 RUN npm install --legacy-peer-deps && \
     npm run build && \
diff --git a/dockerfile-alpine b/dockerfile-alpine
index 178afcabb..7604c40cb 100644
--- a/dockerfile-alpine
+++ b/dockerfile-alpine
@@ -1,6 +1,8 @@
 FROM louislam/uptime-kuma:base-alpine AS build
 WORKDIR /app
 
+ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
+
 COPY . .
 RUN npm install --legacy-peer-deps && \
     npm run build && \

From cdf81a36d3f5d86c9ed15c144107fc5c4de46cab Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 21:42:36 +0800
Subject: [PATCH 45/48] fix broken animation caused by #521

---
 src/components/HeartbeatBar.vue | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index 459a4ad6f..56e267650 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -183,9 +183,6 @@ export default {
 }
 
 .hp-bar-big {
-    display: flex;
-    justify-content: flex-end;
-
     .beat {
         display: inline-block;
         background-color: $primary;

From 9f1e7b0a88bfa8a3f2deee9125e4de2f4d0ae76b Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 21:47:11 +0800
Subject: [PATCH 46/48] Revert "fix(heartbeat-bar): cleanup css styling and
 minor syntax issues"

This reverts commit 3d6c8b7f
---
 src/assets/app.scss             |  2 +-
 src/components/HeartbeatBar.vue | 20 ++++++++++----------
 src/components/MonitorList.vue  | 24 ++++++++++++------------
 src/components/Uptime.vue       | 16 ++++++++--------
 4 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/src/assets/app.scss b/src/assets/app.scss
index 7e4fc07e4..3072e37c4 100644
--- a/src/assets/app.scss
+++ b/src/assets/app.scss
@@ -321,7 +321,7 @@ h2 {
     .item {
         display: block;
         text-decoration: none;
-        padding: 14px 15px;
+        padding: 13px 15px 10px 15px;
         border-radius: 10px;
         transition: all ease-in-out 0.15s;
 
diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index 56e267650..4dc2c712c 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -38,7 +38,7 @@ export default {
             beatMargin: 4,
             move: false,
             maxBeat: -1,
-        };
+        }
     },
     computed: {
 
@@ -69,12 +69,12 @@ export default {
             if (start < 0) {
                 // Add empty placeholder
                 for (let i = start; i < 0; i++) {
-                    placeholders.push(0);
+                    placeholders.push(0)
                 }
                 start = 0;
             }
 
-            return placeholders.concat(this.beatList.slice(start));
+            return placeholders.concat(this.beatList.slice(start))
         },
 
         wrapStyle() {
@@ -84,7 +84,7 @@ export default {
             return {
                 padding: `${topBottom}px ${leftRight}px`,
                 width: "100%",
-            };
+            }
         },
 
         barStyle() {
@@ -94,12 +94,12 @@ export default {
                 return {
                     transition: "all ease-in-out 0.25s",
                     transform: `translateX(${width}px)`,
-                };
+                }
 
             }
             return {
                 transform: "translateX(0)",
-            };
+            }
 
         },
 
@@ -109,7 +109,7 @@ export default {
                 height: this.beatHeight + "px",
                 margin: this.beatMargin + "px",
                 "--hover-scale": this.hoverScale,
-            };
+            }
         },
 
     },
@@ -120,7 +120,7 @@ export default {
 
                 setTimeout(() => {
                     this.move = false;
-                }, 300);
+                }, 300)
             },
             deep: true,
         },
@@ -162,7 +162,7 @@ export default {
     methods: {
         resize() {
             if (this.$refs.wrap) {
-                this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2));
+                this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2))
             }
         },
 
@@ -170,7 +170,7 @@ export default {
             return `${this.$root.datetime(beat.time)} - ${beat.msg}`;
         }
     },
-};
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue
index 77efe564a..2093331a7 100644
--- a/src/components/MonitorList.vue
+++ b/src/components/MonitorList.vue
@@ -3,10 +3,10 @@
         <div class="list-header">
             <div class="placeholder"></div>
             <div class="search-wrapper">
-                <a v-if="!searchText" class="search-icon">
+                <a v-if="searchText == ''" class="search-icon">
                     <font-awesome-icon icon="search" />
                 </a>
-                <a v-if="searchText" class="search-icon" @click="clearSearchText">
+                <a v-if="searchText != ''" class="search-icon" @click="clearSearchText">
                     <font-awesome-icon icon="times" />
                 </a>
                 <input v-model="searchText" class="form-control search-input" :placeholder="$t('Search...')" />
@@ -19,21 +19,21 @@
 
             <router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }">
                 <div class="row">
-                    <div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar === 'bottom' || $root.userHeartbeatBar === 'none' }">
+                    <div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
                         <div class="info">
                             <Uptime :monitor="item" type="24" :pill="true" />
-                            <span class="ms-1">{{ item.name }}</span>
+                            {{ item.name }}
                         </div>
                         <div class="tags">
                             <Tag v-for="tag in item.tags" :key="tag" :item="tag" :size="'sm'" />
                         </div>
                     </div>
-                    <div v-show="$root.userHeartbeatBar === 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4 small-padding">
+                    <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4">
                         <HeartbeatBar size="small" :monitor-id="item.id" />
                     </div>
                 </div>
 
-                <div v-if="$root.userHeartbeatBar === 'bottom'" class="row">
+                <div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
                     <div class="col-12">
                         <HeartbeatBar size="small" :monitor-id="item.id" />
                     </div>
@@ -63,7 +63,7 @@ export default {
     data() {
         return {
             searchText: "",
-        };
+        }
     },
     computed: {
         sortedMonitorList() {
@@ -92,17 +92,17 @@ export default {
                 }
 
                 return m1.name.localeCompare(m2.name);
-            });
+            })
 
             // Simple filter by search text
             // finds monitor name, tag name or tag value
-            if (this.searchText) {
+            if (this.searchText != "") {
                 const loweredSearchText = this.searchText.toLowerCase();
                 result = result.filter(monitor => {
                     return monitor.name.toLowerCase().includes(loweredSearchText)
                     || monitor.tags.find(tag => tag.name.toLowerCase().includes(loweredSearchText)
-                    || tag.value?.toLowerCase().includes(loweredSearchText));
-                });
+                    || tag.value?.toLowerCase().includes(loweredSearchText))
+                })
             }
 
             return result;
@@ -116,7 +116,7 @@ export default {
             this.searchText = "";
         }
     },
-};
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue
index a7931c1b5..a4bf22f68 100644
--- a/src/components/Uptime.vue
+++ b/src/components/Uptime.vue
@@ -22,33 +22,33 @@ export default {
                 return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%";
             }
 
-            return this.$t("notAvailableShort");
+            return this.$t("notAvailableShort")
         },
 
         color() {
             if (this.lastHeartBeat.status === 0) {
-                return "danger";
+                return "danger"
             }
 
             if (this.lastHeartBeat.status === 1) {
-                return "primary";
+                return "primary"
             }
 
             if (this.lastHeartBeat.status === 2) {
-                return "warning";
+                return "warning"
             }
 
-            return "secondary";
+            return "secondary"
         },
 
         lastHeartBeat() {
             if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) {
-                return this.$root.lastHeartbeatList[this.monitor.id];
+                return this.$root.lastHeartbeatList[this.monitor.id]
             }
 
             return {
                 status: -1,
-            };
+            }
         },
 
         className() {
@@ -59,7 +59,7 @@ export default {
             return "";
         },
     },
-};
+}
 </script>
 
 <style>

From 469f7a3e32619d947af27e7e16a0039d05d4bf8a Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 22:33:39 +0800
Subject: [PATCH 47/48] Update README.md

---
 README.md | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 4ad1c5554..be5494b5f 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 
 It is a self-hosted monitoring tool like "Uptime Robot".
 
-<img src="https://louislam.net/uptimekuma/1.jpg" width="512" alt="" />
+<img src="https://uptime.kuma.pet/img/dark.jpg" width="700" alt="" />
 
 ## 🥔 Live Demo
 
@@ -86,9 +86,13 @@ https://github.com/louislam/uptime-kuma/projects/1
 
 ## 🖼 More Screenshots
 
-Dark Mode:
+Light Mode:
 
-<img src="https://user-images.githubusercontent.com/1336778/128710166-908f8d88-9256-43f3-9c49-bfc2c56011d2.png" width="400" alt="" />
+<img src="https://uptime.kuma.pet/img/light.jpg" width="512" alt="" />
+
+Status Page:
+
+<img src="https://user-images.githubusercontent.com/1336778/133384019-962e1120-6c3a-481f-9d07-d7df765e9ba4.png" width="512" alt="" />
 
 Settings Page:
 

From adfe640f422991f9a0213b8a66e18ace9c742ca0 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Thu, 7 Oct 2021 22:53:13 +0800
Subject: [PATCH 48/48] show fewer beat on mobile

---
 src/components/MonitorList.vue | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue
index 2093331a7..bd771f8f0 100644
--- a/src/components/MonitorList.vue
+++ b/src/components/MonitorList.vue
@@ -19,7 +19,7 @@
 
             <router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }">
                 <div class="row">
-                    <div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
+                    <div class="col-9 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
                         <div class="info">
                             <Uptime :monitor="item" type="24" :pill="true" />
                             {{ item.name }}
@@ -28,7 +28,7 @@
                             <Tag v-for="tag in item.tags" :key="tag" :item="tag" :size="'sm'" />
                         </div>
                     </div>
-                    <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4">
+                    <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-3 col-md-4">
                         <HeartbeatBar size="small" :monitor-id="item.id" />
                     </div>
                 </div>
@@ -63,7 +63,7 @@ export default {
     data() {
         return {
             searchText: "",
-        }
+        };
     },
     computed: {
         sortedMonitorList() {
@@ -92,7 +92,7 @@ export default {
                 }
 
                 return m1.name.localeCompare(m2.name);
-            })
+            });
 
             // Simple filter by search text
             // finds monitor name, tag name or tag value
@@ -101,8 +101,8 @@ export default {
                 result = result.filter(monitor => {
                     return monitor.name.toLowerCase().includes(loweredSearchText)
                     || monitor.tags.find(tag => tag.name.toLowerCase().includes(loweredSearchText)
-                    || tag.value?.toLowerCase().includes(loweredSearchText))
-                })
+                    || tag.value?.toLowerCase().includes(loweredSearchText));
+                });
             }
 
             return result;
@@ -116,7 +116,7 @@ export default {
             this.searchText = "";
         }
     },
-}
+};
 </script>
 
 <style lang="scss" scoped>