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/14] 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 de3fe52b..567b1eae 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 c45a5262..99389d29 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 f8039d58..9e16d4e4 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 7d5312c3..f6dbc748 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 1511f411..34a98688 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 3e1a7bdc..4c6e6918 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 10fb6df7..b8ea6dfa 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 2c805e0a..ea848222 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 ae836b5c..7f0a9417 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 64e4ef9c..b980f546 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 0776a154..328da8bd 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 b86a6260..fa9b9c64 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 8598d07f..212aba6a 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 1ec2cdfc..9cd05fb5 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 45ba26c8..0c36cfed 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 a59c804a..1581c027 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 1554a83d..3423910f 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 37704225..ca568944 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/14] 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 78d89091..2cf5eed2 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 ca568944..c4525aef 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/14] 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 2cf5eed2..fb8ff2e0 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 567b1eae..e0f116f0 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 212aba6a..e3c9e7aa 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 c4525aef..c9775403 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/14] 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 e0f116f0..30abfc90 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 f6dbc748..0d7da26a 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 c9775403..948ed437 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/14] 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 fb8ff2e0..0c2035de 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 30abfc90..f485405b 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 948ed437..64546797 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 138ddf56089d7dea14669ff7a62458a7ad264f2a Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Thu, 30 Sep 2021 19:22:17 +0800
Subject: [PATCH 06/14] 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 f485405b..c10e23cf 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 0d7da26a..cb52c0c1 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 4c6e6918..2d174146 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 b8ea6dfa..fb0eb7f4 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 ea848222..37a2e095 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 7f0a9417..af6e9d83 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 b980f546..26f404d2 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 328da8bd..8f808210 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 9cd05fb5..8675922b 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 0c36cfed..2bc76486 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 3423910f..c7031f74 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 39464c3f..b4b7769d 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 07/14] 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 0c2035de..659f5726 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 c7031f74..3cfc940e 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 b4b7769d..4cefa73c 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 54d2fbcc022431baad5f0c7883ad63ae8f89d310 Mon Sep 17 00:00:00 2001
From: zsxeee <zsxeee@qq.com>
Date: Wed, 6 Oct 2021 18:21:31 +0800
Subject: [PATCH 08/14] 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 5c17475e..75510397 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 6d91d2b5..3395bb30 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 09/14] 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 628ccd3a..6a1e55db 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 7558d682..61918970 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 10/14] 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 6a1e55db..a4625c3e 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 61918970..a1a0bb25 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 8f808210..465b0dc9 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 8675922b..0e1e7cb0 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 11/14] 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 7a660cb3..a6be8232 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 12/14] 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 96f221e8..6a72dc82 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 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 13/14] 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 628ccd3a..43b5479e 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 7558d682..8c22401b 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 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 14/14] ignore .env

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

diff --git a/.dockerignore b/.dockerignore
index 539e9328..62925caa 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 0863015d..cd654d90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ dist-ssr
 /private
 /out
 /tmp
+.env