diff --git a/sermant-backend/src/main/webapp/frontend/package-lock.json b/sermant-backend/src/main/webapp/frontend/package-lock.json index e62def19fe..cfd447dbc9 100644 --- a/sermant-backend/src/main/webapp/frontend/package-lock.json +++ b/sermant-backend/src/main/webapp/frontend/package-lock.json @@ -15,6 +15,7 @@ "moment": "^2.29.4", "qs": "^6.11.1", "vue": "^3.2.36", + "vue-i18n": "^9.13.1", "vue-router": "^4.1.6" }, "devDependencies": { @@ -520,6 +521,44 @@ "local-pkg": "^0.4.3" } }, + "node_modules/@intlify/core-base": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/@intlify/core-base/-/core-base-9.13.1.tgz", + "integrity": "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==", + "dependencies": { + "@intlify/message-compiler": "9.13.1", + "@intlify/shared": "9.13.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/@intlify/message-compiler/-/message-compiler-9.13.1.tgz", + "integrity": "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==", + "dependencies": { + "@intlify/shared": "9.13.1", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/@intlify/shared": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/@intlify/shared/-/shared-9.13.1.tgz", + "integrity": "sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -3966,6 +4005,25 @@ "@vue/shared": "3.2.47" } }, + "node_modules/vue-i18n": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/vue-i18n/-/vue-i18n-9.13.1.tgz", + "integrity": "sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==", + "dependencies": { + "@intlify/core-base": "9.13.1", + "@intlify/shared": "9.13.1", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-router": { "version": "4.1.6", "resolved": "https://repo.huaweicloud.com/repository/npm/vue-router/-/vue-router-4.1.6.tgz", @@ -4471,6 +4529,29 @@ "local-pkg": "^0.4.3" } }, + "@intlify/core-base": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/@intlify/core-base/-/core-base-9.13.1.tgz", + "integrity": "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==", + "requires": { + "@intlify/message-compiler": "9.13.1", + "@intlify/shared": "9.13.1" + } + }, + "@intlify/message-compiler": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/@intlify/message-compiler/-/message-compiler-9.13.1.tgz", + "integrity": "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==", + "requires": { + "@intlify/shared": "9.13.1", + "source-map-js": "^1.0.2" + } + }, + "@intlify/shared": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/@intlify/shared/-/shared-9.13.1.tgz", + "integrity": "sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==" + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -6813,6 +6894,16 @@ "@vue/shared": "3.2.47" } }, + "vue-i18n": { + "version": "9.13.1", + "resolved": "https://r.cnpmjs.org/vue-i18n/-/vue-i18n-9.13.1.tgz", + "integrity": "sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==", + "requires": { + "@intlify/core-base": "9.13.1", + "@intlify/shared": "9.13.1", + "@vue/devtools-api": "^6.5.0" + } + }, "vue-router": { "version": "4.1.6", "resolved": "https://repo.huaweicloud.com/repository/npm/vue-router/-/vue-router-4.1.6.tgz", diff --git a/sermant-backend/src/main/webapp/frontend/package.json b/sermant-backend/src/main/webapp/frontend/package.json index 4d725c6f9a..45971c4302 100644 --- a/sermant-backend/src/main/webapp/frontend/package.json +++ b/sermant-backend/src/main/webapp/frontend/package.json @@ -16,6 +16,7 @@ "moment": "^2.29.4", "qs": "^6.11.1", "vue": "^3.2.36", + "vue-i18n": "^9.13.1", "vue-router": "^4.1.6" }, "devDependencies": { diff --git a/sermant-backend/src/main/webapp/frontend/src/App.vue b/sermant-backend/src/main/webapp/frontend/src/App.vue index ef4205ab64..090d868a9e 100644 --- a/sermant-backend/src/main/webapp/frontend/src/App.vue +++ b/sermant-backend/src/main/webapp/frontend/src/App.vue @@ -1,8 +1,8 @@ - - + + - + @@ -17,3 +17,7 @@ margin: 1vh; } + + \ No newline at end of file diff --git a/sermant-backend/src/main/webapp/frontend/src/components/layouts/BaseHeader.vue b/sermant-backend/src/main/webapp/frontend/src/components/layouts/BaseHeader.vue index f13ae08449..374bf766b5 100644 --- a/sermant-backend/src/main/webapp/frontend/src/components/layouts/BaseHeader.vue +++ b/sermant-backend/src/main/webapp/frontend/src/components/layouts/BaseHeader.vue @@ -2,6 +2,7 @@ import {toggleDark} from "~/composables"; import {useRouter} from "vue-router"; import {ref} from "vue"; +import {switchLocale} from '~/composables/translations'; const activeName = ref("first"); @@ -35,24 +36,42 @@ const getAssetsImge = (url: string) => { - 实例状态 + {{ $t('header.instanceStatus') }} - 事件管理 - 监测 - 配置 + {{ $t('header.eventManagement') }} + {{ $t('header.monitoring') }} + {{ $t('header.configuration') }} + + {{ $t('header.configurationManagement') }} + + + + + + + + + + + 中文 + English - 配置管理 - + diff --git a/sermant-backend/src/main/webapp/frontend/src/composables/config.ts b/sermant-backend/src/main/webapp/frontend/src/composables/config.ts index 9f03bd9fa1..cb7556b7a4 100644 --- a/sermant-backend/src/main/webapp/frontend/src/composables/config.ts +++ b/sermant-backend/src/main/webapp/frontend/src/composables/config.ts @@ -1,25 +1,48 @@ -export const resultCodeMap = new Map([ - ['00', '成功'], - ['01', '无法连接配置中心'], - ['02', '配置查询失败'], - ['03', '配置已存在'], - ['04', '新增配置失败'], - ['05', '发布配置失败'], - ['06', '删除配置失败'], - ['07', '配置不存在'], - ['08', '缺少请求参数'], - ['09', '请求失败'], +import i18n from "~/composables/translations"; +import {ref, watch} from "vue"; + +const resultCodes = [ + {code: '00', key: 'common.success'}, + {code: '01', key: 'common.unableToConnectToConfigurationCenter'}, + {code: '02', key: 'common.failedToObtainConfiguration'}, + {code: '03', key: 'common.configurationAlreadyExists'}, + {code: '04', key: 'common.failedToCreateConfiguration'}, + {code: '05', key: 'common.failedToPublishConfiguration'}, + {code: '06', key: 'common.failedToDeleteConfiguration'}, + {code: '07', key: 'common.configurationDoesNotExist'}, + {code: '08', key: 'common.missingRequestParameters'}, + {code: '09', key: 'common.failedToRequest'}, +]; + +export const resultCodeMap = new Map(resultCodes.map(item => [item.code, i18n.global.t(item.key)])); + +watch(() => i18n.global.locale, () => { + resultCodes.forEach(item => { + resultCodeMap.set(item.code, i18n.global.t(item.key)); + }); + updateOptions(); +}); + +const options = ref([ + { label: '', value: '' } ]); -export const options = [ - {label: '路由插件配置', value: 'router',}, - {label: 'springboot注册插件配置', value: 'springboot-registry',}, - {label: '注册迁移插件配置', value: 'service-registry',}, - {label: '流控插件配置', value: 'flowcontrol',}, - {label: '离群实例摘除插件配置', value: 'removal',}, - {label: '负载均衡插件配置', value: 'loadbalancer',}, - {label: '标签透传插件配置', value: 'tag-transmission',}, - {label: '消息队列禁止消费', value: 'mq-consume-prohibition',}, - {label: '数据库禁写插件配置', value: 'database-write-prohibition',}, - {label: '其他配置', value: 'other',}, -] \ No newline at end of file +function updateOptions() { + options.value = [ + {label: i18n.global.t('common.router'), value: 'router'}, + {label: i18n.global.t('common.springbootRegistry'), value: 'springboot-registry'}, + {label: i18n.global.t('common.serviceRegistry'), value: 'service-registry'}, + {label: i18n.global.t('common.flowcontrol'), value: 'flowcontrol'}, + {label: i18n.global.t('common.removal'), value: 'removal'}, + {label: i18n.global.t('common.loadbalancer'), value: 'loadbalancer'}, + {label: i18n.global.t('common.tagTransmission'), value: 'tag-transmission'}, + {label: i18n.global.t('common.mqConsumeProhibition'), value: 'mq-consume-prohibition'}, + {label: i18n.global.t('common.databaseWriteProhibition'), value: 'database-write-prohibition'}, + {label: i18n.global.t('common.other'), value: 'other'}, + ]; +} + +// Initial call to populate options on startup +updateOptions(); + +export {options}; diff --git a/sermant-backend/src/main/webapp/frontend/src/composables/translations.ts b/sermant-backend/src/main/webapp/frontend/src/composables/translations.ts new file mode 100644 index 0000000000..40884a7884 --- /dev/null +++ b/sermant-backend/src/main/webapp/frontend/src/composables/translations.ts @@ -0,0 +1,465 @@ +import {createI18n} from 'vue-i18n'; +import zh from 'element-plus/lib/locale/lang/zh-cn' +import en from 'element-plus/lib/locale/lang/en' +import {reactive} from "vue"; + +const messages = { + en: { + header: { + instanceStatus: 'Instance Status', + eventManagement: 'Event Management', + configurationManagement: 'Configuration Management', + monitoring: 'Monitoring', + configuration: 'Configuration', + }, + instanceView: { + event: 'Event', + instanceStatus: 'Instance Status', + application: 'Application', + service: 'Service', + instance: 'Instance', + version: 'Version', + status: 'Status', + instanceID: 'Instance ID', + heartbeat: 'Heartbeat', + plugin: 'Plugin', + successfullyObtainedInstances: 'Successfully obtained instances', + failedToObtainInstances: 'Failed to obtain instances', + }, + eventViews: { + instance: 'Instance', + eventMonitoring: 'Event Monitoring', + emergencyEvent: 'Emergency', + importantEvent: 'Important', + normalEvent: 'Normal', + service: 'Service', + startTime: 'Start Time', + endTime: 'End Time', + autoRefresh: 'Auto Refresh', + logDetail: 'Log Detail', + logLevel: 'Log Level', + logInformation: 'Log Information', + logRelatedClass: 'Log Related Class', + logRelatedMethod: 'Log Related Method', + logLine: 'Log Line', + threadID: 'Thread ID', + exceptionType: 'Exception Type', + eventDetail: 'Event Detail', + description: 'Description', + event: 'Event', + level: 'Level', + emergency: 'Emergency', + important: 'Important', + normal: 'Normal', + type: 'Type', + log: 'Log', + operation: 'Operation', + governance: 'Governance', + area: 'Area', + time: 'Time', + lastHalfHour: 'Last Half Hour', + lastHour: 'Last Hour', + lastDay: 'Last Day', + lastThreeDays: 'Last Three Days', + lastWeek: 'Last Week', + enableAutoRefresh: 'Enable Auto Refresh', + disableAutoRefresh: 'Disable Auto Refresh', + agentStartup: 'Agent Startup', + agentShutdown: 'Agent Shutdown', + enhanceSuccess: 'Enhance Success', + enhanceFail: 'Enhance Fail', + serviceStop: 'Service Stop', + serviceStart: 'Service Start', + pluginInstall: 'Plugin Install', + warningLog: 'Warning Log', + errorLog: 'Error Log', + rateLimiting: 'RateLimiting', + circuitBreaker: 'CircuitBreaker', + adaptiveOverloadProtection: "Adaptive Overload Protection", + gracefulOnlineBegin: "Graceful Online Begin", + gracefulOnlineEnd: "Graceful Online End", + gracefulOfflineBegin: "Graceful Offline Begin", + gracefulOfflineEnd: "Graceful Offline End", + routerRuleRefresh: "Router Rule Refresh", + sameTagRuleMatch: "Same Tag Rule Match", + sameTagRuleMismatch: "Same Tag Rule Mismatch", + instanceRemoval: "Instance Removal", + instanceRecovery: "Instance Recovery", + springbootRegistry: "Springboot Registry", + springbootUnRegistry: "Springboot UnRegistry", + springbootGrayConfigRefresh: "Springboot Gray Config Refresh", + successfullyObtainedEvents: "Successfully obtained events", + failedToObtainEvents: "Failed to obtain events" + }, + eventConfigView: { + event: 'Event', + configuration: 'Configuration', + webHookConfiguration: 'WebHook Configuration', + switch: 'Switch', + type: 'Type', + address: 'Address', + operation: 'Operation', + edit: 'Edit', + testConnection: 'Test Connection', + feiShu: 'FeiShu', + dingTalk: 'Dingtalk', + successfullyObtainedWebhookConfiguration: 'Successfully obtained webhook configuration', + failedToObtainWebHookConfiguration: 'Failed to obtain WebHook configuration', + success: 'Success', + failed: 'Failed', + successfullyInitiatedWebHookTest: 'Successfully initiated WebHook test', + failedToInitiateWebHookTest: 'Failed to initiate WebHook test', + }, + configView: { + instance: 'Instance', + configurationManagement: 'Configuration Management', + configurationCenterInformation: 'Configuration Center Information', + type: 'Type', + address: 'Address', + user: 'User', + configurationInformation: 'Configuration Information', + plugin: 'Plugin', + configType: 'Type', + inputProjectName: 'Input project name', + project: 'Project', + inputApplicationName: 'Input application name', + application: 'Application', + service: 'Service', + inputServiceName: 'Input service name', + environment: 'Environment', + inputEnvironmentName: 'Input environment name', + zone: 'Zone', + inputZoneName: 'Input zone name', + inputGroup: 'Input group', + inputKey: 'Input key', + operation: 'Operation', + view: 'View', + areYouSureToDeleteTheCurrentConfiguration: 'Are you sure to delete the current configuration?', + delete: 'Delete', + yes: 'Yes', + no: 'No', + create: 'Create', + theProjectCannotBeEmpty: 'The project cannot be empty', + theGroupCannotBeEmpty: 'The group cannot be empty', + successfullyObtainedConfiguration: 'Successfully obtained configuration', + failedObtainedConfiguration: 'Failed to obtain configuration', + successfullyDeletedConfiguration: 'Successfully deleted configuration', + failedDeletedConfiguration: 'Failed to delete configuration', + }, + common: { + success: 'Success', + unableToConnectToConfigurationCenter: 'Unable to connect to configuration center', + failedToObtainConfiguration: 'Failed to obtain configuration', + configurationAlreadyExists: 'Configuration already exists', + failedToCreateConfiguration: 'Failed to create configuration', + failedToPublishConfiguration: 'Failed to publish configuration', + failedToDeleteConfiguration: 'Failed to delete configuration', + configurationDoesNotExist: 'Configuration does not exist', + missingRequestParameters: 'Missing request parameters', + failedToRequest: 'Failed to request', + router: 'router', + springbootRegistry: 'springboot-registry', + serviceRegistry: 'service-registry', + flowcontrol: 'flowcontrol', + removal: 'service-removal', + loadbalancer: 'loadbalancer', + tagTransmission: 'tag-transmission', + mqConsumeProhibition: 'mq-consume-prohibition', + databaseWriteProhibition: 'database-write-prohibition', + other: 'other', + }, + configInfo: { + configurationManagement: 'Configuration Management', + configurationDetail: 'Configuration Detail', + pluginInformation: 'Plugin Information', + type: 'Type', + selectPlugin: 'Select plugin', + ruleInformation: 'Rule Information', + ruleType: 'Rule Type', + selectRuleType: 'Select rule type', + ruleTypeSupportedInPlugin : 'Rule type supported in plugin', + inputSceneName: 'Input scene name', + sceneName: 'Scene Name', + flowcontrolRuleNotice: 'The flow control rule will only take effect when the scene name matches the traffic matching rule', + loadbalancerRuleNotice: 'The load balancing strategy will only take effect when the scenario of traffic tagging and load balancing strategy is consistent', + inputProjectName: 'Input project name', + projectNotice: 'This configuration corresponds to the service.meta.project in the sermant configuration file', + inputApplicationName: 'Input application name', + applicationNotice: 'This configuration corresponds to the service.meta.application in the sermant configuration file', + inputServiceNameOrEmpty: 'Input service name (When empty, it is a global configuration)', + metaServiceNotice: 'This configuration corresponds to the service.meta.service in the sermant configuration file', + inputEnvironmentName: 'Input environment name', + environmentNotice: 'This configuration corresponds to the service.meta.environment in the sermant configuration file', + inputZoneName: 'Input zone name', + zoneNotice: 'This configuration corresponds to the service.meta.zone in the sermant configuration file', + serviceInformation: 'Service Information', + inputServiceName: 'Input service name', + serviceNotice: 'The name of the microservice is determined by the dubbo.application.name and spring.application.name in the microservice configuration file', + configurationInformation: 'Configuration Information', + inputConfigurationGroup : 'Input configuration group', + configurationNotice: 'Automatically generated when configuring the Sermant native plugin configuration, no modification required', + inputConfigurationName: ' Input configuration name', + configurationContent: 'Configuration Content', + inputConfigurationContent: 'Input configuration content', + configurationContentNotice: 'The native Sermant plugin requires the use of yaml format', + trafficMatchingRule:'Traffic Matching Rule', + rateLimitingRule:'Rate Limiting Rule', + circuitBreakerRule:'Circuit Breaker Rule', + bulkheadRule:'Bulkhead Rule', + faultInjectionRule:'Fault Injection Rule', + retryRule:'Retry Rule', + systemLevelFlowControl:'System Level Flow Control', + trafficTaggingRule:'Traffic Tagging Rule', + loadBalancingRule:'Load Balancing Rule', + successfullyCreatedConfiguration:'Successfully created configuration', + failedToCreateConfiguration:'Failed to create configuration', + successfullyUpdatedConfiguration:'Successfully updated configuration', + failedToUpdateConfiguration:'Failed to update configuration', + failedToObtainConfiguration:'Failed to obtain configuration', + submit: 'Submit', + } + }, + zh: { + header: { + instanceStatus: '实例状态', + eventManagement: '事件管理', + configurationManagement: '配置管理', + monitoring: '监测', + configuration: '配置', + }, + instanceView: { + event: '事件', + instanceStatus: '实例状态', + application: '应用', + service: '服务', + instance: '实例', + version: '版本', + status: '状态', + instanceID: '实例ID', + heartbeat: '心跳时间', + plugin: '插件', + successfullyObtainedInstances: '获取实例数据成功', + failedToObtainInstances: '获取实例数据失败', + }, + eventViews: { + instance: '实例', + eventMonitoring: '事件监测', + emergencyEvent: '紧急事件', + importantEvent: '重要事件', + normalEvent: '一般事件', + service: '服务', + startTime: '开始时间', + endTime: '结束时间', + autoRefresh: '自动刷新', + logDetail: '日志详情', + logLevel: '日志级别', + logInformation: '日志信息', + logRelatedClass: '日志触发类', + logRelatedMethod: '日志触发方法', + logLine: '日志触发行号', + threadID: '线程ID', + exceptionType: '异常类型', + eventDetail: '事件详情', + description: '描述信息', + event: '事件', + level: '事件级别', + emergency: '紧急', + important: '重要', + normal: '一般', + type: '事件类型', + log: '日志', + operation: '运行', + governance: '治理', + area: '事件区域', + time: '上报时间', + lastHalfHour: '最近半小时', + lastHour: '最近一小时', + lastDay: '最近一天', + lastThreeDays: '最近三天', + lastWeek: '最近一周', + enableAutoRefresh: '开启自动刷新', + disableAutoRefresh: '关闭自动刷新', + agentStartup: 'Agent启动', + agentShutdown: 'Agent停止', + enhanceSuccess: '增强成功', + enhanceFail: '增强失败', + serviceStop: '服务停止', + serviceStart: '服务启动', + pluginInstall: '插件加载', + warningLog: '警告日志', + errorLog: '错误日志', + rateLimiting: '限流', + circuitBreaker: '熔断', + adaptiveOverloadProtection: "自适应过载保护", + gracefulOnlineBegin: "无损上线开始", + gracefulOnlineEnd: "无损上线结束", + gracefulOfflineBegin: "无损下线开始", + gracefulOfflineEnd: "无损下线结束", + routerRuleRefresh: "路由插件规则刷新", + sameTagRuleMatch: "同标签优先规则匹配成功", + sameTagRuleMismatch: "同标签优先规则匹配失败", + instanceRemoval: "实例摘除", + instanceRecovery: "实例恢复", + springbootRegistry: "springboot服务注册", + springbootUnRegistry: "springboot服务移除注册", + springbootGrayConfigRefresh: "springboot注册插件灰度规则刷新", + successfullyObtainedEvents: "获取事件成功", + failedToObtainEvents: "获取事件失败" + }, + eventConfigView: { + event: '事件', + configuration: '事件配置', + webHookConfiguration: 'WebHook 配置', + switch: '开关', + type: '类型', + address: '地址', + operation: '操作', + edit: '编辑', + testConnection: '测试连接', + feiShu: '飞书', + dingTalk: '钉钉', + successfullyObtainedWebhookConfiguration: '获取WebHook配置成功', + failedToObtainWebHookConfiguration: '获取WebHook配置失败', + success: '设置成功', + failed: '设置失败', + successfullyInitiatedWebHookTest: '发起WebHook测试成功', + failedToInitiateWebHookTest: '发起WebHook测试失败', + }, + configView: { + instance: '实例', + configurationManagement: '配置管理', + configurationCenterInformation: '配置中心信息', + type: '配置中心类型', + address: '配置中心地址', + user: '用户名称', + configurationInformation: '配置信息', + plugin: '插件类型', + configType: '配置类型', + inputProjectName: '请输入命名空间', + project: '命名空间', + inputApplicationName: '请输入应用名称', + application: '应用名称', + service: '服务名称', + inputServiceName: '请输入服务名称', + environment: '环境名称', + inputEnvironmentName: '请输入环境名称', + zone: '区域名称', + inputZoneName: '请输入区域名称', + inputGroup: '请输入group', + inputKey: '请输入key', + operation: '操作类型', + view: '查看', + areYouSureToDeleteTheCurrentConfiguration: '是否确认删除当前配置项?', + delete: '删除', + yes: '是', + no: '否', + create: '新增', + theProjectCannotBeEmpty: '命名空间不能为空', + theGroupCannotBeEmpty: 'group不能为空', + successfullyObtainedConfiguration: '获取配置成功', + failedObtainedConfiguration: '获取配置失败', + successfullyDeletedConfiguration: '删除配置成功', + failedDeletedConfiguration: '删除配置失败', + }, + common: { + success: '成功', + unableToConnectToConfigurationCenter: '无法连接配置中心', + failedToObtainConfiguration: '配置查询失败', + configurationAlreadyExists: '配置已存在', + failedToCreateConfiguration: '新增配置失败', + failedToPublishConfiguration: '发布配置失败', + failedToDeleteConfiguration: '删除配置失败', + configurationDoesNotExist: '配置不存在', + missingRequestParameters: '缺少请求参数', + failedToRequest: '请求失败', + router: '路由插件', + springbootRegistry: 'springboot注册插件', + serviceRegistry: '注册迁移插件', + flowcontrol: '流控插件', + removal: '离群实例摘除插件', + loadbalancer: '负载均衡插件', + tagTransmission: '标签透传插件', + mqConsumeProhibition: '消息队列禁止消费插件', + databaseWriteProhibition: '数据库禁写插件', + other: '其他', + }, + configInfo: { + configurationManagement: '配置管理', + configurationDetail: '配置详情', + pluginInformation: '插件配置信息', + type: '插件类型', + selectPlugin: '请选择插件类型', + ruleInformation: '规则信息', + ruleType: '规则类型', + selectRuleType: '请选择规则类型', + ruleTypeSupportedInPlugin : '插件支持的规则类型', + inputSceneName: '请输入规则场景名称', + sceneName: '规则场景名称', + flowcontrolRuleNotice: '流量匹配和具体流控规则的场景名称一致时流控规则才会生效', + loadbalancerRuleNotice: '流量标记和负载均衡策略的场景一致时负载均衡策略生效', + inputProjectName: '请输入命名空间', + projectNotice: '该配置对应sermant配置文件中的service.meta.project', + inputApplicationName: '请输入应用名称', + applicationNotice: '该配置对应sermant配置文件中的service.meta.application', + inputServiceNameOrEmpty: '请输入服务名称,为空时下发全局配置', + metaServiceNotice: '该配置对应sermant配置文件中的service.meta.service', + inputEnvironmentName: '请输入环境名称', + environmentNotice: '该配置对应sermant配置文件中的service.meta.environment', + inputZoneName: '请输入区域名称', + zoneNotice: '该配置对应sermant配置文件中的service.meta.zone', + serviceInformation: '服务信息', + inputServiceName: '请输入服务名称', + serviceNotice: '微服务的名称,由微服务配置文件的dubbo.application.name、spring.applicaton.name确定', + configurationInformation: '配置项信息', + inputConfigurationGroup : '请输入配置项的group', + configurationNotice: '配置sermant原生插件配置时,自动生成,无需修改', + inputConfigurationName: '请输入配置项名称', + configurationContent: '配置内容', + inputConfigurationContent: '请输入配置内容', + configurationContentNotice: 'sermant原生插件需使用yaml格式', + trafficMatchingRule:'流量匹配规则', + rateLimitingRule:'限流规则', + circuitBreakerRule:'熔断规则', + bulkheadRule:'隔离规则', + faultInjectionRule:'错误注入', + retryRule:'重试', + systemLevelFlowControl:'系统级流控', + trafficTaggingRule:'流量标记规则', + loadBalancingRule:'负载均衡规则', + successfullyCreatedConfiguration:'新增配置成功', + failedToCreateConfiguration:'新增配置失败', + successfullyUpdatedConfiguration:'更新配置成功', + failedToUpdateConfiguration:'更新配置失败', + failedToObtainConfiguration:'获取配置失败', + submit: '提交', + } + }, +}; + +const getSavedLanguage = () => { + return localStorage.getItem('language') || 'zh'; // 默认语言为英语 +}; + +const i18n = createI18n({ + locale: getSavedLanguage(), + fallbackLocale: getSavedLanguage(), + messages, +}); + +export const localeLanguage = reactive({ + value: getSavedLanguage() == 'zh' ? zh : en, + language: getSavedLanguage() +}); + +export function switchLocale(lang: ('zh' | 'en')) { + if (lang === 'en') { + localeLanguage.value = en; + } else { + localeLanguage.value = zh; + } + localeLanguage.language = lang; + i18n.global.locale = lang; + localStorage.setItem('language', lang); +} + +export default i18n; \ No newline at end of file diff --git a/sermant-backend/src/main/webapp/frontend/src/main.ts b/sermant-backend/src/main/webapp/frontend/src/main.ts index 855ce52ba8..ae2443ec0c 100644 --- a/sermant-backend/src/main/webapp/frontend/src/main.ts +++ b/sermant-backend/src/main/webapp/frontend/src/main.ts @@ -5,7 +5,8 @@ import router from "./router" import * as ElementPlusIconsVue from '@element-plus/icons-vue' import ElementPlus from 'element-plus' -import zhCn from 'element-plus/dist/locale/zh-cn.mjs' +import translations from './composables/translations'; +import i18n from './composables/translations'; import "~/styles/index.scss"; @@ -20,9 +21,11 @@ const app = createApp(App); for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } - -app.use(ElementPlus, { - locale: zhCn, -}) +app.use(translations); +app.use(i18n) +app.use(ElementPlus); app.use(router); + +app.config.globalProperties.$t = i18n.global.t; + app.mount("#app"); diff --git a/sermant-backend/src/main/webapp/frontend/src/views/ConfigInfo.vue b/sermant-backend/src/main/webapp/frontend/src/views/ConfigInfo.vue index 07a4a2b689..9fd97f3ce2 100644 --- a/sermant-backend/src/main/webapp/frontend/src/views/ConfigInfo.vue +++ b/sermant-backend/src/main/webapp/frontend/src/views/ConfigInfo.vue @@ -1,155 +1,163 @@ - + - 配置详情 + {{ $t('configInfo.configurationDetail') }} - + - 插件类型 + {{ $t('configInfo.type') }} - - 插件类型 + {{ $t('configInfo.type') }} + :title="$t('configInfo.ruleInformation')" style="margin-top: 15px;"> - 规则类型 + {{ $t('configInfo.ruleType') }} - - + - 规则类型 + {{ $t('configInfo.ruleType') }} - + - - 规则场景名称 + {{ $t('configInfo.sceneName') }} - - + + - project - + - application - + - service - + - environment - + - zone - + - + - service - + service - service - + - + - @@ -157,10 +165,11 @@ group - + - @@ -168,10 +177,10 @@ group - + - @@ -179,7 +188,7 @@ key - + key - + - + - + - 提交 + {{ $t('configInfo.submit') }} @@ -213,37 +222,61 @@ diff --git a/sermant-backend/src/main/webapp/frontend/src/views/EventsView.vue b/sermant-backend/src/main/webapp/frontend/src/views/EventsView.vue index e93ba6dee5..16a1b61d85 100644 --- a/sermant-backend/src/main/webapp/frontend/src/views/EventsView.vue +++ b/sermant-backend/src/main/webapp/frontend/src/views/EventsView.vue @@ -1,19 +1,19 @@ - + - 事件监测 + {{ $t('eventViews.eventMonitoring') }} - 紧急事件 + {{ $t('eventViews.emergencyEvent') }} {{ eventCount.emergency }} @@ -21,11 +21,11 @@ - 重要事件 + {{ $t('eventViews.importantEvent') }} {{ eventCount.important }} @@ -33,11 +33,11 @@ - 一般事件 + {{ $t('eventViews.normalEvent') }} {{ eventCount.normal }} @@ -46,30 +46,30 @@ - 服务: {{ tag }} + {{ $t('eventViews.service') }}: {{ tag }} IP: {{ tag }} @@ -78,12 +78,12 @@ - + - 服务 + {{ $t('eventViews.service') }} IP @@ -93,143 +93,145 @@ - + + + 自动刷新 + size="large" + @click="changeAutoRefreshState" + :type="autoRefresh ? 'success' : ''" + :loading="autoLoadingState" + >{{ $t('eventViews.autoRefresh') }} - + - 日志级别 + {{ $t('eventViews.logLevel') }} {{ props.row.info.logLevel }} - 日志信息 + {{ $t('eventViews.logInformation') }} {{ props.row.info.logMessage }} - 日志触发类 + {{ $t('eventViews.logRelatedClass') }} {{ props.row.info.logClass }} - 日志触发方法 + {{ $t('eventViews.logRelatedMethod') }} {{ props.row.info.logMethod }} - 日志触发行号 + {{ $t('eventViews.logLine') }} {{ props.row.info.logLineNumber }} - 线程ID + {{ $t('eventViews.threadID') }} {{ props.row.info.logThreadId }} - 异常类型 + {{ $t('eventViews.exceptionType') }} {{ props.row.info.logThrowable }} - 描述信息 + {{ $t('eventViews.description') }} {{ props.row.info.description }} - + {{ eventName[scope.row.info.logLevel] }} {{ - eventName[scope.row.info.name] - }} + eventName[scope.row.info.name] + }} - 紧急 + {{ $t('eventViews.emergency') }} - 重要 + {{ $t('eventViews.important') }} - 一般 + {{ $t('eventViews.normal') }} - 日志 - 运行 - 治理 + {{ $t('eventViews.log') }} + {{ $t('eventViews.operation') }} + {{ $t('eventViews.governance') }} - - + + @@ -241,7 +243,7 @@ - + {{ time(scope.row.time) }} @@ -251,21 +253,22 @@
紧急事件
{{ $t('eventViews.emergencyEvent') }}
重要事件
{{ $t('eventViews.importantEvent') }}
一般事件
{{ $t('eventViews.normalEvent') }}