当前位置: 首页 > news >正文

3.9 Spring Boot国际化:动态语言切换与数据库存储方案

在Spring Boot应用中实现国际化(i18n)并支持动态语言切换与数据库存储,可以通过以下方案实现灵活的多语言管理:


一、基础配置(基于.properties文件)

1. 添加依赖
 

xml

<!-- Spring Boot基础依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 创建消息文件
src/main/resources/
├─ messages.properties       # 默认语言(英文)
├─ messages_zh_CN.properties # 简体中文
└─ messages_ja_JP.properties # 日文
3. 配置国际化参数(application.yml)
 

yaml

spring:
  messages:
    basename: messages
    encoding: UTF-8

二、动态语言切换

1. 配置LocaleResolver
 

java

@Configuration
public class LocaleConfig {

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        resolver.setDefaultLocale(Locale.ENGLISH); // 设置默认语言
        return resolver;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang"); // 通过URL参数切换语言
        return interceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}
2. 前端切换语言
 

html

<!-- 语言切换链接 -->
<a href="?lang=en">English</a>
<a href="?lang=zh_CN">中文</a>
<a href="?lang=ja_JP">日本語</a>

三、数据库存储方案

1. 数据库表设计
 

sql

CREATE TABLE sys_language (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    lang_code VARCHAR(10) NOT NULL UNIQUE,  -- 语言代码(如zh_CN)
    lang_name VARCHAR(50) NOT NULL          -- 语言名称
);

CREATE TABLE sys_message (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    message_key VARCHAR(255) NOT NULL,      -- 消息键(如login.title)
    content TEXT NOT NULL,                  -- 消息内容
    lang_code VARCHAR(10) NOT NULL,         -- 关联语言代码
    UNIQUE KEY (message_key, lang_code)
);
2. 自定义MessageSource
 

java

public class DatabaseMessageSource extends AbstractMessageSource {

    @Autowired
    private MessageRepository messageRepository;

    private final Map<String, Map<Locale, String>> messageCache = new ConcurrentHashMap<>();

    @Override
    protected MessageFormat resolveCode(String key, Locale locale) {
        String content = getMessageFromDB(key, locale);
        return new MessageFormat(content, locale);
    }

    private String getMessageFromDB(String key, Locale locale) {
        // 1. 优先从缓存读取
        if (messageCache.containsKey(key) && messageCache.get(key).containsKey(locale)) {
            return messageCache.get(key).get(locale);
        }

        // 2. 查询数据库
        String langCode = locale.toLanguageTag().replace("-", "_");
        String content = messageRepository.findByKeyAndLang(key, langCode)
                .orElseGet(() -> getParentMessage(key, locale));

        // 3. 更新缓存
        messageCache.computeIfAbsent(key, k -> new ConcurrentHashMap<>())
                    .put(locale, content);

        return content;
    }

    // 清理缓存方法(用于语言更新时调用)
    public void clearCache() {
        messageCache.clear();
    }
}
3. 注册自定义MessageSource
 

java

@Configuration
public class MessageSourceConfig {

