Browse Source

Added internationalize, and fixed some small bugs.

Hintay 3 years ago
parent
commit
64f7de47dc

+ 4 - 0
frontend/Makefile

@@ -6,7 +6,11 @@ LOGNAME ?= $(shell logname)
 
 # adding the name of the user's login name to the template file, so that
 # on a multi-user system several users can run this without interference
+ifeq ($(OS),Windows_NT)
+TEMPLATE_POT ?=  ./template-$(LOGNAME).pot
+else
 TEMPLATE_POT ?= /tmp/template-$(LOGNAME).pot
+endif
 
 # Where to find input files (it can be multiple paths).
 INPUT_FILES = ./src

+ 1 - 1
frontend/src/components/Breadcrumb/Breadcrumb.vue

@@ -2,7 +2,7 @@
     <a-breadcrumb class="breadcrumb">
         <a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
             <router-link
-                v-if="item.name != name && index != 1"
+                v-if="item.name !== name && index !== 1"
                 :to="{ path: item.path === '' ? '/' : item.path }"
             >{{ item.name }}
             </router-link>

+ 1 - 0
frontend/src/layouts/BaseLayout.vue

@@ -50,6 +50,7 @@ import FooterLayout from './FooterLayout'
 import PageHeader from '@/components/PageHeader/PageHeader'
 import zh_CN from 'ant-design-vue/es/locale/zh_CN'
 
