diff --git a/src/pages/i18n.js b/src/pages/i18n.js
new file mode 100644
index 000000000..c2555a230
--- /dev/null
+++ b/src/pages/i18n.js
@@ -0,0 +1,52 @@
+const { resolve } = require('path');
+const fs = require('fs');
+const { extractI18NReport, readVueFiles, extractI18NItemsFromVueFiles, readLanguageFiles } = require('vue-i18n-extract');
+const Dot = require('dot-object');
+
+const dot = new Dot('.');
+const config = {
+ filePath: './src/**/*.?(js|vue|ts|tsx|jsx|html)',
+ i18nPath: './src/language/**/*.?(json)'
+}
+
+const vueFiles = readVueFiles(resolve(process.cwd(), config.filePath));
+const I18NItems = extractI18NItemsFromVueFiles(vueFiles);
+
+const languageFiles = readLanguageFiles(resolve(process.cwd(), config.i18nPath));
+const I18NLanguage = languageFiles.reduce((pre, item) => {
+ const content = JSON.parse(item.content)
+ const flatContent = dot.dot(content);
+ // const lang = item.fileName.replace(/^.*[\\\/]/, '')
+ pre[item.fileName] = Object.keys(flatContent).map(key => ({
+ file: item.fileName,
+ path: key
+ }))
+ return pre
+}, {});
+
+const report = extractI18NReport(I18NItems, I18NLanguage);
+
+// 添加未翻译文案
+languageFiles.forEach(languageFile => {
+ const content = JSON.parse(languageFile.content);
+ report.missingKeys.forEach(item => {
+ if (item.language !== languageFile.fileName) return
+
+ const defaultValue = languageFile.fileName.indexOf('zh') > -1 ? item.path : ''
+ dot.str(item.path, defaultValue, content)
+ })
+ languageFile.content = JSON.stringify(content) // 保留结果
+
+ fs.writeFileSync(languageFile.path, JSON.stringify(content, null, 2))
+});
+// 删除未使用文案
+languageFiles.forEach(languageFile => {
+ const content = JSON.parse(languageFile.content);
+ report.unusedKeys.forEach(item => {
+ if (item.language !== languageFile.fileName) return
+
+ dot.delete(item.path, content)
+ })
+ languageFile.content = JSON.stringify(content) // 保留结果
+ fs.writeFileSync(languageFile.path, JSON.stringify(content, null, 2))
+});
\ No newline at end of file
diff --git a/src/pages/package.json b/src/pages/package.json
index b858b2899..275b7e830 100644
--- a/src/pages/package.json
+++ b/src/pages/package.json
@@ -8,7 +8,8 @@
"server": "node ./paas-server/index.js",
"lint": "eslint --ext .js,.vue,.ts ./src",
"lint:fix": "eslint --fix --ext .js,.vue,.ts ./src",
- "lint:style": "stylelint \"./**/*.{css,less,vue,html}\" --fix"
+ "lint:style": "stylelint \"./**/*.{css,less,vue,html}\" --fix",
+ "i18n": "node ./i18n.js"
},
"keywords": [
"bk-ci",
@@ -33,6 +34,7 @@
"cookie": "^0.5.0",
"cookie-parser": "^1.4.5",
"dayjs": "^1.11.2",
+ "dot-object": "^2.1.5",
"express": "^4.18.2",
"express-art-template": "^1.0.1",
"intl-tel-input": "^18.2.1",
@@ -50,6 +52,7 @@
"vite-plugin-monaco-editor": "^1.1.0",
"vue": "^3.2.41",
"vue-i18n": "^9.9.0",
+ "vue-i18n-extract": "^2.0.7",
"vue-router": "^4.1.6"
},
"devDependencies": {
diff --git a/src/pages/src/components/SyncRecords.vue b/src/pages/src/components/SyncRecords.vue
index e10520182..3b09ab35c 100644
--- a/src/pages/src/components/SyncRecords.vue
+++ b/src/pages/src/components/SyncRecords.vue
@@ -15,7 +15,7 @@
diff --git a/src/pages/src/components/layouts/LabelContent.vue b/src/pages/src/components/layouts/LabelContent.vue
index 9f738c516..54f3d2ae8 100644
--- a/src/pages/src/components/layouts/LabelContent.vue
+++ b/src/pages/src/components/layouts/LabelContent.vue
@@ -1,6 +1,8 @@
- {{ `${label ? label + ':' : ''}` }}
+
+ {{ `${label ? label + ':' : ''}` }}
+
@@ -27,8 +29,15 @@ defineProps({
.label-key {
width: 98px;
+ overflow: hidden;
color: #63656E;
text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .label-key-en {
+ width: 150px !important;
}
.label-value {
diff --git a/src/pages/src/components/layouts/MainView.vue b/src/pages/src/components/layouts/MainView.vue
index cbde19237..0b3f60803 100644
--- a/src/pages/src/components/layouts/MainView.vue
+++ b/src/pages/src/components/layouts/MainView.vue
@@ -26,9 +26,10 @@