feat: 支持用户设置默认语言

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2024-08-09 17:44:05 +08:00 committed by Craftsman
parent 6d80ded7ff
commit 7cbe5f5f30
11 changed files with 274 additions and 165 deletions

View File

@ -1,8 +1,11 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import axios from 'axios';
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
@ -17,9 +20,17 @@ const messages = LANG_FILES.keys().reduce((messages, path) => {
export const getLanguage = () => {
let language = localStorage.getItem('language');
if (!language) {
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get('/system/default-locale').then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace('_', '-');
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({

View File

@ -1,51 +1,60 @@
import Vue from 'vue';
import VueI18n from "vue-i18n";
import VueI18n from 'vue-i18n';
import axios from 'axios';
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/)
const LANG_FILES = require.context('./lang', true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
const value = LANG_FILES(path)
const value = LANG_FILES(path);
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
if (LOADED_LANGUAGES.includes(lang)) {
messages[lang] = value.default
messages[lang] = value.default;
}
return messages;
}, {})
}, {});
export const getLanguage = () => {
let language = localStorage.getItem('language')
let language = localStorage.getItem('language');
if (!language) {
language = navigator.language || navigator.browserLanguage
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get('/system/default-locale').then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace('_', '-');
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({
locale: getLanguage(),
messages,
});
const importLanguage = lang => {
const importLanguage = (lang) => {
if (!LOADED_LANGUAGES.includes(lang)) {
return import(`./lang/${lang}`).then(response => {
return import(`./lang/${lang}`).then((response) => {
i18n.mergeLocaleMessage(lang, response.default);
LOADED_LANGUAGES.push(lang);
return Promise.resolve(lang)
})
}
return Promise.resolve(lang)
return Promise.resolve(lang);
});
}
return Promise.resolve(lang);
};
const setLang = lang => {
localStorage.setItem('language', lang)
const setLang = (lang) => {
localStorage.setItem('language', lang);
i18n.locale = lang;
}
};
export const setLanguage = lang => {
export const setLanguage = (lang) => {
if (!lang) {
return;
}
@ -55,24 +64,24 @@ export const setLanguage = lang => {
} else {
setLang(lang);
}
}
};
// 组合翻译例如key为'请输入{0}'keys为login.username则自动将keys翻译并替换到{0} {1}...
Vue.prototype.$tm = function (key, ...keys) {
let values = [];
for (const k of keys) {
values.push(i18n.t(k))
values.push(i18n.t(k));
}
return i18n.t(key, values);
};
// 忽略警告不存在Key直接返回Key
Vue.prototype.$tk = function (key) {
const hasKey = i18n.te(key)
const hasKey = i18n.te(key);
if (hasKey) {
return i18n.t(key)
return i18n.t(key);
}
return key
return key;
};
// 设置当前语言LOADED_LANGUAGES以外的翻译文件会自动从lang目录获取(如果有的话), 如果不需要动态加载语言文件直接用setLang

View File

@ -82,6 +82,8 @@ public class FilterChainUtils {
filterChainDefinitionMap.put("/performance/update/cache", "anon");
// websocket
filterChainDefinitionMap.put("/websocket/**", "csrf");
// 默认语言
filterChainDefinitionMap.put("/system/default-locale", "anon");
return filterChainDefinitionMap;
}

View File

@ -0,0 +1,28 @@
package io.metersphere.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
@Configuration
public class BundleConfig {
@Value("${spring.messages.default-locale}")
private String defaultLocale;
@Value("${spring.messages.basename}")
private String[] basements;
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
Locale defaultLocale = Locale.forLanguageTag(this.defaultLocale);
// 设置消息资源文件的基名
messageSource.setBasenames(basements);
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name()); // 设置编码
messageSource.setDefaultLocale(defaultLocale); // 设置默认语言
return messageSource;
}
}

View File

@ -16,6 +16,7 @@ import io.metersphere.service.BaseUserService;
import io.metersphere.service.SystemParameterService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;
@ -34,6 +35,9 @@ public class SystemParameterController {
@Resource
private BaseUserService baseUserService;
@Value("${spring.messages.default-locale}")
private String defaultLocale;
@PostMapping("/edit/email")
@RequiresPermissions(PermissionConstants.SYSTEM_SETTING_READ_EDIT)
@ -139,5 +143,10 @@ public class SystemParameterController {
return systemParameter;
}
@GetMapping(value = "/default-locale")
public String defaultLocale() {
return defaultLocale;
}
}

View File

@ -108,3 +108,4 @@ eureka.client.registry-fetch-interval-seconds=10
# redisson
spring.redis.redisson.file=file:/opt/metersphere/conf/redisson.yml
spring.session.redis.repository-type=indexed
spring.messages.default-locale=zh_CN

View File

@ -1,75 +1,84 @@
import Vue from 'vue';
import Vue from "vue";
import VueI18n from "vue-i18n";
import axios from "axios";
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/)
const LOADED_LANGUAGES = ["zh-CN", "zh-TW", "en-US"];
const LANG_FILES = require.context("./lang", true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
const value = LANG_FILES(path)
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
const value = LANG_FILES(path);
const lang = path.replace(/^\.\/(.*)\.\w+$/, "$1");
if (LOADED_LANGUAGES.includes(lang)) {
messages[lang] = value.default
messages[lang] = value.default;
}
return messages;
}, {})
}, {});
export const getLanguage = () => {
let language = localStorage.getItem('language')
let language = localStorage.getItem("language");
if (!language) {
language = navigator.language || navigator.browserLanguage
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get("/system/default-locale").then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace("_", "-");
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({
locale: getLanguage(),
messages,
});
const importLanguage = lang => {
const importLanguage = (lang) => {
if (!LOADED_LANGUAGES.includes(lang)) {
return import(`./lang/${lang}`).then(response => {
return import(`./lang/${lang}`).then((response) => {
i18n.mergeLocaleMessage(lang, response.default);
LOADED_LANGUAGES.push(lang);
return Promise.resolve(lang)
})
}
return Promise.resolve(lang)
return Promise.resolve(lang);
});
}
return Promise.resolve(lang);
};
const setLang = lang => {
localStorage.setItem('language', lang)
const setLang = (lang) => {
localStorage.setItem("language", lang);
i18n.locale = lang;
}
};
export const setLanguage = lang => {
export const setLanguage = (lang) => {
if (lang) {
lang = lang.replace('_', '-');
lang = lang.replace("_", "-");
}
if (i18n.locale !== lang) {
importLanguage(lang).then(setLang);
}
}
};
// 组合翻译例如key为'请输入{0}'keys为login.username则自动将keys翻译并替换到{0} {1}...
Vue.prototype.$tm = function (key, ...keys) {
let values = [];
for (const k of keys) {
values.push(i18n.t(k))
values.push(i18n.t(k));
}
return i18n.t(key, values);
};
// 忽略警告不存在Key直接返回Key
Vue.prototype.$tk = function (key) {
const hasKey = i18n.te(key)
const hasKey = i18n.te(key);
if (hasKey) {
return i18n.t(key)
return i18n.t(key);
}
return key
return key;
};
// 设置当前语言LOADED_LANGUAGES以外的翻译文件会自动从lang目录获取(如果有的话), 如果不需要动态加载语言文件直接用setLang

View File

@ -1,76 +1,85 @@
import Vue from 'vue';
import Vue from "vue";
import VueI18n from "vue-i18n";
import axios from "axios";
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/)
const LOADED_LANGUAGES = ["zh-CN", "zh-TW", "en-US"];
const LANG_FILES = require.context("./lang", true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
const value = LANG_FILES(path)
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
const value = LANG_FILES(path);
const lang = path.replace(/^\.\/(.*)\.\w+$/, "$1");
if (LOADED_LANGUAGES.includes(lang)) {
messages[lang] = value.default
messages[lang] = value.default;
}
return messages;
}, {})
}, {});
export const getLanguage = () => {
let language = localStorage.getItem('language')
let language = localStorage.getItem("language");
if (!language) {
language = navigator.language || navigator.browserLanguage
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get("/system/default-locale").then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace("_", "-");
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({
locale: getLanguage(),
messages,
silentTranslationWarn: true // todo 暂时隐藏控制台警告
silentTranslationWarn: true, // todo 暂时隐藏控制台警告
});
const importLanguage = lang => {
const importLanguage = (lang) => {
if (!LOADED_LANGUAGES.includes(lang)) {
return import(`./lang/${lang}`).then(response => {
return import(`./lang/${lang}`).then((response) => {
i18n.mergeLocaleMessage(lang, response.default);
LOADED_LANGUAGES.push(lang);
return Promise.resolve(lang)
})
}
return Promise.resolve(lang)
return Promise.resolve(lang);
});
}
return Promise.resolve(lang);
};
const setLang = lang => {
localStorage.setItem('language', lang)
const setLang = (lang) => {
localStorage.setItem("language", lang);
i18n.locale = lang;
}
};
export const setLanguage = lang => {
export const setLanguage = (lang) => {
if (lang) {
lang = lang.replace('_', '-');
lang = lang.replace("_", "-");
}
if (i18n.locale !== lang) {
importLanguage(lang).then(setLang);
}
}
};
// 组合翻译例如key为'请输入{0}'keys为login.username则自动将keys翻译并替换到{0} {1}...
Vue.prototype.$tm = function (key, ...keys) {
let values = [];
for (const k of keys) {
values.push(i18n.t(k))
values.push(i18n.t(k));
}
return i18n.t(key, values);
};
// 忽略警告不存在Key直接返回Key
Vue.prototype.$tk = function (key) {
const hasKey = i18n.te(key)
const hasKey = i18n.te(key);
if (hasKey) {
return i18n.t(key)
return i18n.t(key);
}
return key
return key;
};
// 设置当前语言LOADED_LANGUAGES以外的翻译文件会自动从lang目录获取(如果有的话), 如果不需要动态加载语言文件直接用setLang

View File

@ -1,76 +1,85 @@
import Vue from 'vue';
import Vue from "vue";
import VueI18n from "vue-i18n";
import axios from "axios";
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/)
const LOADED_LANGUAGES = ["zh-CN", "zh-TW", "en-US"];
const LANG_FILES = require.context("./lang", true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
const value = LANG_FILES(path)
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
const value = LANG_FILES(path);
const lang = path.replace(/^\.\/(.*)\.\w+$/, "$1");
if (LOADED_LANGUAGES.includes(lang)) {
messages[lang] = value.default
messages[lang] = value.default;
}
return messages;
}, {})
}, {});
export const getLanguage = () => {
let language = localStorage.getItem('language')
let language = localStorage.getItem("language");
if (!language) {
language = navigator.language || navigator.browserLanguage
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get("/system/default-locale").then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace("_", "-");
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({
locale: getLanguage(),
messages,
silentTranslationWarn: true // todo 暂时隐藏控制台警告
silentTranslationWarn: true, // todo 暂时隐藏控制台警告
});
const importLanguage = lang => {
const importLanguage = (lang) => {
if (!LOADED_LANGUAGES.includes(lang)) {
return import(`./lang/${lang}`).then(response => {
return import(`./lang/${lang}`).then((response) => {
i18n.mergeLocaleMessage(lang, response.default);
LOADED_LANGUAGES.push(lang);
return Promise.resolve(lang)
})
}
return Promise.resolve(lang)
return Promise.resolve(lang);
});
}
return Promise.resolve(lang);
};
const setLang = lang => {
localStorage.setItem('language', lang)
const setLang = (lang) => {
localStorage.setItem("language", lang);
i18n.locale = lang;
}
};
export const setLanguage = lang => {
export const setLanguage = (lang) => {
if (lang) {
lang = lang.replace('_', '-');
lang = lang.replace("_", "-");
}
if (i18n.locale !== lang) {
importLanguage(lang).then(setLang);
}
}
};
// 组合翻译例如key为'请输入{0}'keys为login.username则自动将keys翻译并替换到{0} {1}...
Vue.prototype.$tm = function (key, ...keys) {
let values = [];
for (const k of keys) {
values.push(i18n.t(k))
values.push(i18n.t(k));
}
return i18n.t(key, values);
};
// 忽略警告不存在Key直接返回Key
Vue.prototype.$tk = function (key) {
const hasKey = i18n.te(key)
const hasKey = i18n.te(key);
if (hasKey) {
return i18n.t(key)
return i18n.t(key);
}
return key
return key;
};
// 设置当前语言LOADED_LANGUAGES以外的翻译文件会自动从lang目录获取(如果有的话), 如果不需要动态加载语言文件直接用setLang

View File

@ -1,87 +1,100 @@
import Vue from 'vue';
import Vue from "vue";
import VueI18n from "vue-i18n";
import axios from "axios";
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/)
const LOADED_LANGUAGES = ["zh-CN", "zh-TW", "en-US"];
const LANG_FILES = require.context("./lang", true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
const lang = path.replace(/^\.\/(.*)\.\w+$/, "$1");
if (LOADED_LANGUAGES.includes(lang)) {
const value = LANG_FILES(path)
messages[lang] = value.default
const value = LANG_FILES(path);
messages[lang] = value.default;
}
return messages;
}, {});
// 添加脑图国际化文件
const MINDER_LANG_FILES = require.context('vue-minder-editor-plus/src/locale/lang', true, /\.js$/);
MINDER_LANG_FILES.keys().forEach(path => {
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
const MINDER_LANG_FILES = require.context(
"vue-minder-editor-plus/src/locale/lang",
true,
/\.js$/
);
MINDER_LANG_FILES.keys().forEach((path) => {
const lang = path.replace(/^\.\/(.*)\.\w+$/, "$1");
if (LOADED_LANGUAGES.includes(lang)) {
const value = MINDER_LANG_FILES(path);
Object.keys(value.default).forEach(key => {
Object.keys(value.default).forEach((key) => {
messages[lang][key] = value.default[key];
});
}
});
export const getLanguage = () => {
let language = localStorage.getItem('language')
let language = localStorage.getItem("language");
if (!language) {
language = navigator.language || navigator.browserLanguage
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get("/system/default-locale").then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace("_", "-");
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({
locale: getLanguage(),
messages,
});
const importLanguage = lang => {
const importLanguage = (lang) => {
if (!LOADED_LANGUAGES.includes(lang)) {
return import(`./lang/${lang}`).then(response => {
return import(`./lang/${lang}`).then((response) => {
i18n.mergeLocaleMessage(lang, response.default);
LOADED_LANGUAGES.push(lang);
return Promise.resolve(lang)
})
}
return Promise.resolve(lang)
return Promise.resolve(lang);
});
}
return Promise.resolve(lang);
};
const setLang = lang => {
localStorage.setItem('language', lang)
const setLang = (lang) => {
localStorage.setItem("language", lang);
i18n.locale = lang;
}
};
export const setLanguage = lang => {
export const setLanguage = (lang) => {
if (lang) {
lang = lang.replace('_', '-');
lang = lang.replace("_", "-");
}
if (i18n.locale !== lang) {
importLanguage(lang).then(setLang);
}
}
};
// 组合翻译例如key为'请输入{0}'keys为login.username则自动将keys翻译并替换到{0} {1}...
Vue.prototype.$tm = function (key, ...keys) {
let values = [];
for (const k of keys) {
values.push(i18n.t(k))
values.push(i18n.t(k));
}
return i18n.t(key, values);
};
// 忽略警告不存在Key直接返回Key
Vue.prototype.$tk = function (key) {
const hasKey = i18n.te(key)
const hasKey = i18n.te(key);
if (hasKey) {
return i18n.t(key)
return i18n.t(key);
}
return key
return key;
};
// 设置当前语言LOADED_LANGUAGES以外的翻译文件会自动从lang目录获取(如果有的话), 如果不需要动态加载语言文件直接用setLang

View File

@ -1,75 +1,84 @@
import Vue from 'vue';
import Vue from "vue";
import VueI18n from "vue-i18n";
import axios from "axios";
Vue.use(VueI18n);
// 直接加载翻译的语言文件
const LOADED_LANGUAGES = ['zh-CN', 'zh-TW', 'en-US'];
const LANG_FILES = require.context('./lang', true, /\.js$/)
const LOADED_LANGUAGES = ["zh-CN", "zh-TW", "en-US"];
const LANG_FILES = require.context("./lang", true, /\.js$/);
// 自动加载lang目录下语言文件默认只加载LOADED_LANGUAGES中规定的语言文件其他的语言动态加载
const messages = LANG_FILES.keys().reduce((messages, path) => {
const value = LANG_FILES(path)
const lang = path.replace(/^\.\/(.*)\.\w+$/, '$1');
const value = LANG_FILES(path);
const lang = path.replace(/^\.\/(.*)\.\w+$/, "$1");
if (LOADED_LANGUAGES.includes(lang)) {
messages[lang] = value.default
messages[lang] = value.default;
}
return messages;
}, {})
}, {});
export const getLanguage = () => {
let language = localStorage.getItem('language')
let language = localStorage.getItem("language");
if (!language) {
language = navigator.language || navigator.browserLanguage
// 远程接口获取用户语言
language = navigator.language || navigator.browserLanguage;
axios.get("/system/default-locale").then((response) => {
if (response.data && response.data.data) {
language = response.data.data.replace("_", "-");
}
return language;
});
} else {
return language;
}
};
const i18n = new VueI18n({
locale: getLanguage(),
messages,
});
const importLanguage = lang => {
const importLanguage = (lang) => {
if (!LOADED_LANGUAGES.includes(lang)) {
return import(`./lang/${lang}`).then(response => {
return import(`./lang/${lang}`).then((response) => {
i18n.mergeLocaleMessage(lang, response.default);
LOADED_LANGUAGES.push(lang);
return Promise.resolve(lang)
})
}
return Promise.resolve(lang)
return Promise.resolve(lang);
});
}
return Promise.resolve(lang);
};
const setLang = lang => {
localStorage.setItem('language', lang)
const setLang = (lang) => {
localStorage.setItem("language", lang);
i18n.locale = lang;
}
};
export const setLanguage = lang => {
export const setLanguage = (lang) => {
if (lang) {
lang = lang.replace('_', '-');
lang = lang.replace("_", "-");
}
if (i18n.locale !== lang) {
importLanguage(lang).then(setLang);
}
}
};
// 组合翻译例如key为'请输入{0}'keys为login.username则自动将keys翻译并替换到{0} {1}...
Vue.prototype.$tm = function (key, ...keys) {
let values = [];
for (const k of keys) {
values.push(i18n.t(k))
values.push(i18n.t(k));
}
return i18n.t(key, values);
};
// 忽略警告不存在Key直接返回Key
Vue.prototype.$tk = function (key) {
const hasKey = i18n.te(key)
const hasKey = i18n.te(key);
if (hasKey) {
return i18n.t(key)
return i18n.t(key);
}
return key
return key;
};
// 设置当前语言LOADED_LANGUAGES以外的翻译文件会自动从lang目录获取(如果有的话), 如果不需要动态加载语言文件直接用setLang