+// TODO: Change language for base layout.
 export default {
     name: 'BaseLayout',
     data() {

+ 2 - 1
frontend/src/layouts/HeaderLayout.vue

@@ -19,13 +19,14 @@
 
 <script>
 import SetLanguage from '@/components/SetLanguage/SetLanguage'
+import $gettext from "@/lib/translate/gettext";
 export default {
     name: 'HeaderComponent',
     components: {SetLanguage},
     methods: {
         logout() {
             this.$api.auth.logout().then(() => {
-                this.$message.success('注销成功')
+                this.$message.success($gettext('Logout successful'))
                 this.$router.push('/login')
             })
         }

+ 1 - 1
frontend/src/layouts/SideBar.vue

@@ -8,7 +8,7 @@
             v-model="selectedKey"
         >
             <template v-for="sidebar in visible(sidebars)">
-                <a-menu-item v-if="!sidebar.children" :key="sidebar.name"
+                <a-menu-item v-if="!sidebar.children || sidebar.meta.hideChildren === true" :key="sidebar.name"
                              @click="$router.push('/'+sidebar.path).catch(() => {})">
                     <a-icon :type="sidebar.meta.icon"/>
                     <span>{{ sidebar.name }}</span>

+ 8 - 8
frontend/src/lib/translate/gettext.js

@@ -2,14 +2,14 @@ import {translate} from 'vue-gettext'
 import store from '@/lib/store'
 import {availableLanguages} from '@/lib/translate/index'
 
-let lang = window.navigator.language
-if (!lang.includes('zh')) {
-    lang = lang.split('-')[0]
-} else {
-    lang = lang.replace('-', '_')
+let lang = window.navigator.language.replace('-', '_')
+if(availableLanguages[lang] === undefined) {
+    lang = lang.split('_')[0]
+    if(availableLanguages[lang] === undefined)
+        lang = 'en'
 }
 store.getters.current_language ||
-store.commit('set_language', availableLanguages[lang] ? lang : 'en')
+store.commit('set_language', lang)
 
 const config = {
     language: store.getters.current_language,
@@ -19,8 +19,8 @@ const config = {
 }
 
 // easygettext aliases
-const {
-    gettext: $gettext,
+export const {
+    gettext: $gettext, gettextInterpolate: $interpolate
 } = translate
 
 translate.initTranslations(store.state.settings.translations, config)

+ 328 - 37
frontend/src/locale/en/LC_MESSAGES/app.po

@@ -10,110 +10,401 @@ msgstr ""
 "Generated-By: easygettext\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: src/router/index.js:98
+#: src/router/index.js:100
 msgid "404 Not Found"
 msgstr ""
 
-#: src/router/index.js:76
+#: src/router/index.js:78
 msgid "About"
 msgstr ""
 
-#: src/router/index.js:47
-msgid "Add Sites"
+#: src/views/config/Config.vue:18 src/views/domain/DomainList.vue:29
+#: src/views/user/User.vue:35
+msgid "Action"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Cached:"
+#: src/router/index.js:47 src/views/domain/DomainAdd.vue:18
+#: src/views/domain/DomainEdit.vue:45
+msgid "Add Site"
 msgstr ""
 
-#: src/router/index.js:60
-msgid "Config"
+#: src/views/domain/DomainAdd.vue:2
+msgid ""
+"Add site here first, then you can configure TLS on the domain edit page."
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:194
+msgid "Auto-renewal disabled for %{name}"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:188
+msgid "Auto-renewal enabled for %{name}"
+msgstr ""
+
+#: src/views/other/About.vue:11
+msgid "Build with"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:23
+msgid "Cancel"
+msgstr ""
+
+#: src/views/domain/columns.js:44
+msgid "Certificate Auto-renewal"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:11 src/views/domain/CertInfo.vue:2
+msgid "Certificate has expired"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:15 src/views/domain/CertInfo.vue:2
+msgid "Certificate is valid"
+msgstr ""
+
+#: src/views/domain/columns.js:61
+msgid "Certificate Path (ssl_certificate)"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:2
+msgid "Certificate Status"
+msgstr ""
+
+#: src/views/domain/columns.js:4
+msgid "Configuration Name"
+msgstr ""
+
+#: src/views/config/Config.vue:8
+msgid "Configurations"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:52
+msgid "CPU"
+msgstr ""
+
+#: src/views/user/User.vue:23
+msgid "Created at"
 msgstr ""
 
 #: src/router/index.js:19
 msgid "Dashboard"
 msgstr ""
 
-#: src/router/index.js:124
-msgid "Detected version update, this page will automatically refresh."
+#: src/views/other/Install.vue:104
+msgid "Database (Optional, default: database)"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:95
-msgid "Free:"
+#: src/router/index.js:126
+msgid "Detected version update, this page will refresh."
+msgstr ""
+
+#: src/views/other/About.vue:11
+msgid "Development Mode"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:196
+msgid "Disable auto-renewal failed for %{name}"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:7 src/views/domain/DomainList.vue:2
+#: src/views/domain/DomainList.vue:17
+msgid "Disabled"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:55
+msgid "Disabled successfully"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:133
+msgid "Do you want to change the template to support the TLS?"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:45
+msgid "Edit %{n}"
+msgstr ""
+
+#: src/router/index.js:68 src/views/config/ConfigEdit.vue:15
+msgid "Edit Configuration"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:91
+msgid "Edit Configuration File"
+msgstr ""
+
+#: src/router/index.js:51
+msgid "Edit Site"
+msgstr ""
+
+#: src/views/other/Install.vue:30
+msgid "Email (*)"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:190
+msgid "Enable auto-renewal failed for %{name}"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:33
+msgid "Enable failed"
+msgstr ""
+
+#: src/views/domain/columns.js:35
+msgid "Enable TLS"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:8 src/views/domain/DomainList.vue:3
+#: src/views/domain/DomainList.vue:16
+msgid "Enabled"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:27 src/views/domain/DomainList.vue:46
+msgid "Enabled successfully"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:5
+msgid "Expiration Date: %{date}"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:59
+msgid "Failed to disable %{msg}"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:50
+msgid "Failed to enable %{msg}"
+msgstr ""
+
+#: src/views/other/Error.vue:9
+msgid "File Not Found"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:9 src/views/domain/DomainEdit.vue:3
+msgid "Getting Certificate from Let's Encrypt"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:156
+msgid "Getting the certificate, please wait..."
 msgstr ""
 
 #: src/router/index.js:12
 msgid "Home"
 msgstr ""
 
-#: src/router/index.js:86
+#: src/views/domain/columns.js:28
+msgid "HTTP Listen Port"
+msgstr ""
+
+#: src/views/domain/columns.js:54
+msgid "HTTPS Listen Port"
+msgstr ""
+
+#: src/views/domain/columns.js:22
+msgid "Index (index)"
+msgstr ""
+
+#: src/router/index.js:88 src/views/other/Install.vue:50
 msgid "Install"
 msgstr ""
 
+#: src/views/domain/CertInfo.vue:3
+msgid "Intermediate Certification Authorities: %{issuer}"
+msgstr ""
+
+#: src/views/other/Install.vue:45
+msgid "Invalid E-mail!"
+msgstr ""
+
+#: src/views/user/User.vue:19
+msgid "Leave blank for no change"
+msgstr ""
+
+#: src/views/other/About.vue:16
+msgctxt "Project"
+msgid "License"
+msgstr ""
+
 #: src/views/dashboard/DashBoard.vue:13
-msgid "Load averages"
+msgid "Load Averages:"
 msgstr ""
 
-#: src/router/index.js:92
+#: src/router/index.js:94 src/views/other/Login.vue:24
 msgid "Login"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:23
-msgid "Memory"
+#: src/views/other/Login.vue:28
+msgid "Login successful"
 msgstr ""
 
-#: src/router/index.js:68
-msgid "Modify Config"
+#: src/layouts/HeaderLayout.vue:9
+msgid "Logout successful"
 msgstr ""
 
-#: src/router/index.js:51
-msgid "Modify Sites"
+#: src/views/domain/DomainEdit.vue:12 src/views/domain/DomainEdit.vue:6
+msgid ""
+"Make sure you have configured a reverse proxy for <code>.well-known</code> "
+"directory to <code>HTTPChallengePort</code> (default: 9180) before getting "
+"the certificate."
+msgstr ""
+
+#: src/router/index.js:60
+msgid "Manage Configs"
+msgstr ""
+
+#: src/router/index.js:35 src/views/domain/DomainList.vue:21
+msgid "Manage Sites"
+msgstr ""
+
+#: src/router/index.js:27
+msgid "Manage Users"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:24
+msgid "Memory"
 msgstr ""
 
-#: src/router/index.js:104
+#: src/views/config/Config.vue:5 src/views/domain/DomainList.vue:5
+msgid "Name"
+msgstr ""
+
+#: src/router/index.js:106
 msgid "Not Found"
 msgstr ""
 
-#: src/router/index.js:128
+#: src/views/domain/CertInfo.vue:7
+msgid "Not Valid Before: %{date}"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:155
+msgid ""
+"Note: The server_name in the current configuration must be the domain name "
+"you need to get the certificate."
+msgstr ""
+
+#: src/router/index.js:130
 msgid "OK"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:96
-msgid "Physical memory:"
+#: src/views/other/Login.vue:55 src/views/user/User.vue:13
+msgid "Password"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:38
-msgid "Server status"
+#: src/views/other/Install.vue:82
+msgid "Password (*)"
+msgstr ""
+
+#: src/views/other/Install.vue:49
+msgid "Please input your E-mail!"
+msgstr ""
+
+#: src/views/other/Install.vue:95 src/views/other/Login.vue:68
+msgid "Please input your password!"
+msgstr ""
+
+#: src/views/other/Install.vue:72 src/views/other/Login.vue:45
+msgid "Please input your username!"
 msgstr ""
 
-#: src/router/index.js:35
-msgid "Sites"
+#: src/views/domain/columns.js:67
+msgid "Private Key Path (ssl_certificate_key)"
+msgstr ""
+
+#: src/views/other/About.vue:8
+msgid "Project Team"
+msgstr ""
+
+#: src/views/domain/columns.js:16
+msgid "Root Directory (root)"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:6 src/views/domain/DomainAdd.vue:6
+#: src/views/domain/DomainEdit.vue:24
+msgid "Save"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:47 src/views/domain/DomainAdd.vue:38
+#: src/views/domain/DomainEdit.vue:151
+msgid "Save error %{msg}"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:44 src/views/domain/DomainAdd.vue:24
+#: src/views/domain/DomainEdit.vue:145
+msgid "Saved successfully"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:35 src/views/domain/DomainEdit.vue:71
+#: src/views/other/Login.vue:33
+msgid "Server error"
+msgstr ""
+
+#: src/views/domain/columns.js:10
+msgid "Server Names (server_name)"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:38
+msgid "Server Status"
 msgstr ""
 
 #: src/router/index.js:43
 msgid "Sites List"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:32
+#: src/views/domain/DomainList.vue:11
+msgid "Status"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:33
 msgid "Storage"
 msgstr ""
 
-#: src/router/index.js:123
+#: src/views/domain/CertInfo.vue:4
+msgid "Subject Name: %{name}"
+msgstr ""
+
+#: src/router/index.js:125
 msgid "System message"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:124
-msgid "Total:"
+#: src/views/domain/columns.js:50
+msgid ""
+"The certificate for the domain will be checked every hour, and will be "
+"renewed if it has been more than 1 month since it was last issued.<br/>If "
+"you do not have a certificate before, please click \"Getting Certificate "
+"from Let's Encrypt\" first."
+msgstr ""
+
+#: src/views/other/Install.vue:119
+msgid "The filename cannot contain the following characters: %{c}"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:4
+msgid ""
+"The following values will only take effect if you have the corresponding "
+"fields in your configuration file. The configuration filename cannot be "
+"changed after it has been created."
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:134
+msgid "This operation will lose the custom configuration."
+msgstr ""
+
+#: src/views/config/Config.vue:11 src/views/domain/DomainList.vue:22
+#: src/views/user/User.vue:29
+msgid "Updated at"
 msgstr ""
 
 #: src/views/dashboard/DashBoard.vue:12
 msgid "Uptime"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:94 src/views/dashboard/DashBoard.vue:123
-msgid "Used:"
+#: src/views/dashboard/DashBoard.vue:95
+msgid "Used: %{u}, Cached: %{c}, Free: %{f}, Physical Memory: %{p}"
 msgstr ""
 
-#: src/router/index.js:27
-msgid "Users"
+#: src/views/dashboard/DashBoard.vue:123
+msgid "Used: %{used} / Total: %{total}"
+msgstr ""
+
+#: src/views/other/Login.vue:32 src/views/user/User.vue:5
+msgid "Username"
+msgstr ""
+
+#: src/views/other/Install.vue:59
+msgid "Username (*)"
 msgstr ""

+ 0 - 123
frontend/src/locale/en/LC_MESSAGES/app.po~

@@ -1,123 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
-"Language: en\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: easygettext\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: src/router/index.js:98
-msgid "404 Not Found"
-msgstr ""
-
-#: src/router/index.js:76
-msgid "About"
-msgstr ""
-
-#: src/router/index.js:47
-msgid "Add Sites"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Cached:"
-msgstr ""
-
-#: src/router/index.js:60
-msgid "Config"
-msgstr ""
-
-#: src/router/index.js:19
-msgid "Dashboard"
-msgstr ""
-
-#: src/router/index.js:124
-msgid "Detected version update, this page will automatically refresh."
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:95
-msgid "Free:"
-msgstr ""
-
-#: src/router/index.js:12
-msgid "Home"
-msgstr ""
-
-#: src/router/index.js:86
-msgid "Install"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:13
-msgid "Load averages"
-msgstr ""
-
-#: src/router/index.js:92
-msgid "Login"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:23
-msgid "Memory"
-msgstr ""
-
-#: src/router/index.js:68
-msgid "Modify Config"
-msgstr ""
-
-#: src/router/index.js:51
-msgid "Modify Sites"
-msgstr ""
-
-#: src/router/index.js:104
-msgid "Not Found"
-msgstr ""
-
-#: src/router/index.js:128
-msgid "OK"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:96
-msgid "Physical memory:"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:38
-msgid "Server status"
-msgstr ""
-
-#: src/router/index.js:35
-msgid "Sites"
-msgstr ""
-
-#: src/router/index.js:43
-msgid "Sites List"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:32
-msgid "Storage"
-msgstr ""
-
-#: src/router/index.js:123
-msgid "System message"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:124
-msgid "Total: "
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:12
-msgid "Uptime"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Used:"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:123
-msgid "Used: "
-msgstr ""
-
-#: src/router/index.js:27
-msgid "Users"
-msgstr ""

BIN
frontend/src/locale/zh_CN/LC_MESSAGES/app.mo


+ 348 - 51
frontend/src/locale/zh_CN/LC_MESSAGES/app.po

@@ -10,112 +10,409 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: easygettext\n"
-"X-Generator: Poedit 3.0.1\n"
+"X-Generator: Poedit 2.2\n"
 
-#: src/router/index.js:98
+#: src/router/index.js:100
 msgid "404 Not Found"
-msgstr "404 找到页面"
+msgstr "404 找到页面"
 
-#: src/router/index.js:76
+#: src/router/index.js:78
 msgid "About"
 msgstr "关于"
 
-#: src/router/index.js:47
-msgid "Add Sites"
+#: src/views/config/Config.vue:18 src/views/domain/DomainList.vue:29
+#: src/views/user/User.vue:35
+msgid "Action"
+msgstr "操作"
+
+#: src/router/index.js:47 src/views/domain/DomainAdd.vue:18
+#: src/views/domain/DomainEdit.vue:45
+msgid "Add Site"
 msgstr "添加站点"
 
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Cached:"
-msgstr "缓存:"
+#: src/views/domain/DomainAdd.vue:2
+msgid ""
+"Add site here first, then you can configure TLS on the domain edit page."
+msgstr "在这里添加站点,完成后可进入编辑页面配置 TLS。"
 
-#: src/router/index.js:60
-msgid "Config"
+#: src/views/domain/DomainEdit.vue:194
+msgid "Auto-renewal disabled for %{name}"
+msgstr "成功关闭 %{name} 自动续签"
+
+#: src/views/domain/DomainEdit.vue:188
+msgid "Auto-renewal enabled for %{name}"
+msgstr "成功启用 %{name} 自动续签"
+
+#: src/views/other/About.vue:11
+msgid "Build with"
+msgstr "构建基于"
+
+#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:23
+msgid "Cancel"
+msgstr "取消"
+
+#: src/views/domain/columns.js:44
+msgid "Certificate Auto-renewal"
+msgstr "证书自动续签"
+
+#: src/views/domain/CertInfo.vue:11 src/views/domain/CertInfo.vue:2
+msgid "Certificate has expired"
+msgstr "此证书已过期"
+
+#: src/views/domain/CertInfo.vue:15 src/views/domain/CertInfo.vue:2
+msgid "Certificate is valid"
+msgstr "此证书有效"
+
+#: src/views/domain/columns.js:61
+msgid "Certificate Path (ssl_certificate)"
+msgstr "TLS 证书路径 (ssl_certificate)"
+
+#: src/views/domain/CertInfo.vue:2
+msgid "Certificate Status"
+msgstr "证书状态"
+
+#: src/views/domain/columns.js:4
+msgid "Configuration Name"
+msgstr "配置名称"
+
+#: src/views/config/Config.vue:8
+msgid "Configurations"
 msgstr "配置"
 
+#: src/views/dashboard/DashBoard.vue:52
+msgid "CPU"
+msgstr "CPU"
+
+#: src/views/user/User.vue:23
+msgid "Created at"
+msgstr "创建时间"
+
 #: src/router/index.js:19
 msgid "Dashboard"
 msgstr "仪表盘"
 
-#: src/router/index.js:124
-msgid "Detected version update, this page will automatically refresh."
-msgstr "检测到版本更新,页面将会自动刷新。"
+#: src/views/other/Install.vue:104
+msgid "Database (Optional, default: database)"
+msgstr "数据库 (可选,默认: database)"
 
-#: src/views/dashboard/DashBoard.vue:95
-msgid "Free:"
-msgstr "空闲:"
+#: src/router/index.js:126
+msgid "Detected version update, this page will refresh."
+msgstr "检测到版本更新,页面将会刷新。"
+
+#: src/views/other/About.vue:11
+msgid "Development Mode"
+msgstr "开发模式"
+
+#: src/views/domain/DomainEdit.vue:196
+msgid "Disable auto-renewal failed for %{name}"
+msgstr "关闭 %{name} 自动续签失败"
+
+#: src/views/domain/DomainList.vue:7 src/views/domain/DomainList.vue:2
+#: src/views/domain/DomainList.vue:17
+msgid "Disabled"
+msgstr "禁用"
+
+#: src/views/domain/DomainList.vue:55
+msgid "Disabled successfully"
+msgstr "禁用成功"
+
+#: src/views/domain/DomainEdit.vue:133
+msgid "Do you want to change the template to support the TLS?"
+msgstr "你想要改变模板以支持 TLS 吗?"
+
+#: src/views/domain/DomainEdit.vue:45
+msgid "Edit %{n}"
+msgstr "编辑 %{n}"
+
+#: src/router/index.js:68 src/views/config/ConfigEdit.vue:15
+msgid "Edit Configuration"
+msgstr "编辑配置"
+
+#: src/views/domain/DomainEdit.vue:91
+msgid "Edit Configuration File"
+msgstr "编辑配置文件"
+
+#: src/router/index.js:51
+msgid "Edit Site"
+msgstr "编辑站点"
+
+#: src/views/other/Install.vue:30
+msgid "Email (*)"
+msgstr "邮箱 (*)"
+
+#: src/views/domain/DomainEdit.vue:190
+msgid "Enable auto-renewal failed for %{name}"
+msgstr "启用 %{name} 自动续签失败"
+
+#: src/views/domain/DomainAdd.vue:33
+msgid "Enable failed"
+msgstr "启用失败"
+
+#: src/views/domain/columns.js:35
+msgid "Enable TLS"
+msgstr "启用 TLS"
+
+#: src/views/domain/DomainList.vue:8 src/views/domain/DomainList.vue:3
+#: src/views/domain/DomainList.vue:16
+msgid "Enabled"
+msgstr "启用"
+
+#: src/views/domain/DomainAdd.vue:27 src/views/domain/DomainList.vue:46
+msgid "Enabled successfully"
+msgstr "启用成功"
+
+#: src/views/domain/CertInfo.vue:5
+msgid "Expiration Date: %{date}"
+msgstr "过期时间: %{date}"
+
+#: src/views/domain/DomainList.vue:59
+msgid "Failed to disable %{msg}"
+msgstr "禁用失败 %{msg}"
+
+#: src/views/domain/DomainList.vue:50
+msgid "Failed to enable %{msg}"
+msgstr "启用失败 %{msg}"
+
+#: src/views/other/Error.vue:9
+msgid "File Not Found"
+msgstr "未找到文件"
+
+#: src/views/domain/DomainEdit.vue:9 src/views/domain/DomainEdit.vue:3
+msgid "Getting Certificate from Let's Encrypt"
+msgstr "从 Let's Encrypt 获取证书"
+
+#: src/views/domain/DomainEdit.vue:156
+msgid "Getting the certificate, please wait..."
+msgstr "正在获取证书,请稍等..."
 
 #: src/router/index.js:12
 msgid "Home"
 msgstr "首页"
 
-#: src/router/index.js:86
+#: src/views/domain/columns.js:28
+msgid "HTTP Listen Port"
+msgstr "HTTP 监听端口"
+
+#: src/views/domain/columns.js:54
+msgid "HTTPS Listen Port"
+msgstr "HTTPS 监听端口"
+
+#: src/views/domain/columns.js:22
+msgid "Index (index)"
+msgstr "网站首页 (index)"
+
+#: src/router/index.js:88 src/views/other/Install.vue:50
 msgid "Install"
 msgstr "安装"
 
+#: src/views/domain/CertInfo.vue:3
+msgid "Intermediate Certification Authorities: %{issuer}"
+msgstr "中级证书颁发机构: %{issuer}"
+
+#: src/views/other/Install.vue:45
+msgid "Invalid E-mail!"
+msgstr "无效的邮箱!"
+
+#: src/views/user/User.vue:19
+msgid "Leave blank for no change"
+msgstr "留空表示不修改"
+
+#: src/views/other/About.vue:16
+msgctxt "Project"
+msgid "License"
+msgstr "开源许可"
+
 #: src/views/dashboard/DashBoard.vue:13
-msgid "Load averages"
-msgstr "系统负载"
+msgid "Load Averages:"
+msgstr "系统负载:"
 
-#: src/router/index.js:92
+#: src/router/index.js:94 src/views/other/Login.vue:24
 msgid "Login"
 msgstr "登录"
 
-#: src/views/dashboard/DashBoard.vue:23
+#: src/views/other/Login.vue:28
+msgid "Login successful"
+msgstr "登录成功"
+
+#: src/layouts/HeaderLayout.vue:9
+msgid "Logout successful"
+msgstr "登出成功"
+
+#: src/views/domain/DomainEdit.vue:12 src/views/domain/DomainEdit.vue:6
+msgid ""
+"Make sure you have configured a reverse proxy for <code>.well-known</code> "
+"directory to <code>HTTPChallengePort</code> (default: 9180) before getting "
+"the certificate."
+msgstr ""
+"在获取签发证书前,请确保配置文件中已将 <code>.well-known</code> 目录反向代"
+"理到<code>HTTPChallengePort</code> (默认: 9180)"
+
+#: src/router/index.js:60
+msgid "Manage Configs"
+msgstr "配置管理"
+
+#: src/router/index.js:35 src/views/domain/DomainList.vue:21
+msgid "Manage Sites"
+msgstr "网站管理"
+
+#: src/router/index.js:27
+msgid "Manage Users"
+msgstr "用户管理"
+
+#: src/views/dashboard/DashBoard.vue:24
 msgid "Memory"
 msgstr "内存"
 
-#: src/router/index.js:68
-msgid "Modify Config"
-msgstr "配置修改"
+#: src/views/config/Config.vue:5 src/views/domain/DomainList.vue:5
+msgid "Name"
+msgstr "名称"
 
-#: src/router/index.js:51
-msgid "Modify Sites"
-msgstr "站点修改"
-
-#: src/router/index.js:104
+#: src/router/index.js:106
 msgid "Not Found"
 msgstr "找不到页面"
 
-#: src/router/index.js:128
+#: src/views/domain/CertInfo.vue:7
+msgid "Not Valid Before: %{date}"
+msgstr "此前无效: %{date}"
+
+#: src/views/domain/DomainEdit.vue:155
+msgid ""
+"Note: The server_name in the current configuration must be the domain name "
+"you need to get the certificate."
+msgstr "注意:当前配置中的 server_name 必须为需要申请证书的域名。"
+
+#: src/router/index.js:130
 msgid "OK"
-msgstr "好的"
+msgstr "确定"
+
+#: src/views/other/Login.vue:55 src/views/user/User.vue:13
+msgid "Password"
+msgstr "密码"
+
+#: src/views/other/Install.vue:82
+msgid "Password (*)"
+msgstr "密码 (*)"
+
+#: src/views/other/Install.vue:49
+msgid "Please input your E-mail!"
+msgstr "请输入您的邮箱!"
+
+#: src/views/other/Install.vue:95 src/views/other/Login.vue:68
+msgid "Please input your password!"
+msgstr "请输入您的密码!"
+
+#: src/views/other/Install.vue:72 src/views/other/Login.vue:45
+msgid "Please input your username!"
+msgstr "请输入您的用户名!"
+
+#: src/views/domain/columns.js:67
+msgid "Private Key Path (ssl_certificate_key)"
+msgstr "私钥路径 (ssl_certificate_key)"
 
-#: src/views/dashboard/DashBoard.vue:96
-msgid "Physical memory:"
-msgstr "物理内存:"
+#: src/views/other/About.vue:8
+msgid "Project Team"
+msgstr "项目团队"
+
+#: src/views/domain/columns.js:16
+msgid "Root Directory (root)"
+msgstr "网站根目录 (root)"
+
+#: src/views/config/ConfigEdit.vue:6 src/views/domain/DomainAdd.vue:6
+#: src/views/domain/DomainEdit.vue:24
+msgid "Save"
+msgstr "保存"
+
+#: src/views/config/ConfigEdit.vue:47 src/views/domain/DomainAdd.vue:38
+#: src/views/domain/DomainEdit.vue:151
+msgid "Save error %{msg}"
+msgstr "保存错误 %{msg}"
+
+#: src/views/config/ConfigEdit.vue:44 src/views/domain/DomainAdd.vue:24
+#: src/views/domain/DomainEdit.vue:145
+msgid "Saved successfully"
+msgstr "保存成功"
+
+#: src/views/config/ConfigEdit.vue:35 src/views/domain/DomainEdit.vue:71
+#: src/views/other/Login.vue:33
+msgid "Server error"
+msgstr "服务器错误"
+
+#: src/views/domain/columns.js:10
+msgid "Server Names (server_name)"
+msgstr "网站域名 (server_name)"
 
 #: src/views/dashboard/DashBoard.vue:38
-msgid "Server status"
+msgid "Server Status"
 msgstr "服务器状态"
 
-#: src/router/index.js:35
-msgid "Sites"
-msgstr "站点"
-
 #: src/router/index.js:43
 msgid "Sites List"
 msgstr "站点列表"
 
-#: src/views/dashboard/DashBoard.vue:32
+#: src/views/domain/DomainList.vue:11
+msgid "Status"
+msgstr "状态"
+
+#: src/views/dashboard/DashBoard.vue:33
 msgid "Storage"
 msgstr "存储"
 
-#: src/router/index.js:123
+#: src/views/domain/CertInfo.vue:4
+msgid "Subject Name: %{name}"
+msgstr "主体名称: %{name}"
+
+#: src/router/index.js:125
 msgid "System message"
 msgstr "系统消息"
 
-#: src/views/dashboard/DashBoard.vue:124
-msgid "Total:"
-msgstr "总共:"
+#: src/views/domain/columns.js:50
+msgid ""
+"The certificate for the domain will be checked every hour, and will be "
+"renewed if it has been more than 1 month since it was last issued.<br/>If "
+"you do not have a certificate before, please click \"Getting Certificate "
+"from Let's Encrypt\" first."
+msgstr ""
+"系统将会每小时检测一次该域名证书,若距离上次签发已超过1个月,则将自动续签。"
+"<br/>如果您之前没有证书,请先点击 \"从 Let's Encrypt 获取证书\"。"
+
+#: src/views/other/Install.vue:119
+msgid "The filename cannot contain the following characters: %{c}"
+msgstr "文件名不能包含以下字符: %{c}"
+
+#: src/views/domain/DomainEdit.vue:4
+msgid ""
+"The following values will only take effect if you have the corresponding "
+"fields in your configuration file. The configuration filename cannot be "
+"changed after it has been created."
+msgstr ""
+"只有在您的配置文件中有相应字段时,下列的配置才能生效。配置文件名称创建后不"
+"可修改。"
+
+#: src/views/domain/DomainEdit.vue:134
+msgid "This operation will lose the custom configuration."
+msgstr "该操作将会丢失自定义配置。"
+
+#: src/views/config/Config.vue:11 src/views/domain/DomainList.vue:22
+#: src/views/user/User.vue:29
+msgid "Updated at"
+msgstr "修改时间"
 
 #: src/views/dashboard/DashBoard.vue:12
 msgid "Uptime"
 msgstr "运行时间"
 
-#: src/views/dashboard/DashBoard.vue:94 src/views/dashboard/DashBoard.vue:123
-msgid "Used:"
-msgstr "已使用:"
+#: src/views/dashboard/DashBoard.vue:95
+msgid "Used: %{u}, Cached: %{c}, Free: %{f}, Physical Memory: %{p}"
+msgstr "已使用: %{u}, 缓存: %{c}, 空闲: %{f}, 物理内存: %{p}"
 
-#: src/router/index.js:27
-msgid "Users"
-msgstr "用户"
+#: src/views/dashboard/DashBoard.vue:123
+msgid "Used: %{used} / Total: %{total}"
+msgstr "已使用: %{used} / 总共: %{total}"
+
+#: src/views/other/Login.vue:32 src/views/user/User.vue:5
+msgid "Username"
+msgstr "用户名"
+
+#: src/views/other/Install.vue:59
+msgid "Username (*)"
+msgstr "用户名 (*)"

+ 328 - 37
frontend/src/locale/zh_TW/LC_MESSAGES/app.po

@@ -9,110 +9,401 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: easygettext\n"
 
-#: src/router/index.js:98
+#: src/router/index.js:100
 msgid "404 Not Found"
 msgstr ""
 
-#: src/router/index.js:76
+#: src/router/index.js:78
 msgid "About"
 msgstr ""
 
-#: src/router/index.js:47
-msgid "Add Sites"
+#: src/views/config/Config.vue:18 src/views/domain/DomainList.vue:29
+#: src/views/user/User.vue:35
+msgid "Action"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Cached:"
+#: src/router/index.js:47 src/views/domain/DomainAdd.vue:18
+#: src/views/domain/DomainEdit.vue:45
+msgid "Add Site"
 msgstr ""
 
-#: src/router/index.js:60
-msgid "Config"
+#: src/views/domain/DomainAdd.vue:2
+msgid ""
+"Add site here first, then you can configure TLS on the domain edit page."
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:194
+msgid "Auto-renewal disabled for %{name}"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:188
+msgid "Auto-renewal enabled for %{name}"
+msgstr ""
+
+#: src/views/other/About.vue:11
+msgid "Build with"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:23
+msgid "Cancel"
+msgstr ""
+
+#: src/views/domain/columns.js:44
+msgid "Certificate Auto-renewal"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:11 src/views/domain/CertInfo.vue:2
+msgid "Certificate has expired"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:15 src/views/domain/CertInfo.vue:2
+msgid "Certificate is valid"
+msgstr ""
+
+#: src/views/domain/columns.js:61
+msgid "Certificate Path (ssl_certificate)"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:2
+msgid "Certificate Status"
+msgstr ""
+
+#: src/views/domain/columns.js:4
+msgid "Configuration Name"
+msgstr ""
+
+#: src/views/config/Config.vue:8
+msgid "Configurations"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:52
+msgid "CPU"
+msgstr ""
+
+#: src/views/user/User.vue:23
+msgid "Created at"
 msgstr ""
 
 #: src/router/index.js:19
 msgid "Dashboard"
 msgstr ""
 
-#: src/router/index.js:124
-msgid "Detected version update, this page will automatically refresh."
+#: src/views/other/Install.vue:104
+msgid "Database (Optional, default: database)"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:95
-msgid "Free:"
+#: src/router/index.js:126
+msgid "Detected version update, this page will refresh."
+msgstr ""
+
+#: src/views/other/About.vue:11
+msgid "Development Mode"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:196
+msgid "Disable auto-renewal failed for %{name}"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:7 src/views/domain/DomainList.vue:2
+#: src/views/domain/DomainList.vue:17
+msgid "Disabled"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:55
+msgid "Disabled successfully"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:133
+msgid "Do you want to change the template to support the TLS?"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:45
+msgid "Edit %{n}"
+msgstr ""
+
+#: src/router/index.js:68 src/views/config/ConfigEdit.vue:15
+msgid "Edit Configuration"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:91
+msgid "Edit Configuration File"
+msgstr ""
+
+#: src/router/index.js:51
+msgid "Edit Site"
+msgstr ""
+
+#: src/views/other/Install.vue:30
+msgid "Email (*)"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:190
+msgid "Enable auto-renewal failed for %{name}"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:33
+msgid "Enable failed"
+msgstr ""
+
+#: src/views/domain/columns.js:35
+msgid "Enable TLS"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:8 src/views/domain/DomainList.vue:3
+#: src/views/domain/DomainList.vue:16
+msgid "Enabled"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:27 src/views/domain/DomainList.vue:46
+msgid "Enabled successfully"
+msgstr ""
+
+#: src/views/domain/CertInfo.vue:5
+msgid "Expiration Date: %{date}"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:59
+msgid "Failed to disable %{msg}"
+msgstr ""
+
+#: src/views/domain/DomainList.vue:50
+msgid "Failed to enable %{msg}"
+msgstr ""
+
+#: src/views/other/Error.vue:9
+msgid "File Not Found"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:9 src/views/domain/DomainEdit.vue:3
+msgid "Getting Certificate from Let's Encrypt"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:156
+msgid "Getting the certificate, please wait..."
 msgstr ""
 
 #: src/router/index.js:12
 msgid "Home"
 msgstr ""
 
-#: src/router/index.js:86
+#: src/views/domain/columns.js:28
+msgid "HTTP Listen Port"
+msgstr ""
+
+#: src/views/domain/columns.js:54
+msgid "HTTPS Listen Port"
+msgstr ""
+
+#: src/views/domain/columns.js:22
+msgid "Index (index)"
+msgstr ""
+
+#: src/router/index.js:88 src/views/other/Install.vue:50
 msgid "Install"
 msgstr ""
 
+#: src/views/domain/CertInfo.vue:3
+msgid "Intermediate Certification Authorities: %{issuer}"
+msgstr ""
+
+#: src/views/other/Install.vue:45
+msgid "Invalid E-mail!"
+msgstr ""
+
+#: src/views/user/User.vue:19
+msgid "Leave blank for no change"
+msgstr ""
+
+#: src/views/other/About.vue:16
+msgctxt "Project"
+msgid "License"
+msgstr ""
+
 #: src/views/dashboard/DashBoard.vue:13
-msgid "Load averages"
+msgid "Load Averages:"
 msgstr ""
 
-#: src/router/index.js:92
+#: src/router/index.js:94 src/views/other/Login.vue:24
 msgid "Login"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:23
-msgid "Memory"
+#: src/views/other/Login.vue:28
+msgid "Login successful"
 msgstr ""
 
-#: src/router/index.js:68
-msgid "Modify Config"
+#: src/layouts/HeaderLayout.vue:9
+msgid "Logout successful"
 msgstr ""
 
-#: src/router/index.js:51
-msgid "Modify Sites"
+#: src/views/domain/DomainEdit.vue:12 src/views/domain/DomainEdit.vue:6
+msgid ""
+"Make sure you have configured a reverse proxy for <code>.well-known</code> "
+"directory to <code>HTTPChallengePort</code> (default: 9180) before getting "
+"the certificate."
+msgstr ""
+
+#: src/router/index.js:60
+msgid "Manage Configs"
+msgstr ""
+
+#: src/router/index.js:35 src/views/domain/DomainList.vue:21
+msgid "Manage Sites"
+msgstr ""
+
+#: src/router/index.js:27
+msgid "Manage Users"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:24
+msgid "Memory"
 msgstr ""
 
-#: src/router/index.js:104
+#: src/views/config/Config.vue:5 src/views/domain/DomainList.vue:5
+msgid "Name"
+msgstr ""
+
+#: src/router/index.js:106
 msgid "Not Found"
 msgstr ""
 
-#: src/router/index.js:128
+#: src/views/domain/CertInfo.vue:7
+msgid "Not Valid Before: %{date}"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:155
+msgid ""
+"Note: The server_name in the current configuration must be the domain name "
+"you need to get the certificate."
+msgstr ""
+
+#: src/router/index.js:130
 msgid "OK"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:96
-msgid "Physical memory:"
+#: src/views/other/Login.vue:55 src/views/user/User.vue:13
+msgid "Password"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:38
-msgid "Server status"
+#: src/views/other/Install.vue:82
+msgid "Password (*)"
+msgstr ""
+
+#: src/views/other/Install.vue:49
+msgid "Please input your E-mail!"
+msgstr ""
+
+#: src/views/other/Install.vue:95 src/views/other/Login.vue:68
+msgid "Please input your password!"
+msgstr ""
+
+#: src/views/other/Install.vue:72 src/views/other/Login.vue:45
+msgid "Please input your username!"
 msgstr ""
 
-#: src/router/index.js:35
-msgid "Sites"
+#: src/views/domain/columns.js:67
+msgid "Private Key Path (ssl_certificate_key)"
+msgstr ""
+
+#: src/views/other/About.vue:8
+msgid "Project Team"
+msgstr ""
+
+#: src/views/domain/columns.js:16
+msgid "Root Directory (root)"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:6 src/views/domain/DomainAdd.vue:6
+#: src/views/domain/DomainEdit.vue:24
+msgid "Save"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:47 src/views/domain/DomainAdd.vue:38
+#: src/views/domain/DomainEdit.vue:151
+msgid "Save error %{msg}"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:44 src/views/domain/DomainAdd.vue:24
+#: src/views/domain/DomainEdit.vue:145
+msgid "Saved successfully"
+msgstr ""
+
+#: src/views/config/ConfigEdit.vue:35 src/views/domain/DomainEdit.vue:71
+#: src/views/other/Login.vue:33
+msgid "Server error"
+msgstr ""
+
+#: src/views/domain/columns.js:10
+msgid "Server Names (server_name)"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:38
+msgid "Server Status"
 msgstr ""
 
 #: src/router/index.js:43
 msgid "Sites List"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:32
+#: src/views/domain/DomainList.vue:11
+msgid "Status"
+msgstr ""
+
+#: src/views/dashboard/DashBoard.vue:33
 msgid "Storage"
 msgstr ""
 
-#: src/router/index.js:123
+#: src/views/domain/CertInfo.vue:4
+msgid "Subject Name: %{name}"
+msgstr ""
+
+#: src/router/index.js:125
 msgid "System message"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:124
-msgid "Total:"
+#: src/views/domain/columns.js:50
+msgid ""
+"The certificate for the domain will be checked every hour, and will be "
+"renewed if it has been more than 1 month since it was last issued.<br/>If "
+"you do not have a certificate before, please click \"Getting Certificate "
+"from Let's Encrypt\" first."
+msgstr ""
+
+#: src/views/other/Install.vue:119
+msgid "The filename cannot contain the following characters: %{c}"
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:4
+msgid ""
+"The following values will only take effect if you have the corresponding "
+"fields in your configuration file. The configuration filename cannot be "
+"changed after it has been created."
+msgstr ""
+
+#: src/views/domain/DomainEdit.vue:134
+msgid "This operation will lose the custom configuration."
+msgstr ""
+
+#: src/views/config/Config.vue:11 src/views/domain/DomainList.vue:22
+#: src/views/user/User.vue:29
+msgid "Updated at"
 msgstr ""
 
 #: src/views/dashboard/DashBoard.vue:12
 msgid "Uptime"
 msgstr ""
 
-#: src/views/dashboard/DashBoard.vue:94 src/views/dashboard/DashBoard.vue:123
-msgid "Used:"
+#: src/views/dashboard/DashBoard.vue:95
+msgid "Used: %{u}, Cached: %{c}, Free: %{f}, Physical Memory: %{p}"
 msgstr ""
 
-#: src/router/index.js:27
-msgid "Users"
+#: src/views/dashboard/DashBoard.vue:123
+msgid "Used: %{used} / Total: %{total}"
+msgstr ""
+
+#: src/views/other/Login.vue:32 src/views/user/User.vue:5
+msgid "Username"
+msgstr ""
+
+#: src/views/other/Install.vue:59
+msgid "Username (*)"
 msgstr ""

+ 0 - 122
frontend/src/locale/zh_TW/LC_MESSAGES/app.po~

@@ -1,122 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
-"Language: zh_TW\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: easygettext\n"
-
-#: src/router/index.js:98
-msgid "404 Not Found"
-msgstr ""
-
-#: src/router/index.js:76
-msgid "About"
-msgstr ""
-
-#: src/router/index.js:47
-msgid "Add Sites"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Cached:"
-msgstr ""
-
-#: src/router/index.js:60
-msgid "Config"
-msgstr ""
-
-#: src/router/index.js:19
-msgid "Dashboard"
-msgstr ""
-
-#: src/router/index.js:124
-msgid "Detected version update, this page will automatically refresh."
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:95
-msgid "Free:"
-msgstr ""
-
-#: src/router/index.js:12
-msgid "Home"
-msgstr ""
-
-#: src/router/index.js:86
-msgid "Install"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:13
-msgid "Load averages"
-msgstr ""
-
-#: src/router/index.js:92
-msgid "Login"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:23
-msgid "Memory"
-msgstr ""
-
-#: src/router/index.js:68
-msgid "Modify Config"
-msgstr ""
-
-#: src/router/index.js:51
-msgid "Modify Sites"
-msgstr ""
-
-#: src/router/index.js:104
-msgid "Not Found"
-msgstr ""
-
-#: src/router/index.js:128
-msgid "OK"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:96
-msgid "Physical memory:"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:38
-msgid "Server status"
-msgstr ""
-
-#: src/router/index.js:35
-msgid "Sites"
-msgstr ""
-
-#: src/router/index.js:43
-msgid "Sites List"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:32
-msgid "Storage"
-msgstr ""
-
-#: src/router/index.js:123
-msgid "System message"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:124
-msgid "Total: "
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:12
-msgid "Uptime"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:94
-msgid "Used:"
-msgstr ""
-
-#: src/views/dashboard/DashBoard.vue:123
-msgid "Used: "
-msgstr ""
-
-#: src/router/index.js:27
-msgid "Users"
-msgstr ""

+ 1 - 1
frontend/src/main.js

@@ -28,7 +28,7 @@ Vue.use(GetTextPlugin, {
 })
 
 http.get('/translations.json').then(r => {
-    store.commit('update_translations', r)
+    if(r) store.commit('update_translations', r)
 })
 
 NProgress.configure({

+ 17 - 15
frontend/src/router/index.js

@@ -24,7 +24,7 @@ export const routes = [
             },
             {
                 path: 'user',
-                name: $gettext('Users'),
+                name: $gettext('Manage Users'),
                 component: () => import('@/views/user/User.vue'),
                 meta: {
                     icon: 'user'
@@ -32,7 +32,7 @@ export const routes = [
             },
             {
                 path: 'domain',
-                name: $gettext('Sites'),
+                name: $gettext('Manage Sites'),
                 component: () => import('@/layouts/BaseRouterView'),
                 meta: {
                     icon: 'cloud'
@@ -44,11 +44,11 @@ export const routes = [
                     component: () => import('@/views/domain/DomainList.vue'),
                 }, {
                     path: 'add',
-                    name: $gettext('Add Sites'),
+                    name: $gettext('Add Site'),
                     component: () => import('@/views/domain/DomainAdd.vue'),
                 }, {
                     path: ':name',
-                    name: $gettext('Modify Sites'),
+                    name: $gettext('Edit Site'),
                     component: () => import('@/views/domain/DomainEdit.vue'),
                     meta: {
                         hiddenInSidebar: true
@@ -57,20 +57,22 @@ export const routes = [
             },
             {
                 path: 'config',
-                name: $gettext('Config'),
+                name: $gettext('Manage Configs'),
                 component: () => import('@/views/config/Config.vue'),
                 meta: {
-                    icon: 'file'
-                },
-            },
-            {
-                path: 'config/:name',
-                name: $gettext('Modify Config'),
-                component: () => import('@/views/config/ConfigEdit.vue'),
-                meta: {
-                    hiddenInSidebar: true
+                    icon: 'file',
+                    hideChildren: true
                 },
+                children: [{
+                    path: 'config/:name',
+                    name: $gettext('Edit Configuration'),
+                    component: () => import('@/views/config/ConfigEdit.vue'),
+                    meta: {
+                        hiddenInSidebar: true
+                    },
+                },]
             },
+
             {
                 path: 'about',
                 name: $gettext('About'),
@@ -121,7 +123,7 @@ router.beforeEach((to, from, next) => {
                 && Number(process.env.VUE_APP_BUILD_ID) === r.data.build_id)) {
                 Vue.prototype.$info({
                     title: $gettext('System message'),
-                    content: $gettext('Detected version update, this page will automatically refresh.'),
+                    content: $gettext('Detected version update, this page will refresh.'),
                     onOk() {
                         location.reload()
                     },

+ 6 - 5
frontend/src/views/config/Config.vue

@@ -1,5 +1,5 @@
 <template>
-    <a-card title="配置文件">
+    <a-card :title="$gettext('Configurations')">
         <std-table
             :api="api"
             :columns="columns"
@@ -18,22 +18,23 @@
 
 <script>
 import StdTable from '@/components/StdDataDisplay/StdTable'
+import $gettext from "@/lib/translate/gettext";
 
 const columns = [{
-    title: '名称',
+    title: $gettext('Name'),
     dataIndex: 'name',
-    scopedSlots: {customRender: '名称'},
+    scopedSlots: {customRender: 'name'},
     sorter: true,
     pithy: true
 }, {
-    title: '修改时间',
+    title: $gettext('Updated at'),
     dataIndex: 'modify',
     datetime: true,
     scopedSlots: {customRender: 'modify'},
     sorter: true,
     pithy: true
 }, {
-    title: '操作',
+    title: $gettext('Action'),
     dataIndex: 'action',
     scopedSlots: {customRender: 'action'}
 }]

+ 7 - 6
frontend/src/views/config/ConfigEdit.vue

@@ -1,10 +1,10 @@
 <template>
-    <a-card title="配置文件编辑">
+    <a-card :title="$gettext('Edit Configuration')">
         <vue-itextarea v-model="configText"/>
         <footer-tool-bar>
             <a-space>
-                <a-button @click="$router.go(-1)">返回</a-button>
-                <a-button type="primary" @click="save">保存</a-button>
+                <a-button @click="$router.go(-1)" v-translate>Cancel</a-button>
+                <a-button type="primary" @click="save" v-translate>Save</a-button>
             </a-space>
         </footer-tool-bar>
     </a-card>
@@ -13,6 +13,7 @@
 <script>
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar'
 import VueItextarea from '@/components/VueItextarea/VueItextarea'
+import $gettext, {$interpolate} from "@/lib/translate/gettext";
 
 export default {
     name: 'DomainEdit',
@@ -44,7 +45,7 @@ export default {
                     this.configText = r.config
                 }).catch(r => {
                     console.log(r)
-                    this.$message.error('服务器错误')
+                    this.$message.error($gettext('Server error'))
                 })
             } else {
                 this.configText = ''
@@ -53,10 +54,10 @@ export default {
         save() {
             this.$api.config.save(this.name ? this.name : this.config.name, {content: this.configText}).then(r => {
                 this.configText = r.config
-                this.$message.success('保存成功')
+                this.$message.success($gettext('Saved successfully'))
             }).catch(r => {
                 console.log(r)
-                this.$message.error('保存错误')
+                this.$message.error($interpolate($gettext('Save error %{msg}'), {msg: r.message ?? ""}))
             })
         }
     }

+ 10 - 10
frontend/src/views/dashboard/DashBoard.vue

@@ -2,7 +2,7 @@
     <div>
         <a-row class="row-two">
             <a-col :lg="24" :sm="24">
-                <a-card style="min-height: 400px" :title="$gettext('Server status')">
+                <a-card style="min-height: 400px" :title="$gettext('Server Status')">
                     <a-row>
                         <a-col :lg="12" :sm="24" class="chart">
                             <a-statistic :value="cpu" style="margin: 0 50px 10px 0" title="CPU">
@@ -11,17 +11,17 @@
                                 </template>
                             </a-statistic>
                             <p><translate>Uptime</translate> {{ uptime }}</p>
-                            <p><translate>Load averages</translate> 1min:{{ loadavg?.load1?.toFixed(2) }}
-                                5min:{{ loadavg?.load5?.toFixed(2) }}
+                            <p><translate>Load Averages: </translate> 1min:{{ loadavg?.load1?.toFixed(2) }} |
+                                5min:{{ loadavg?.load5?.toFixed(2) }} |
                                 15min:{{ loadavg?.load15?.toFixed(2) }}</p>
                             <line-chart :chart-data="cpu_analytic" :options="cpu_analytic.options" :height="150"/>
                         </a-col>
                         <a-col :lg="6" :sm="8" :xs="12" class="chart_dashboard">
                             <div>
                                 <a-tooltip
-                                    :title="$gettext('Used:') + memory_used + $gettext('Cached:') +
-                                     memory_cached + $gettext('Free:') + memory_free +
-                                     $gettext('Physical memory:') + memory_total">
+                                    :title="$gettextInterpolate(
+                                        $gettext('Used: %{u}, Cached: %{c}, Free: %{f}, Physical Memory: %{p}'),
+                                         {u: memory_used, c: memory_cached, f:memory_free, p: memory_total})">
                                     <a-progress :percent="memory_pressure" strokeColor="rgb(135, 208, 104)"
                                                 type="dashboard"/>
                                     <p class="description" v-translate>Memory</p>
@@ -31,8 +31,8 @@
                         <a-col :lg="6" :sm="8" :xs="12" class="chart_dashboard">
                             <div>
                                 <a-tooltip
-                                    :title="$gettext('Used:')+ disk_used +
-                                     ' / '+ $gettext('Total:') + disk_total">
+                                    :title="$gettextInterpolate($gettext('Used: %{used} / Total: %{total}'),
+                                    {used: disk_used, total: disk_total})">
                                     <a-progress :percent="disk_percentage" type="dashboard"/>
                                     <p class="description" v-translate>Storage</p>
                                 </a-tooltip>
@@ -81,10 +81,10 @@ export default {
                 options: {
                     responsive: true,
                     maintainAspectRatio: false,
-                    responsiveAnimationDuration: 0, // 调整大小后的动画持续时间
+                    responsiveAnimationDuration: 0, // Duration of the animation after resizing 调整大小后的动画持续时间
                     elements: {
                         line: {
-                            tension: 0 // 禁用贝塞尔曲线
+                            tension: 0 // Disable Bessel curves 禁用贝塞尔曲线
                         }
                     },
                     scales: {

+ 9 - 7
frontend/src/views/domain/CertInfo.vue

@@ -1,17 +1,19 @@
 <template>
     <div v-if="ok">
-        <h3>证书状态</h3>
-        <p>中级证书颁发机构:{{ cert.issuer_name }}</p>
-        <p>证书名称:{{ cert.subject_name }}</p>
-        <p>过期时间:{{ moment(cert.not_after).format('YYYY-MM-DD HH:mm:ss') }}</p>
-        <p>在此之前无效:{{ moment(cert.not_before).format('YYYY-MM-DD HH:mm:ss') }}</p>
+        <h3 v-translate>Certificate Status</h3>
+        <p v-translate :translate-params="{issuer: cert.issuer_name}">Intermediate Certification Authorities: %{issuer}</p>
+        <p v-translate :translate-params="{name: cert.subject_name}">Subject Name: %{name}</p>
+        <p v-translate :translate-params="{date: moment(cert.not_after).format('YYYY-MM-DD HH:mm:ss')}">
+            Expiration Date: %{date}</p>
+        <p v-translate :translate-params="{date: moment(cert.not_before).format('YYYY-MM-DD HH:mm:ss')}">
+            Not Valid Before: %{date}</p>
         <template v-if="new Date().toISOString() < cert.not_before || new Date().toISOString() > cert.not_after">
             <a-icon :style="{ color: 'red' }" type="close-circle"/>
-            此证书已过期
+            <translate>Certificate has expired</translate>
         </template>
         <template v-else>
             <a-icon :style="{ color: 'green' }" type="check-circle"/>
-            证书处在有效期内
+            <translate>Certificate is valid</translate>
         </template>
     </div>
 </template>

+ 8 - 7
frontend/src/views/domain/DomainAdd.vue

@@ -1,13 +1,13 @@
 <template>
-    <a-card title="添加站点">
-        <p>在这里添加站点,添加完成后进入域名配置编辑页面即可配置 SSL</p>
+    <a-card :title="$gettext('Add Site')">
+        <p v-translate>Add site here first, then you can configure TLS on the domain edit page.</p>
         <std-data-entry :data-list="columns" :data-source="config"/>
         <footer-tool-bar>
             <a-button
                 type="primary"
                 @click="save"
             >
-                完成
+                <translate>Save</translate>
             </a-button>
         </footer-tool-bar>
     </a-card>
@@ -18,6 +18,7 @@ import FooterToolBar from '@/components/FooterToolbar/FooterToolBar'
 import StdDataEntry from '@/components/StdDataEntry/StdDataEntry'
 import {columns} from '@/views/domain/columns'
 import {unparse} from '@/views/domain/methods'
+import $gettext, {$interpolate} from "@/lib/translate/gettext";
 
 export default {
     name: 'DomainAdd',
@@ -36,21 +37,21 @@ export default {
             this.$api.domain.get_template('http-conf').then(r => {
                 let text = unparse(r.template, this.config)
                 this.$api.domain.save(this.config.name, {content: text, enabled: true}).then(() => {
-                    this.$message.success('保存成功')
+                    this.$message.success($gettext('Saved successfully'))
 
                     this.$api.domain.enable(this.config.name).then(() => {
-                        this.$message.success('启用成功')
+                        this.$message.success($gettext('Enabled successfully'))
 
                         this.$router.push('/domain/' + this.config.name)
 
                     }).catch(r => {
                         console.log(r)
-                        this.$message.error(r.message ?? '启用失败', 10)
+                        this.$message.error(r.message ?? $gettext('Enable failed'), 10)
                     })
 
                 }).catch(r => {
                     console.log(r)
-                    this.$message.error(r.message ?? '保存错误', 10)
+                    this.$message.error($interpolate($gettext('Save error %{msg}'), {msg: r.message ?? ""}), 10)
                 })
             })
         }

+ 21 - 23
frontend/src/views/domain/DomainEdit.vue

@@ -1,31 +1,28 @@
 <template>
     <div>
         <a-collapse :bordered="false" default-active-key="1">
-            <a-collapse-panel key="1" :header="name ? '编辑站点:' + name : '添加站点'">
-                <p>您的配置文件中应当有对应的字段时,下列表单中的设置才能生效,配置文件名称创建后不可修改。</p>
+            <a-collapse-panel key="1" :header="name ? $gettextInterpolate($gettext('Edit %{n}'), {n: name}) : $gettext('Add Site')">
+                <p v-translate>The following values will only take effect if you have the corresponding fields in your configuration file. The configuration filename cannot be changed after it has been created.</p>
                 <std-data-entry :data-list="columns" v-model="config"/>
                 <template v-if="config.support_ssl">
                     <cert-info :domain="name" ref="cert-info" v-if="name"/>
                     <br/>
-                    <a-button @click="issue_cert" type="primary" ghost>
-                        自动申请 Let's Encrypt 证书
-                    </a-button>
-                    <p><br/>点击自动申请证书将会从 Let's Encrypt 获得签发证书
-                        在获取签发证书前,请确保配置文件中已为
-                        <code>/.well-known</code> 目录反向代理到后端的
-                        <code>HTTPChallengePort (default:9180)</code></p>
+                    <a-button @click="issue_cert" type="primary" ghost v-translate>
+                        Getting Certificate from Let's Encrypt
+                    </a-button><br/>
+                    <p v-translate>Make sure you have configured a reverse proxy for <code>.well-known</code> directory to <code>HTTPChallengePort</code> (default: 9180) before getting the certificate.</p>
                 </template>
             </a-collapse-panel>
         </a-collapse>
 
-        <a-card title="配置文件编辑">
+        <a-card :title="$gettext('Edit Configuration File')">
             <vue-itextarea v-model="configText"/>
         </a-card>
 
         <footer-tool-bar>
             <a-space>
-                <a-button @click="$router.push('/domain/list')">返回</a-button>
-                <a-button type="primary" @click="save">保存</a-button>
+                <a-button @click="$router.push('/domain/list')" v-translate>Cancel</a-button>
+                <a-button type="primary" @click="save" v-translate>Save</a-button>
             </a-space>
         </footer-tool-bar>
     </div>
@@ -39,6 +36,7 @@ import VueItextarea from '@/components/VueItextarea/VueItextarea'
 import {columns, columnsSSL} from '@/views/domain/columns'
 import {unparse} from '@/views/domain/methods'
 import CertInfo from '@/views/domain/CertInfo'
+import $gettext, {$interpolate} from "@/lib/translate/gettext";
 
 export default {
     name: 'DomainEdit',
@@ -102,7 +100,7 @@ export default {
                     })
                 }).catch(r => {
                     console.log(r)
-                    this.$message.error('服务器错误')
+                    this.$message.error($gettext('Server error'))
                 })
             } else {
                 this.config = {
@@ -164,8 +162,8 @@ export default {
         change_support_ssl() {
             const that = this
             this.$confirm({
-                title: '您已修改 SSL 支持状态,是否需要更换配置文件模板?',
-                content: '更换配置文件模板将会丢失自定义配置',
+                title: $gettext('Do you want to change the template to support the TLS?'),
+                content: $gettext('This operation will lose the custom configuration.'),
                 onOk() {
                     that.get_template()
                 },
@@ -176,18 +174,18 @@ export default {
         save() {
             this.$api.domain.save(this.name, {content: this.configText}).then(r => {
                 this.parse(r)
-                this.$message.success('保存成功')
+                this.$message.success($gettext('Saved successfully'))
                 if (this.name) {
                     if (this.$refs['cert-info']) this.$refs['cert-info'].get()
                 }
             }).catch(r => {
                 console.log(r)
-                this.$message.error('保存错误' + r.message !== undefined ? ' ' + r.message : null, 10)
+                this.$message.error($interpolate($gettext('Save error %{msg}'), {msg: r.message ?? ""}), 10)
             })
         },
         issue_cert() {
-            this.$message.info('请注意,当前配置中 server_name 必须为需要申请证书的域名,否则无法申请', 15)
-            this.$message.info('正在申请,请稍后', 15)
+            this.$message.info($gettext('Note: The server_name in the current configuration must be the domain name you need to get the certificate.'), 15)
+            this.$message.info($gettext('Getting the certificate, please wait...'), 15)
             this.ws = new WebSocket(this.getWebSocketRoot() + '/cert/issue/' + this.config.server_name
                 + '?token=' + btoa(this.$store.state.user.token))
 
@@ -219,15 +217,15 @@ export default {
         change_auto_cert() {
             if (this.config.auto_cert) {
                 this.$api.domain.add_auto_cert(this.name).then(() => {
-                    this.$message.success(this.name + ' 加入自动续签列表成功')
+                    this.$message.success($interpolate($gettext('Auto-renewal enabled for %{name}', {name: this.name})))
                 }).catch(e => {
-                    this.$message.error(e.message ?? this.name + ' 加入自动续签列表失败')
+                    this.$message.error(e.message ?? $interpolate($gettext('Enable auto-renewal failed for %{name}', {name: this.name})))
                 })
             } else {
                 this.$api.domain.remove_auto_cert(this.name).then(() => {
-                    this.$message.success('从自动续签列表中删除 ' + this.name + ' 成功')
+                    this.$message.success($interpolate($gettext('Auto-renewal disabled for %{name}', {name: this.name})))
                 }).catch(e => {
-                    this.$message.error(e.message ?? '从自动续签列表中删除 ' + this.name + ' 失败')
+                    this.$message.error(e.message ?? $interpolate($gettext('Disable auto-renewal failed for %{name}', {name: this.name})))
                 })
             }
         }

+ 15 - 14
frontend/src/views/domain/DomainList.vue

@@ -1,5 +1,5 @@
 <template>
-    <a-card title="网站管理">
+    <a-card :title="$gettext('Manage Sites')">
         <std-table
             :api="api"
             :columns="columns"
@@ -13,8 +13,8 @@
         >
             <template #actions="{record}">
                 <a-divider type="vertical"/>
-                <a v-if="record.enabled" @click="disable(record.name)">禁用</a>
-                <a v-else @click="enable(record.name)">启用</a>
+                <a v-if="record.enabled" @click="disable(record.name)" v-translate>Disabled</a>
+                <a v-else @click="enable(record.name)" v-translate>Enabled</a>
             </template>
         </std-table>
     </a-card>
@@ -22,33 +22,34 @@
 
 <script>
 import StdTable from '@/components/StdDataDisplay/StdTable'
+import $gettext, {$interpolate} from "@/lib/translate/gettext";
 
 const columns = [{
-    title: '配置名称',
+    title: $gettext('Name'),
     dataIndex: 'name',
-    scopedSlots: {customRender: '名称'},
+    scopedSlots: {customRender:  'name'},
     sorter: true,
     pithy: true
 }, {
-    title: '状态',
+    title: $gettext('Status'),
     dataIndex: 'enabled',
     badge: true,
     scopedSlots: {customRender: 'enabled'},
     mask: {
-        true: '启用',
-        false: '未启用'
+        true: $gettext('Enabled'),
+        false: $gettext('Disabled')
     },
     sorter: true,
     pithy: true
 }, {
-    title: '修改时间',
+    title: $gettext('Updated at'),
     dataIndex: 'modify',
     datetime: true,
     scopedSlots: {customRender: 'modify'},
     sorter: true,
     pithy: true
 }, {
-    title: '操作',
+    title: $gettext('Action'),
     dataIndex: 'action',
     scopedSlots: {customRender: 'action'}
 }]
@@ -65,20 +66,20 @@ export default {
     methods: {
         enable(name) {
             this.$api.domain.enable(name).then(() => {
-                this.$message.success('启用成功')
+                this.$message.success($gettext('Enabled successfully'))
                 this.$refs.table.get_list()
             }).catch(r => {
                 console.log(r)
-                this.$message.error('启用失败 ' + (r.message ?? ''), 10)
+                this.$message.error($interpolate($gettext('Failed to enable %{msg}'), {msg: r.message ?? ''}), 10)
             })
         },
         disable(name) {
             this.$api.domain.disable(name).then(() => {
-                this.$message.success('禁用成功')
+                this.$message.success($gettext('Disabled successfully'))
                 this.$refs.table.get_list()
             }).catch(r => {
                 console.log(r)
-                this.$message.error('禁用失败')
+                this.$message.error($interpolate($gettext('Failed to disable %{msg}'), {msg: r.message ?? ''}))
             })
         }
     }

+ 15 - 13
frontend/src/views/domain/columns.js

@@ -1,36 +1,38 @@
+import $gettext from "@/lib/translate/gettext";
+
 const columns = [{
-    title: '配置文件名称',
+    title: $gettext('Configuration Name'),
     dataIndex: 'name',
     edit: {
         type: 'input'
     }
 }, {
-    title: '网站域名 (server_name)',
+    title: $gettext('Server Names (server_name)'),
     dataIndex: 'server_name',
     edit: {
         type: 'input'
     }
 }, {
-    title: '网站根目录 (root)',
+    title: $gettext('Root Directory (root)'),
     dataIndex: 'root',
     edit: {
         type: 'input'
     }
 }, {
-    title: '网站首页 (index)',
+    title: $gettext('Index (index)'),
     dataIndex: 'index',
     edit: {
         type: 'input'
     }
 }, {
-    title: 'http 监听端口',
+    title: $gettext('HTTP Listen Port'),
     dataIndex: 'http_listen_port',
     edit: {
         type: 'number',
         min: 80
     }
 }, {
-    title: '支持 SSL',
+    title: $gettext('Enable TLS'),
     dataIndex: 'support_ssl',
     edit: {
         type: 'switch',
@@ -39,30 +41,30 @@ const columns = [{
 }]
 
 const columnsSSL = [{
-    title: '自动续签',
+    title: $gettext('Certificate Auto-renewal'),
     dataIndex: 'auto_cert',
     edit: {
         type: 'switch',
         event: 'change_auto_cert'
     },
-    description: '启用自动续签后,系统将会每小时检测一次该域名证书的信息,' +
-        '如果距离上次签发已超过1个月,则将执行自动续签。' +
-        '<br/>启用前先点击下方「自动申请 Let\'s Encrypt 证书」即可获得证书路径。'
+    description: $gettext('The certificate for the domain will be checked every hour, ' +
+        'and will be renewed if it has been more than 1 month since it was last issued.' +
+        '<br/>If you do not have a certificate before, please click "Getting Certificate from Let\'s Encrypt" first.')
 }, {
-    title: 'https 监听端口',
+    title: $gettext('HTTPS Listen Port'),
     dataIndex: 'https_listen_port',
     edit: {
         type: 'number',
         min: 443
     }
 }, {
-    title: 'SSL 证书路径 (ssl_certificate)',
+    title: $gettext('Certificate Path (ssl_certificate)'),
     dataIndex: 'ssl_certificate',
     edit: {
         type: 'input'
     }
 }, {
-    title: 'SSL 证书私钥路径 (ssl_certificate_key)',
+    title: $gettext('Private Key Path (ssl_certificate_key)'),
     dataIndex: 'ssl_certificate_key',
     edit: {
         type: 'input'

+ 6 - 11
frontend/src/views/other/About.vue

@@ -6,21 +6,23 @@
         <h2>Nginx UI</h2>
         <p>Yet another WebUI for Nginx</p>
         <p>Version: {{ version }} ({{ build_id }})</p>
-        <h3>项目组</h3>
+        <h3 v-translate>Project Team</h3>
         <p><a href="https://jackyu.cn/">@0xJacky</a></p>
         <p><a href="https://blog.kugeek.com/">@Hintay</a></p>
-        <h3>技术栈</h3>
+        <h3 v-translate>Build with</h3>
         <p>Go</p>
         <p>Gin</p>
         <p>Vue</p>
         <p>Websocket</p>
-        <h3>开源协议</h3>
+        <h3 v-translate translate-context="Project">License</h3>
         <p>GNU General Public License v2.0</p>
         <p>Copyright © 2020 - {{ this_year }} Nginx UI </p>
     </a-card>
 </template>
 
 <script>
+import $gettext from "@/lib/translate/gettext";
+
 export default {
     name: 'About',
     data() {
@@ -29,16 +31,9 @@ export default {
             logo: require('@/assets/img/logo.png'),
             this_year: date.getFullYear(),
             version: process.env.VUE_APP_VERSION,
-            build_id: process.env.VUE_APP_TOTAL_BUILD ?? '开发模式',
+            build_id: process.env.VUE_APP_TOTAL_BUILD ?? $gettext('Development Mode'),
             api_root: process.env.VUE_APP_API_ROOT
         }
-    },
-    methods: {
-        async changeUserPower(power) {
-            await this.$store.dispatch('update_mock_user', {power: power})
-            await this.$api.user.info()
-            await this.$message.success('修改成功')
-        }
     }
 }
 </script>

+ 1 - 1
frontend/src/views/other/Error.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="wrapper">
         <h1 class="title">{{ $route.meta.status_code ? $route.meta.status_code : 404 }}</h1>
-        <p>{{ $route.meta.error ? $route.meta.error : '找不到文件' }}</p>
+        <p>{{ $route.meta.error ? $route.meta.error : $gettext('File Not Found') }}</p>
     </div>
 </template>
 

+ 0 - 18
frontend/src/views/other/Home.vue

@@ -1,18 +0,0 @@
-<template>
-    <div class="home">
-        <img alt="Vue logo" src="../assets/img/logo.png">
-        <HelloWorld msg="Welcome to Your Vue.js App"/>
-    </div>
-</template>
-
-<script>
-// @ is an alias to /src
-import HelloWorld from '@/components/HelloWorld.vue'
-
-export default {
-    name: 'Home',
-    components: {
-        HelloWorld
-    }
-}
-</script>

+ 16 - 12
frontend/src/views/other/Install.vue

@@ -4,7 +4,7 @@
             <h1>Nginx UI</h1>
         </div>
         <a-form
-            id="components-form-demo-normal-login"
+            id="components-form-install"
             :form="form"
             class="login-form"
             @submit="handleSubmit"
@@ -15,14 +15,14 @@
           'email',
           { rules: [{
                 type: 'email',
-                message: 'The input is not valid E-mail!',
+                message: $gettext('Invalid E-mail!'),
               },
               {
                 required: true,
-                message: 'Please input your E-mail!',
+                message: $gettext('Please input your E-mail!'),
               },] },
         ]"
-                    placeholder="Email (*)"
+                    :placeholder="$gettext('Email (*)')"
                 >
                     <a-icon slot="prefix" type="mail" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
@@ -31,9 +31,9 @@
                 <a-input
                     v-decorator="[
           'username',
-          { rules: [{ required: true, message: 'Please input your username!' }] },
+          { rules: [{ required: true, message: $gettext('Please input your username!') }] },
         ]"
-                    placeholder="Username (*)"
+                    :placeholder="$gettext('Username (*)')"
                 >
                     <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
@@ -42,10 +42,10 @@
                 <a-input
                     v-decorator="[
           'password',
-          { rules: [{ required: true, message: 'Please input your Password!' }] },
+          { rules: [{ required: true, message: $gettext('Please input your password!') }] },
         ]"
                     type="password"
-                    placeholder="Password (*)"
+                    :placeholder="$gettext('Password (*)')"
                 >
                     <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
@@ -54,16 +54,20 @@
                 <a-input
                     v-decorator="[
           'database',
-          { rules: [{ pattern: /^[^\\/:*?\x22<>|]{1,120}$/, message: 'Please input a legal file name!'}] },
+          { rules: [{ pattern: /^[^\\/:*?\x22<>|]{1,120}$/,
+          message: $gettextInterpolate(
+              $gettext('The filename cannot contain the following characters: %{c}'),
+              {c: '& &quot; ? < > # {} % ~ / \\'}
+          )}] },
         ]"
-                    placeholder="Database (Optional, default: database)"
+                    :placeholder="$gettext('Database (Optional, default: database)')"
                 >
                     <a-icon slot="prefix" type="database" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
             </a-form-item>
             <a-form-item>
-                <a-button type="primary" :block="true" html-type="submit" :loading="loading">
-                    安装
+                <a-button type="primary" :block="true" html-type="submit" :loading="loading" v-translate>
+                    Install
                 </a-button>
             </a-form-item>
         </a-form>

+ 10 - 8
frontend/src/views/other/Login.vue

@@ -13,9 +13,9 @@
                     <a-input
                         v-decorator="[
           'name',
-          { rules: [{ required: true, message: '请输入用户名' }] },
+          { rules: [{ required: true, message: $gettext('Please input your username!') }] },
         ]"
-                        placeholder="Username"
+                        :placeholder="$gettext('Username')"
                     >
                         <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)"/>
                     </a-input>
@@ -24,17 +24,17 @@
                     <a-input
                         v-decorator="[
           'password',
-          { rules: [{ required: true, message: '请输入密码' }] },
+          { rules: [{ required: true, message: $gettext('Please input your password!') }] },
         ]"
                         type="password"
-                        placeholder="Password"
+                        :placeholder="$gettext('Password')"
                     >
                         <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/>
                     </a-input>
                 </a-form-item>
                 <a-form-item>
-                    <a-button type="primary" :block="true" html-type="submit" :loading="loading">
-                        登录
+                    <a-button type="primary" :block="true" html-type="submit" :loading="loading" v-translate>
+                        Login
                     </a-button>
                 </a-form-item>
             </a-form>
@@ -46,6 +46,8 @@
 </template>
 
 <script>
+import $gettext from "@/lib/translate/gettext";
+
 export default {
     name: 'Login',
     data() {
@@ -71,12 +73,12 @@ export default {
     methods: {
         login(values) {
             return this.$api.auth.login(values.name, values.password).then(async () => {
-                await this.$message.success('登录成功', 1)
+                await this.$message.success($gettext('Login successful'), 1)
                 const next = this.$route.query.next ? this.$route.query.next : '/'
                 await this.$router.push(next)
             }).catch(r => {
                 console.log(r)
-                this.$message.error(r.message ?? '服务器错误')
+                this.$message.error(r.message ?? $gettext('Server error'))
             })
         },
         handleSubmit: async function (e) {

+ 7 - 6
frontend/src/views/user/User.vue

@@ -5,9 +5,10 @@
 <script>
 
 import StdCurd from '@/components/StdDataDisplay/StdCurd'
+import $gettext from "@/lib/translate/gettext";
 
 const columns = [{
-    title: '用户名',
+    title: $gettext('Username'),
     dataIndex: 'name',
     sorter: true,
     pithy: true,
@@ -15,29 +16,29 @@ const columns = [{
         type: 'input'
     }
 }, {
-    title: '密码',
+    title: $gettext('Password'),
     dataIndex: 'password',
     sorter: true,
     pithy: true,
     edit: {
         type: 'input',
-        placeholder: '留空不修改'
+        placeholder: $gettext('Leave blank for no change')
     },
     display: false
 }, {
-    title: '创建时间',
+    title: $gettext('Created at'),
     dataIndex: 'created_at',
     datetime: true,
     sorter: true,
     pithy: true
 }, {
-    title: '修改时间',
+    title: $gettext('Updated at'),
     dataIndex: 'updated_at',
     datetime: true,
     sorter: true,
     pithy: true
 }, {
-    title: '操作',
+    title: $gettext('Action'),
     dataIndex: 'action'
 }]
 

+ 1 - 1
frontend/version.json

@@ -1 +1 @@
-{"version":"1.1.0","build_id":9,"total_build":26}
+{"version":"1.1.0","build_id":22,"total_build":39}

+ 1 - 1
go.mod

@@ -3,6 +3,7 @@ module github.com/0xJacky/Nginx-UI
 go 1.17
 
 require (
+	github.com/0xJacky/pofile v0.0.1
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/dustin/go-humanize v1.0.0
 	github.com/gin-contrib/static v0.0.1
@@ -23,7 +24,6 @@ require (
 )
 
 require (
-	github.com/0xJacky/pofile v0.0.1 // indirect
 	github.com/StackExchange/wmi v1.2.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.0 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect

+ 0 - 4
go.sum

@@ -23,10 +23,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/0xJacky/pofile v0.0.0-20220216140954-bc565a3feb80 h1:qKPxkbGoP/vVxN1Vgd964oJBlhGwhrCAKiheoieOPhc=
-github.com/0xJacky/pofile v0.0.0-20220216140954-bc565a3feb80/go.mod h1:gSDWobvodMtvwh7FE/F999AQoCwBoXgzyGffYFX9nKA=
-github.com/0xJacky/pofile v0.0.0-20220219022008-df96416310e5 h1:NEsbh2Wez8+0Y72hbVQPPhyIkmc1RAgWOWbcISRUxEs=
-github.com/0xJacky/pofile v0.0.0-20220219022008-df96416310e5/go.mod h1:gSDWobvodMtvwh7FE/F999AQoCwBoXgzyGffYFX9nKA=
 github.com/0xJacky/pofile v0.0.0-20220219101524-60ce48e4de23 h1:nqaxj4ZYzLZzhFQeX1ZrXEBz0haUu7PypGFhQbQfDX0=
 github.com/0xJacky/pofile v0.0.0-20220219101524-60ce48e4de23/go.mod h1:gSDWobvodMtvwh7FE/F999AQoCwBoXgzyGffYFX9nKA=
 github.com/0xJacky/pofile v0.0.1 h1:hVRaw6ZHkajSMAuP58WMDTvGF8+OF297jpAchFK/4rQ=

+ 1 - 1
main.go

@@ -33,9 +33,9 @@ func main() {
 	log.Printf("nginx config dir path: %s", tool2.GetNginxConfPath(""))
 	if "" != settings.ServerSettings.JwtSecret {
 		model.Init()
-		frontend.InitTranslations()
 		go tool2.AutoCert()
 	}
+	frontend.InitTranslations()
 
 	srv := &http.Server{
 		Addr:    ":" + settings.ServerSettings.HttpPort,