diff --git a/backend/pom.xml b/backend/pom.xml index 5e5ff6b40aa5d8bd0334a2b2fc3b855d2c126043..7500d12a39be8efc003e6eecc4c6926a658b1afd 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -86,6 +86,16 @@ org.apache.commons commons-lang3 + + org.apache.commons + commons-collections4 + 4.1 + + + org.apache.commons + commons-text + 1.8 + commons-codec commons-codec diff --git a/backend/src/main/java/io/metersphere/commons/constants/I18nConstants.java b/backend/src/main/java/io/metersphere/commons/constants/I18nConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..01a1bf13e83cca531d71f046a388c17107f181f6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/I18nConstants.java @@ -0,0 +1,11 @@ +package io.metersphere.commons.constants; + +public class I18nConstants { + + public static final String LANG_COOKIE_NAME = "MS_USER_LANG"; + + public static final String LOCAL = "local"; + public static final String CLUSTER = "cluster"; + + +} diff --git a/backend/src/main/java/io/metersphere/commons/constants/ParamConstants.java b/backend/src/main/java/io/metersphere/commons/constants/ParamConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..544201bdab9780cf3c685dca92766d40e08b9fa0 --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/ParamConstants.java @@ -0,0 +1,190 @@ +package io.metersphere.commons.constants; + +/** + * Author: chunxing + * Date: 2018/6/26 下午3:44 + * Description: + */ +public interface ParamConstants { + + String getValue(); + + enum KeyCloak implements ParamConstants { + + USERNAME("keycloak.username"), + PASSWORD("keycloak.password"), + REALM("keycloak.realm"), + AUTH_SERVER_URL("keycloak.auth-server-url"), + ADDRESS("keycloak-server-address"); + + private String value; + + KeyCloak(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + enum Type implements ParamConstants { + + PASSWORD("password"), + TEXT("text"), + JSON("json"); + + private String value; + + Type(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + enum Classify implements ParamConstants { + + KEYCLOAK("keycloak"), + MAIL("smtp"), + UI("ui"), + REGISTRY("registry"); + + private String value; + + Classify(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + enum UI implements ParamConstants { + + LOGO("ui.logo"), + SYSTEM_NAME("ui.system.name"), + THEME_PRIMARY("ui.theme.primary"), + THEME_ACCENT("ui.theme.accent"), + FAVICON("ui.favicon"), + LOGIN_TITLE("ui.login.title"), + LOGIN_IMG("ui.login.img"), + SUPPORT_NAME("ui.support.name"), + SUPPORT_URL("ui.support.url"), + TITLE("ui.title"); + + private String value; + + UI(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + enum MAIL { + SERVER("smtp.server", 1), + PORT("smtp.port", 2), + ACCOUNT("smtp.account", 3), + PASSWORD("smtp.password", 4), + SSL("smtp.ssl", 5), + TLS("smtp.tls", 6); + + private String key; + private Integer value; + + MAIL(String key, Integer value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public Integer getValue() { + return value; + } + } + + enum Log implements ParamConstants { + KEEP_MONTHS("log.keep.months"); + private String value; + + Log(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + enum Registry implements ParamConstants { + URL("registry.url"), + REPO("registry.repo"), + USERNAME("registry.username"), + PASSWORD("registry.password"); + + private String value; + + Registry(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + } + + enum I18n implements ParamConstants { + + LANGUAGE("i18n.language"); + + private String value; + + I18n(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/backend/src/main/java/io/metersphere/config/I18nConfig.java b/backend/src/main/java/io/metersphere/config/I18nConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..be2fd6ab3fa4dd7a5e3b426532eae0dcc16d4021 --- /dev/null +++ b/backend/src/main/java/io/metersphere/config/I18nConfig.java @@ -0,0 +1,21 @@ +package io.metersphere.config; + +import io.metersphere.i18n.I18nManager; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +public class I18nConfig { + + @Bean + @ConditionalOnMissingBean + public I18nManager i18nManager() { + List dirs = new ArrayList<>(); + dirs.add("i18n/"); + return new I18nManager(dirs); + } +} diff --git a/backend/src/main/java/io/metersphere/controller/I18nController.java b/backend/src/main/java/io/metersphere/controller/I18nController.java new file mode 100644 index 0000000000000000000000000000000000000000..71c78dc3be15c1148169b0a8467eef97809d04ba --- /dev/null +++ b/backend/src/main/java/io/metersphere/controller/I18nController.java @@ -0,0 +1,53 @@ +package io.metersphere.controller; + + +import io.metersphere.commons.constants.I18nConstants; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.i18n.Lang; +import io.metersphere.service.UserService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +/** + * Created by liqiang on 2019/4/1. + */ +@RestController +public class I18nController { + + private static final int FOR_EVER = 3600 * 24 * 30 * 12 * 10; //10 years in second + + @Value("${run.mode:release}") + private String runMode; + + @Resource + private UserService userService; + + @GetMapping("lang/change/{lang}") + public void changeLang(@PathVariable String lang, HttpServletResponse response) { + Lang targetLang = Lang.getLangWithoutDefault(lang); + if (targetLang == null) { + response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); + LogUtil.error("Invalid parameter: " + lang); + MSException.throwException("ERROR_LANG_INVALID"); + } + userService.setLanguage(targetLang.getDesc()); + Cookie cookie = new Cookie(I18nConstants.LANG_COOKIE_NAME, targetLang.getDesc()); + cookie.setPath("/"); + cookie.setMaxAge(FOR_EVER); + response.addCookie(cookie); + //重新登录 + if ("release".equals(runMode)) { + Cookie f2cCookie = new Cookie("MS_COOKIE_ID", "deleteMe"); + f2cCookie.setPath("/"); + f2cCookie.setMaxAge(0); + response.addCookie(f2cCookie); + } + } +} diff --git a/backend/src/main/java/io/metersphere/dto/UserDTO.java b/backend/src/main/java/io/metersphere/dto/UserDTO.java index 0caa5b4fbabb4378f9fd609b25f898c61e6914af..bb0835c43cce915e54e4f13d5030f39699a54ce2 100644 --- a/backend/src/main/java/io/metersphere/dto/UserDTO.java +++ b/backend/src/main/java/io/metersphere/dto/UserDTO.java @@ -21,6 +21,8 @@ public class UserDTO { private Long updateTime; + private String language; + private String lastWorkspaceId; private String lastOrganizationId; @@ -103,6 +105,14 @@ public class UserDTO { this.userRoles = userRoles; } + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + public String getLastWorkspaceId() { return lastWorkspaceId; } diff --git a/backend/src/main/java/io/metersphere/i18n/I18nManager.java b/backend/src/main/java/io/metersphere/i18n/I18nManager.java new file mode 100644 index 0000000000000000000000000000000000000000..71802ee58d8a5433138ce1ef49aa3aee057d4652 --- /dev/null +++ b/backend/src/main/java/io/metersphere/i18n/I18nManager.java @@ -0,0 +1,86 @@ +package io.metersphere.i18n; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import io.metersphere.commons.utils.IOUtils; +import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.util.ResourceUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class I18nManager implements ApplicationRunner { + + private static Map> i18nMap = new HashMap<>(); + + private List dirs; + + public I18nManager(List dirs) { + this.dirs = dirs; + } + + public static Map> getI18nMap() { + return i18nMap; + } + + private static Resource[] getResources(String dir, String suffix) throws IOException { + Resource[] result = new Resource[0]; + PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver(); + if (!patternResolver.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + dir).exists()) { + return result; + } + Resource[] resources = patternResolver.getResources(ResourceUtils.CLASSPATH_URL_PREFIX + dir + "*"); + for (Resource resource : resources) { + if (StringUtils.endsWithIgnoreCase(resource.getFilename(), suffix)) { + result = ArrayUtils.add(result, resource); + } + } + return result; + } + + private void init() { + try { + for (Lang lang : Lang.values()) { + Resource[] resources = new Resource[0]; + String i18nKey = lang.getDesc().toLowerCase(); + for (String dir : dirs) { + resources = ArrayUtils.addAll(resources, getResources(dir, i18nKey + ".json")); + } + for (Resource resource : resources) { + if (resource.exists()) { + try (InputStream inputStream = resource.getInputStream()) { + String fileContent = IOUtils.toString(inputStream, Charset.defaultCharset()); + Map langMap = JSON.parseObject(fileContent, new TypeReference>() { + }); + i18nMap.computeIfAbsent(i18nKey, k -> new HashMap<>()); + i18nMap.get(i18nKey).putAll(langMap); + } catch (Exception e) { + e.printStackTrace(); + LogUtil.error("failed to load resource: " + resource.getURI()); + } + } + } + } + } catch (Exception e) { + LogUtil.error("failed to load i18n.", e); + } + } + + /** + * 国际化配置初始化 + */ + @Override + public void run(ApplicationArguments args) { + init(); + } +} diff --git a/backend/src/main/java/io/metersphere/i18n/Lang.java b/backend/src/main/java/io/metersphere/i18n/Lang.java new file mode 100644 index 0000000000000000000000000000000000000000..866befd21e34ab1c695754f09e70fdcc14a170cb --- /dev/null +++ b/backend/src/main/java/io/metersphere/i18n/Lang.java @@ -0,0 +1,48 @@ +package io.metersphere.i18n; + +import org.apache.commons.lang3.StringUtils; + +public enum Lang { + + zh_CN("zh-CN"), zh_TW("zh-TW"), en_US("en-US"); + + private String desc; + + Lang(String desc) { + this.desc = desc; + } + + public String getDesc() { + return this.desc; + } + + public static Lang getLang(String lang) { + Lang result = getLangWithoutDefault(lang); + if (result == null) { + result = zh_CN; + } + return result; + } + + public static Lang getLangWithoutDefault(String lang) { + if (StringUtils.isBlank(lang)) { + return null; + } + for (Lang lang1 : values()) { + if (StringUtils.equalsIgnoreCase(lang1.getDesc(), lang)) { + return lang1; + } + } + if (StringUtils.startsWithIgnoreCase(lang, "zh-CN")) { + return zh_CN; + } + if (StringUtils.startsWithIgnoreCase(lang, "zh-HK") || StringUtils.startsWithIgnoreCase(lang, "zh-TW")) { + return zh_TW; + } + if (StringUtils.startsWithIgnoreCase(lang, "en")) { + return en_US; + } + return null; + } + +} diff --git a/backend/src/main/java/io/metersphere/i18n/Translator.java b/backend/src/main/java/io/metersphere/i18n/Translator.java new file mode 100644 index 0000000000000000000000000000000000000000..0e87901eca4dd290b9cf9df9833ab06df989d46d --- /dev/null +++ b/backend/src/main/java/io/metersphere/i18n/Translator.java @@ -0,0 +1,274 @@ +package io.metersphere.i18n; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.JavaBeanSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeConfig; +import io.metersphere.commons.constants.I18nConstants; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.service.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.service.SystemParameterService; +import io.metersphere.user.SessionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.collections4.map.PassiveExpiringMap; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.text.StringSubstitutor; +import org.springframework.http.HttpHeaders; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.lang.reflect.Array; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +public class Translator { + + public static final String PREFIX = "$[{"; + public static final String SUFFIX = "}]"; + private static final String JSON_SYMBOL = "\":"; + + private static final HashSet IGNORE_KEYS = new HashSet<>(Arrays.asList("id", "password", "passwd")); + + private static Map langCache4Thread = Collections.synchronizedMap(new PassiveExpiringMap(1, TimeUnit.MINUTES)); + + public static String getLangDes() { + return getLang().getDesc(); + } + + public static Lang getLang() { + HttpServletRequest request = getRequest(); + return getLang(request); + } + + public static Object gets(Object keys) { + return gets(getLang(), keys); + } + + public static Object gets(Lang lang, Object keys) { + Map context = I18nManager.getI18nMap().get(lang.getDesc().toLowerCase()); + return translateObject(keys, context); + } + + // 单Key翻译 + public static String get(String key) { + return get(getLang(), key); + } + + public static String get(Lang lang, String key) { + if (StringUtils.isBlank(key)) { + return StringUtils.EMPTY; + } + return translateKey(key, I18nManager.getI18nMap().get(lang.getDesc().toLowerCase())); + } + + public static String toI18nKey(String key) { + return String.format("%s%s%s", PREFIX, key, SUFFIX); + } + + private static HttpServletRequest getRequest() { + try { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } catch (NullPointerException npe) { + return null; + } + } + + private static Lang getLang(HttpServletRequest request) { + String preferLang = Lang.zh_CN.getDesc(); + + try { + + if (request != null) { + Object sessionLang = request.getSession(true).getAttribute(I18nConstants.LANG_COOKIE_NAME); + if (sessionLang != null && StringUtils.isNotBlank(sessionLang.toString())) { + return Lang.getLang(sessionLang.toString()); + } + preferLang = getSystemParameterLanguage(preferLang); + if (StringUtils.isNotBlank(request.getHeader(HttpHeaders.ACCEPT_LANGUAGE))) { + String preferLangWithComma = StringUtils.substringBefore(request.getHeader(HttpHeaders.ACCEPT_LANGUAGE), ";"); + String acceptLanguage = StringUtils.replace(StringUtils.substringBefore(preferLangWithComma, ","), "-", "_"); + if (Lang.getLangWithoutDefault(acceptLanguage) != null) { + preferLang = acceptLanguage; + } + } + if (request.getCookies() != null && request.getCookies().length > 0) { + for (Cookie cookie : request.getCookies()) { + if (StringUtils.equalsIgnoreCase(cookie.getName(), I18nConstants.LANG_COOKIE_NAME)) { + preferLang = cookie.getValue(); + } + } + } + if (SessionUtils.getUser() != null && StringUtils.isNotBlank(SessionUtils.getUser().getLanguage())) { + preferLang = SessionUtils.getUser().getLanguage(); + } + request.getSession(true).setAttribute(I18nConstants.LANG_COOKIE_NAME, preferLang); + } else { + preferLang = getSystemParameterLanguage(preferLang); + } + + } catch (Exception e) { + LogUtil.error("Fail to getLang.", e); + } + + return Lang.getLang(preferLang); + } + + private static String getSystemParameterLanguage(String defaultLang) { + String result = defaultLang; + try { + String cachedLang = langCache4Thread.get(I18nConstants.LANG_COOKIE_NAME); + if (StringUtils.isNotBlank(cachedLang)) { + return cachedLang; + } + String systemLanguage = Objects.requireNonNull(CommonBeanFactory.getBean(SystemParameterService.class)).getSystemLanguage(); + if (StringUtils.isNotBlank(systemLanguage)) { + result = systemLanguage; + } + langCache4Thread.put(I18nConstants.LANG_COOKIE_NAME, result); + } catch (Exception e) { + LogUtil.error(e); + } + return result; + + } + + private static Object translateObject(Object javaObject, final Map context) { + if (MapUtils.isEmpty(context)) { + return javaObject; + } + if (javaObject == null) { + return null; + } + + try { + if (javaObject instanceof String) { + String rawString = javaObject.toString(); + if (StringUtils.contains(rawString, JSON_SYMBOL)) { + try { + Object jsonObject = JSON.parse(rawString); + Object a = translateObject(jsonObject, context); + return JSON.toJSONString(a); + } catch (Exception e) { + LogUtil.warn("Failed to translate object " + rawString + ". Error: " + ExceptionUtils.getStackTrace(e)); + return translateRawString(null, rawString, context); + } + + } else { + return translateRawString(null, rawString, context); + } + } + + if (javaObject instanceof Map) { + Map map = (Map) javaObject; + + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() != null) { + if (entry.getValue() instanceof String) { + if (StringUtils.contains(entry.getValue().toString(), JSON_SYMBOL)) { + map.put(entry.getKey(), translateObject(entry.getValue(), context)); + } else { + map.put(entry.getKey(), translateRawString(entry.getKey().toString(), entry.getValue().toString(), context)); + } + } else { + translateObject(entry.getValue(), context); + } + } + } + + } + + if (javaObject instanceof Collection) { + Collection collection = (Collection) javaObject; + for (Object item : collection) { + translateObject(item, context); + } + } + + if (javaObject.getClass().isArray()) { + for (int i = 0; i < Array.getLength(javaObject); ++i) { + Object item = Array.get(javaObject, i); + Array.set(javaObject, i, translateObject(item, context)); + } + } + + ObjectSerializer serializer = SerializeConfig.globalInstance.getObjectWriter(javaObject.getClass()); + if (serializer instanceof JavaBeanSerializer) { + JavaBeanSerializer javaBeanSerializer = (JavaBeanSerializer) serializer; + + try { + Map values = javaBeanSerializer.getFieldValuesMap(javaObject); + for (Map.Entry entry : values.entrySet()) { + if (entry.getValue() != null) { + if (entry.getValue() instanceof String) { + if (StringUtils.contains(entry.getValue().toString(), JSON_SYMBOL)) { + BeanUtils.setFieldValueByName(javaObject, entry.getKey(), translateObject(entry.getValue(), context), String.class); + } else { + BeanUtils.setFieldValueByName(javaObject, entry.getKey(), translateRawString(entry.getKey(), entry.getValue().toString(), context), String.class); + } + } else { + translateObject(entry.getValue(), context); + } + } + } + } catch (Exception e) { + MSException.throwException(e); + } + } + + return javaObject; + } catch (StackOverflowError stackOverflowError) { + try { + return JSON.parseObject(translateRawString(null, JSON.toJSONString(javaObject), context).toString(), javaObject.getClass()); + } catch (Exception e) { + LogUtil.error("Failed to translate object " + javaObject.toString(), e); + return javaObject; + } + } + } + + private static Object translateRawString(String key, String rawString, Map context) { + if (StringUtils.isBlank(rawString)) { + return rawString; + } + for (String ignoreKey : IGNORE_KEYS) { + if (StringUtils.containsIgnoreCase(key, ignoreKey)) { + return rawString; + } + } + if (StringUtils.contains(rawString, PREFIX)) { + rawString = new StringSubstitutor(context, PREFIX, SUFFIX).replace(rawString); + if (StringUtils.contains(rawString, PREFIX)) { + String[] unTrans = StringUtils.substringsBetween(rawString, PREFIX, SUFFIX); + if (unTrans != null) { + for (String unTran : unTrans) { + rawString = StringUtils.replace(rawString, PREFIX + unTran + SUFFIX, unTran); + } + } + } + } + if (key != null) { + String desc = context.get(rawString); + if (StringUtils.isNotBlank(desc)) { + return desc; + } + } + return rawString; + } + + private static String translateKey(String key, Map context) { + if (MapUtils.isEmpty(context)) { + return key; + } + String desc = context.get(StringUtils.replace(StringUtils.replace(key, PREFIX, StringUtils.EMPTY), SUFFIX, StringUtils.EMPTY)); + if (StringUtils.isNotBlank(desc)) { + return desc; + } + return key; + } +} diff --git a/backend/src/main/java/io/metersphere/service/CommonBeanFactory.java b/backend/src/main/java/io/metersphere/service/CommonBeanFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3cf1aa80625c027b4dc5ba47ac427cff446425ec --- /dev/null +++ b/backend/src/main/java/io/metersphere/service/CommonBeanFactory.java @@ -0,0 +1,26 @@ +package io.metersphere.service; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class CommonBeanFactory implements ApplicationContextAware { + private static ApplicationContext context; + + public CommonBeanFactory() { + } + + public void setApplicationContext(ApplicationContext ctx) throws BeansException { + context = ctx; + } + + public static Object getBean(String beanName) { + return context != null && !StringUtils.isBlank(beanName) ? context.getBean(beanName) : null; + } + + public static T getBean(Class className) { + return context != null && className != null ? context.getBean(className) : null; + } +} + diff --git a/backend/src/main/java/io/metersphere/service/SystemParameterService.java b/backend/src/main/java/io/metersphere/service/SystemParameterService.java new file mode 100644 index 0000000000000000000000000000000000000000..4e1f0e49c9504ce59ddf6aadea699f607814dffb --- /dev/null +++ b/backend/src/main/java/io/metersphere/service/SystemParameterService.java @@ -0,0 +1,34 @@ +package io.metersphere.service; + +import io.metersphere.base.domain.SystemParameter; +import io.metersphere.base.domain.SystemParameterExample; +import io.metersphere.base.mapper.SystemParameterMapper; +import io.metersphere.commons.constants.ParamConstants; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +import javax.annotation.Resource; + +@Service +public class SystemParameterService { + + @Resource + private SystemParameterMapper systemParameterMapper; + + public String getSystemLanguage() { + String result = StringUtils.EMPTY; + SystemParameterExample example = new SystemParameterExample(); + example.createCriteria().andParamKeyEqualTo(ParamConstants.I18n.LANGUAGE.getValue()); + List list = systemParameterMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(list)) { + String value = list.get(0).getParamValue(); + if (StringUtils.isNotBlank(value)) { + result = value; + } + } + return result; + } +} diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index 1e607fb6e523f27a3f0531810aa851f02d19db1b..9c68f461898ba1f7317206e58799dc2b18e844bb 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -337,4 +337,14 @@ public class UserService { public List getBesideOrgMemberList(String orgId) { return extUserRoleMapper.getBesideOrgMemberList(orgId); } + + public void setLanguage(String lang) { + if (SessionUtils.getUser() != null) { + User user = new User(); + user.setId(SessionUtils.getUser().getId()); + user.setLanguage(lang); + updateUser(user); + SessionUtils.getUser().setLanguage(lang); + } + } } diff --git a/backend/src/main/resources/i18n/zh-CN.json b/backend/src/main/resources/i18n/zh-CN.json new file mode 100644 index 0000000000000000000000000000000000000000..4b7a0a4de80037f94a7361ce5b5d99c6a4082e50 --- /dev/null +++ b/backend/src/main/resources/i18n/zh-CN.json @@ -0,0 +1,3 @@ +{ + "i18n_test": "测试" +} \ No newline at end of file