From bc70318571c93f2eabcb11d9db580ea0ed7e6b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=97=AD=E6=98=8E=5C73939?= <739390650@QQ.COM> Date: Wed, 24 Sep 2025 16:07:05 +0800 Subject: [PATCH] =?UTF-8?q?add:=E5=9B=BD=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/BadRequestException.java | 1 - .../java/org/nl/common/utils/I18nUtil.java | 20 ++ .../config/language/I18nManagerService.java | 178 ++++++++++++++++++ .../nl/config/language/I18nProperties.java | 53 ++++++ .../nl/config/language/LangContextHolder.java | 29 +++ .../nl/config/language/LangInterceptor.java | 51 +++++ .../org/nl/config/language/WebConfig.java | 21 +++ .../src/main/resources/config/application.yml | 14 ++ .../main/resources/language/i18n/lang_en.js | 13 ++ .../main/resources/language/i18n/lang_in.js | 13 ++ .../main/resources/language/i18n/lang_ja.js | 13 ++ .../main/resources/language/i18n/lang_zh.js | 13 ++ 12 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/common/utils/I18nUtil.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nManagerService.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nProperties.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangContextHolder.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangInterceptor.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/config/language/WebConfig.java create mode 100644 nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_en.js create mode 100644 nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_in.js create mode 100644 nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_ja.js create mode 100644 nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_zh.js diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/common/exception/BadRequestException.java b/nladmin-system/nlsso-server/src/main/java/org/nl/common/exception/BadRequestException.java index 627bdef..b27464b 100644 --- a/nladmin-system/nlsso-server/src/main/java/org/nl/common/exception/BadRequestException.java +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/common/exception/BadRequestException.java @@ -17,7 +17,6 @@ package org.nl.common.exception; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.nl.common.logging.annotation.Log; import org.springframework.http.HttpStatus; import static org.springframework.http.HttpStatus.BAD_REQUEST; diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/common/utils/I18nUtil.java b/nladmin-system/nlsso-server/src/main/java/org/nl/common/utils/I18nUtil.java new file mode 100644 index 0000000..2b198d9 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/common/utils/I18nUtil.java @@ -0,0 +1,20 @@ +package org.nl.common.utils; + +import org.apache.commons.lang3.StringUtils; +import org.nl.config.SpringContextHolder; +import org.nl.config.language.I18nManagerService; + +public class I18nUtil { + private static I18nManagerService i18nManagerService = SpringContextHolder.getBean(I18nManagerService.class); + + public static String msg(String key,String...args) { + if(StringUtils.isBlank(key)){ + return ""; + } + String msg = i18nManagerService.getCurrentMessage(key); + for(String arg: args){ + msg += arg; + } + return msg; + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nManagerService.java b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nManagerService.java new file mode 100644 index 0000000..bd1fd44 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nManagerService.java @@ -0,0 +1,178 @@ +package org.nl.config.language; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; + +@Service +public class I18nManagerService implements InitializingBean, DisposableBean { + private final I18nProperties properties; + private final Map langCache = new HashMap<>(); + private final Map lastModifiedTimes = new HashMap<>(); + private final Map filePaths = new HashMap<>(); + private ScheduledExecutorService scheduler; + // 默认语言(从配置中获取) + private String defaultLang; + + public I18nManagerService(I18nProperties properties) { + this.properties = properties; + } + + @Override + public void afterPropertiesSet() throws Exception { + // 初始化默认语言 + if (!properties.getSupportedLanguages().isEmpty()) { + defaultLang = properties.getSupportedLanguages().get(0); + } + + // 加载所有语言文件 + for (String lang : properties.getSupportedLanguages()) { + loadLangFile(lang); + } + + // startRefreshScheduler(); + } + + // 新增:不带语言参数的方法,从上下文获取当前语言 + public JSONObject getCurrentLangConfig() { + String lang = LangContextHolder.getLangOrDefault(defaultLang); + return getLangConfig(lang); + } + + // 新增:不带语言参数的方法,从上下文获取当前语言 + public String getCurrentMessage(String key) { + String lang = LangContextHolder.getLangOrDefault(defaultLang); + return getMessage(lang, key); + } + + // 以下为原有方法(保持不变) + private void loadLangFile(String lang) throws IOException { + String fileName = lang + ".js"; + String fullPath = properties.getLocation() + fileName; + Resource resource = getResource(fullPath); + + if (!resource.exists() && properties.isFallbackToClasspath()) { + fullPath = "language/i18n/lang_" + fileName; + resource = new ClassPathResource(fullPath); + } + + if (!resource.exists()) { + throw new IOException("Language file not found: " + fullPath); + } + + String content = new String(Files.readAllBytes(Paths.get(resource.getURI())), + StandardCharsets.UTF_8); + JSONObject config = parseJsConfig(content); + + langCache.put(lang, config); + lastModifiedTimes.put(lang, getLastModifiedTime(resource)); + filePaths.put(lang, fullPath); + } + + private Resource getResource(String path) { + if (path.startsWith("file:")) { + return new FileSystemResource(path.substring("file:".length())); + } else if (path.startsWith("classpath:")) { + return new ClassPathResource(path.substring("classpath:".length())); + } else { + return new FileSystemResource(path); + } + } + + private long getLastModifiedTime(Resource resource) throws IOException { + if (resource instanceof FileSystemResource) { + return ((FileSystemResource) resource).getFile().lastModified(); + } else if (resource instanceof ClassPathResource) { + return System.currentTimeMillis(); + } + return 0; + } + + private JSONObject parseJsConfig(String jsContent) { + String jsonStr = jsContent.replace("var config = ","").trim(); + return JSON.parseObject(jsonStr); + } + + public JSONObject getLangConfig(String lang) { + if (!langCache.containsKey(lang)) { + lang = defaultLang; + } + return langCache.get(lang); + } + + public String getMessage(String lang, String key) { + JSONObject config = getLangConfig(lang); + if (config == null) { + return null; + } + + String[] keyParts = key.split("\\."); + JSONObject current = config; + + for (int i = 0; i < keyParts.length; i++) { + if (i == keyParts.length - 1) { + return StringUtils.isEmpty(current.getString(keyParts[i])) ? key : current.getString(keyParts[i]); + } + current = current.getJSONObject(keyParts[i]); + if (current == null) { + return key; + } + } + return key; + } + + // private void startRefreshScheduler() { + // scheduler = Executors.newSingleThreadScheduledExecutor(); + // scheduler.scheduleAtFixedRate(() -> { + // try { + // checkAndRefreshFiles(); + // } catch (Exception e) { + // System.err.println("Error checking for language file updates: " + e.getMessage()); + // } + // }, 0, properties.getRefreshInterval(), TimeUnit.SECONDS); + // } + + // private void checkAndRefreshFiles() throws IOException { + // for (String lang : properties.getSupportedLanguages()) { + // String fullPath = filePaths.get(lang); + // if (StringUtils.isEmpty(fullPath)) { + // continue; + // } + + // Resource resource = getResource(fullPath); + // if (!resource.exists()) { + // continue; + // } + + // long currentLastModified = getLastModifiedTime(resource); + // if (currentLastModified > lastModifiedTimes.getOrDefault(lang, 0L)) { + // loadLangFile(lang); + // System.out.println("Language file updated: " + lang + " (" + fullPath + ")"); + // } + // } + // } + + @Override + public void destroy() { + if (scheduler != null) { + scheduler.shutdown(); + } + langCache.clear(); + lastModifiedTimes.clear(); + filePaths.clear(); + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nProperties.java b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nProperties.java new file mode 100644 index 0000000..192147d --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/I18nProperties.java @@ -0,0 +1,53 @@ +package org.nl.config.language; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; +import java.util.List; + +@Configuration +@ConfigurationProperties(prefix = "i18n") +public class I18nProperties { + // 语言文件存放路径,可以是外部路径或classpath + private String location = "D:\\i18n\\"; + // 热更新检查间隔(秒) + private long refreshInterval = 60; + // 支持的语言列表 + private List supportedLanguages = Arrays.asList("en", "zh", "ja"); + // 当外部文件不存在时,是否回退到classpath中的默认文件 + private boolean fallbackToClasspath = true; + + // getter和setter方法 + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public long getRefreshInterval() { + return refreshInterval; + } + + public void setRefreshInterval(long refreshInterval) { + this.refreshInterval = refreshInterval; + } + + public List getSupportedLanguages() { + return supportedLanguages; + } + + public void setSupportedLanguages(List supportedLanguages) { + this.supportedLanguages = supportedLanguages; + } + + public boolean isFallbackToClasspath() { + return fallbackToClasspath; + } + + public void setFallbackToClasspath(boolean fallbackToClasspath) { + this.fallbackToClasspath = fallbackToClasspath; + } +} \ No newline at end of file diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangContextHolder.java b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangContextHolder.java new file mode 100644 index 0000000..2cc188b --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangContextHolder.java @@ -0,0 +1,29 @@ +package org.nl.config.language; + +import org.springframework.util.StringUtils; + +public class LangContextHolder { + // 线程本地变量,存储当前线程的语言标识 + private static final ThreadLocal LANG_HOLDER = new ThreadLocal<>(); + + // 设置当前线程的语言 + public static void setLang(String lang) { + LANG_HOLDER.set(lang); + } + + // 获取当前线程的语言 + public static String getLang() { + return LANG_HOLDER.get(); + } + + // 清除当前线程的语言设置(防止内存泄漏) + public static void clear() { + LANG_HOLDER.remove(); + } + + // 获取当前语言,如果未设置则返回默认语言 + public static String getLangOrDefault(String defaultLang) { + String lang = getLang(); + return StringUtils.hasText(lang) ? lang : defaultLang; + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangInterceptor.java b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangInterceptor.java new file mode 100644 index 0000000..e801a3c --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/LangInterceptor.java @@ -0,0 +1,51 @@ +package org.nl.config.language; + + +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +public class LangInterceptor implements HandlerInterceptor { + + // 支持的语言列表(实际项目中可从配置获取) + private final I18nProperties properties; + + public LangInterceptor(I18nProperties properties) { + this.properties = properties; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 1. 从请求参数获取语言(如 ?lang=zh) + String lang = request.getParameter("lang"); + + // 2. 如果参数不存在,可从Header获取(如 Accept-Language) + if (lang == null || lang.isEmpty()) { + lang = request.getHeader("Accept-Language"); + // 简单处理,只取前两位(如 zh-CN -> zh) + if (lang != null && lang.length() >= 2) { + lang = lang.substring(0, 2); + } + } + + // 3. 验证语言是否在支持的列表中 + if (lang != null && properties.getSupportedLanguages().contains(lang)) { + LangContextHolder.setLang(lang); + } else { + // 不支持的语言使用默认语言 + LangContextHolder.setLang(properties.getSupportedLanguages().get(0)); + } + + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, Exception ex) { + // 清除线程变量,防止内存泄漏 + LangContextHolder.clear(); + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/WebConfig.java b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/WebConfig.java new file mode 100644 index 0000000..1d2a0c0 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/config/language/WebConfig.java @@ -0,0 +1,21 @@ +package org.nl.config.language; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final LangInterceptor langInterceptor; + + public WebConfig(LangInterceptor langInterceptor) { + this.langInterceptor = langInterceptor; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 对所有请求生效 + registry.addInterceptor(langInterceptor).addPathPatterns("/**"); + } +} diff --git a/nladmin-system/nlsso-server/src/main/resources/config/application.yml b/nladmin-system/nlsso-server/src/main/resources/config/application.yml index 1d52e5e..950af34 100644 --- a/nladmin-system/nlsso-server/src/main/resources/config/application.yml +++ b/nladmin-system/nlsso-server/src/main/resources/config/application.yml @@ -36,6 +36,20 @@ login: # 是否限制单用户登录 single-login: false +i18n: + # 语言文件存放路径(支持外部路径和classpath) + location: D:/i18n/lang_ + # 热更新检查间隔(秒) + refresh-interval: 30 + # 支持的语言列表 + supported-languages: + - zh + - en + - in + - ja + # 当外部文件不存在时,是否回退到classpath中的默认文件 + fallback-to-classpath: true + #密码加密传输,前端公钥加密,后端私钥解密 rsa: private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== diff --git a/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_en.js b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_en.js new file mode 100644 index 0000000..5193744 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_en.js @@ -0,0 +1,13 @@ +var config = { + "lang": "English222", + "platform": { + "title": "NOBLELIFT Platform", + "tip1": "The user name cannot be empty", + "tip2": "The password cannot be empty", + "tip3": "当前语言,111111英语" + }, + "common": { + "home": "Dashboard", + "Layout_setting": "Layout Setting" + } +} \ No newline at end of file diff --git a/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_in.js b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_in.js new file mode 100644 index 0000000..f567aaa --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_in.js @@ -0,0 +1,13 @@ +var config = { + "lang": "English222", + "platform": { + "title": "NOBLELIFT Platform", + "tip1": "The user name cannot be empty", + "tip2": "The password cannot be empty", + "tip3": "当前语言,1111印度尼西3333333亚语" + }, + "common": { + "home": "Dashboard", + "Layout_setting": "Layout Setting" + } +} \ No newline at end of file diff --git a/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_ja.js b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_ja.js new file mode 100644 index 0000000..2b65b37 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_ja.js @@ -0,0 +1,13 @@ +var config = { + "lang": "English222", + "platform": { + "title": "NOBLELIFT Platform", + "tip1": "The user name cannot be empty", + "tip2": "The password cannot be empty", + "tip3": "The verification code cannot be empty" + }, + "common": { + "home": "Dashboard", + "Layout_setting": "Layout Setting" + } +} \ No newline at end of file diff --git a/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_zh.js b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_zh.js new file mode 100644 index 0000000..6abfb7a --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/resources/language/i18n/lang_zh.js @@ -0,0 +1,13 @@ +var config = { + "lang": "English222", + "platform": { + "title": "NOBLELIFT Platform", + "tip1": "The user name cannot be empty", + "tip2": "The password cannot be empty", + "tip3": "当前语言中文" + }, + "common": { + "home": "Dashboard", + "Layout_setting": "Layout Setting" + } +} \ No newline at end of file