    @Bean
    public MessageSource messageSource() {
        DatabaseMessageSource messageSource = new DatabaseMessageSource();
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

四、动态语言管理接口

1. 语言管理API
 

java

@RestController
@RequestMapping("/api/language")
public class LanguageController {

    @Autowired
    private MessageRepository messageRepository;
    
    @Autowired
    private DatabaseMessageSource messageSource;

    // 添加语言项
    @PostMapping
    public ResponseEntity<?> addMessage(@RequestBody MessageDTO dto) {
        SysMessage message = new SysMessage();
        message.setMessageKey(dto.getKey());
        message.setContent(dto.getContent());
        message.setLangCode(dto.getLangCode());
        messageRepository.save(message);
        
        messageSource.clearCache(); // 清理缓存
        return ResponseEntity.ok().build();
    }

    // 更新语言项
    @PutMapping("/{id}")
    public ResponseEntity<?> updateMessage(@PathVariable Long id, @RequestBody MessageDTO dto) {
        SysMessage message = messageRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Message not found"));
        message.setContent(dto.getContent());
        messageRepository.save(message);
        
        messageSource.clearCache();
        return ResponseEntity.ok().build();
    }
}

五、高级功能扩展

1. 消息缓存优化
 

java

// 使用Spring Cache + Redis缓存
@Cacheable(value = "messages", key = "#key + '_' + #locale")
public String getMessage(String key, Locale locale) {
    // 数据库查询逻辑
}

// 更新时清理缓存
@CacheEvict(value = "messages", allEntries = true)
public void updateMessage() {
    // 更新逻辑
}
2. 默认语言回退机制
 

java

private String getParentMessage(String key, Locale locale) {
    // 尝试获取父语言(如zh_CN -> zh)
    if (locale.getCountry().length() > 0) {
        Locale parentLocale = new Locale(locale.getLanguage());
        String parentMessage = getMessageFromDB(key, parentLocale);
        if (!parentMessage.equals(key)) {
            return parentMessage;
        }
    }
    // 最终返回默认语言
    return getMessageFromDB(key, Locale.ENGLISH);
}
3. 实时热更新监听
 

java

@EventListener
public void handleMessageUpdateEvent(MessageUpdateEvent event) {
    messageSource.clearCache(); // 清理缓存
    log.info("Language cache cleared due to update");
}

六、使用示例

1. 在Thymeleaf中调用
 

html

<h1 th:text="#{login.title}"></h1>
<p th:text="#{login.welcome(${user.name})}"></p>
2. 在Controller中获取消息
 

java

@Autowired
private MessageSource messageSource;

@GetMapping("/greeting")
public String greeting(Locale locale) {
    return messageSource.getMessage("greeting.message", null, locale);
}

七、验证测试

1. 数据库初始化数据
 

sql

INSERT INTO sys_language (lang_code, lang_name) VALUES 
('en', 'English'),
('zh_CN', '简体中文'),
('ja_JP', '日本語');

INSERT INTO sys_message (message_key, content, lang_code) VALUES 
('login.title', 'Login', 'en'),
('login.title', '登录', 'zh_CN'),
('login.title', 'ログイン', 'ja_JP');
2. 发送测试请求
 

bash

# 英文
curl http://localhost:8080/api/greeting?lang=en

# 中文
curl http://localhost:8080/api/greeting?lang=zh_CN

八、安全增强建议

  1. 接口权限控制:语言管理接口添加@PreAuthorize("hasRole('ADMIN')")
  2. 输入校验:对语言代码进行正则校验(如@Pattern(regexp = "^[a-z]{2}_[A-Z]{2}$")
  3. 防SQL注入:使用JPA参数化查询
  4. 审计日志:记录语言项修改操作

通过此方案,可以实现以下核心优势:

  • 动态更新:无需重启即可更新语言项
  • 灵活存储:支持数据库管理多语言内容
  • 高效缓存:结合缓存机制降低数据库压力
  • 扩展性强:可轻松集成第三方翻译服务(如Google Translate API)

相关文章:

  • LabVIEW 与 PLC 通讯的常见方式
  • 大数据学习栈记——HBase安装
  • HashMap学习总结——JDK17
  • PHP 应用TP 框架路由访问对象操作内置过滤绕过核心漏洞
  • STM32-ARM
  • 深入理解 Spring 框架中的 IOC 容器
  • 智能AI优化SEO关键词实战
  • LLM(6):理解词嵌入
  • 大白话详细解读React框架的纯函数和高阶函数
  • 京准电钟:NTP网络时间服务器让城市更智慧更精准
  • 快速入手-基于Django的Form和ModelForm操作(七)
  • C++ 介绍STL底层一些数据结构
  • 数据结构(排序(上)):冒泡、选择、插入
  • MySQL 创建用户,建库,建表
  • 红宝书第八讲:箭头函数与高阶函数:厨房工具与智能菜谱的对比
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能示例6,TableView15_06自定义导出文件名示例
  • ZYNQ的cache原理与一致性操作
  • 跟着StatQuest学知识06-CNN进行图像分类
  • 为容器指定固定IP地址
  • 06_数组
  • 4月证券私募产品备案量创23个月新高,股票策略占比超六成
  • 溢价26.3%!保利置业42.4亿元竞得上海杨浦宅地,楼板价80199元/平方米
  • 数说母亲节|妈妈的妈妈带娃比例提升,托举效果如何?
  • 玉渊谭天丨一艘航母看中国稀土出口管制为何有效
  • 105岁八路军老战士、抗美援朝老战士谭克煜逝世
  • 新华每日电讯:给“男性妇科病论文”开一剂复方药