若依框架实际国际化前后端统一解决方案

一、整体交互流程图

复制代码
前端切换语言 → 存储本地 → 后续请求携带语言标识 → 后端解析标识 → 返回对应语言资源

二、前端详细步骤

1. 语言切换组件实现
vue 复制代码
<!-- src/components/LangSelect.vue -->
<template>
  <el-select 
    v-model="currentLang" 
    @change="handleLanguageChange"
    size="small"
    style="width: 120px"
  >
    <el-option
      v-for="item in languages"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>
</template>

<script setup>
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import { setLanguage } from '@/api/system'

const { locale } = useI18n()
const currentLang = ref(localStorage.getItem('lang') || 'zh-CN')

const languages = [
  { label: '中文', value: 'zh-CN' },
  { label: 'English', value: 'en-US' }
]

// 切换语言逻辑
const handleLanguageChange = async (lang) => {
  try {
    // 1. 调用后端接口同步语言偏好(可选)
    await setLanguage(lang) 
    
    // 2. 更新前端i18n实例
    locale.value = lang
    
    // 3. 持久化存储
    localStorage.setItem('lang', lang)
    
    // 4. 刷新页面使路由meta生效
    window.location.reload()
    
    ElMessage.success(lang === 'zh-CN' ? '语言切换成功' : 'Language changed')
  } catch (err) {
    console.error('语言切换失败', err)
  }
}
</script>
2. 请求拦截器配置
javascript 复制代码
// src/utils/request.js
import axios from 'axios'
import { getToken } from '@/utils/auth'
import i18n from '@/lang'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 携带当前语言标识
    config.headers['Accept-Language'] = i18n.global.locale.value
    
    // 如果已登录,携带token
    if (getToken()) {
      config.headers['Authorization'] = 'Bearer ' + getToken()
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

三、后端详细步骤(Spring Boot)

1. 语言标识处理策略

优先级顺序

  1. 请求头 Accept-Language(前端主动传递)
  2. Cookie lang(可选持久化方案)
  3. 默认 zh-CN
2. 国际化配置类
java 复制代码
@Configuration
public class I18nConfig {

    // 区域解析器
    @Bean
    public LocaleResolver localeResolver() {
        return new SmartLocaleResolver();
    }

    // 消息源配置
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = 
            new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:i18n/messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(3600);
        return messageSource;
    }
}

// 自定义区域解析器
public class SmartLocaleResolver implements LocaleResolver {
    
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        // 1. 检查请求头
        String headerLang = request.getHeader("Accept-Language");
        if (StringUtils.hasText(headerLang)) {
            return Locale.forLanguageTag(headerLang);
        }
        
        // 2. 检查Cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("lang".equals(cookie.getName())) {
                    return Locale.forLanguageTag(cookie.getValue());
                }
            }
        }
        
        // 3. 默认中文
        return Locale.CHINA;
    }

    @Override
    public void setLocale(HttpServletRequest request, 
                         HttpServletResponse response, 
                         Locale locale) {
        throw new UnsupportedOperationException();
    }
}
3. 统一响应处理
java 复制代码
// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Autowired
    private MessageSource messageSource;

    // 处理业务异常
    @ExceptionHandler(ServiceException.class)
    public AjaxResult handleServiceException(ServiceException e) {
        String message = messageSource.getMessage(e.getCode(), 
                                                   e.getArgs(), 
                                                   LocaleContextHolder.getLocale());
        return AjaxResult.error(e.getCode(), message);
    }

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public AjaxResult handleValidException(MethodArgumentNotValidException e) {
        String code = "err.param_invalid";
        String defaultMsg = e.getBindingResult().getFieldError().getDefaultMessage();
        String message = messageSource.getMessage(code, 
                                                 null, 
                                                 defaultMsg, // 默认消息作为fallback
                                                 LocaleContextHolder.getLocale());
        return AjaxResult.error(code, message);
    }
}

四、前后端交互协议

1. 语言切换API(可选)
java 复制代码
// 后端接口
@PostMapping("/system/user/language")
public AjaxResult setLanguage(@RequestParam String lang) {
    // 1. 验证语言合法性
    if (!Arrays.asList("zh-CN", "en-US").contains(lang)) {
        throw new ServiceException("err.invalid_language");
    }
    
    // 2. 更新用户语言偏好(需用户登录)
    LoginUser loginUser = getLoginUser();
    SysUser user = userService.selectUserById(loginUser.getUserId());
    user.setLang(lang);
    userService.updateUser(user);
    
    // 3. 设置Cookie(可选)
    response.addCookie(new Cookie("lang", lang));
    
    return AjaxResult.success();
}
2. 异常响应格式
json 复制代码
{
  "code": "err.user_not_exist",
  "msg": "用户不存在", // 根据语言动态变化
  "data": null
}

五、关键配置细节

1. 消息资源文件示例
properties 复制代码
# messages_en_US.properties
login.title=MDM System
login.username=Username
button.submit=Submit
err.user_not_exist=User not found
validation.phone=Invalid phone format: {0}

# messages_zh_CN.properties 
login.title=物料主数据管理系统
login.username=用户名
button.submit=提交
err.user_not_exist=用户不存在
validation.phone=手机号 {0} 格式不正确
2. Element Plus国际化
javascript 复制代码
// src/lang/en-US.js
import elementLocale from 'element-plus/dist/locale/en.mjs'

export default {
  el: elementLocale.el,
  message: {
    // 自定义组件文本...
  }
}

六、测试验证方案

1. 前端测试用例
javascript 复制代码
// 测试语言切换组件
describe('LangSelect', () => {
  it('切换英语应更新localStorage', async () => {
    const wrapper = mount(LangSelect)
    await wrapper.find('.el-select').trigger('click')
    await wrapper.findAll('.el-option')[1].trigger('click')
    expect(localStorage.getItem('lang')).toBe('en-US')
  })
})
2. 后端测试方法
java 复制代码
// 测试消息解析
@SpringBootTest
public class I18nTest {

    @Autowired
    private MessageSource messageSource;

    @Test
    void testEnMessage() {
        LocaleContextHolder.setLocale(Locale.US);
        String msg = messageSource.getMessage("err.user_not_exist", null, Locale.US);
        assertEquals("User not found", msg);
    }
}

七、部署注意事项

  1. Nginx配置
nginx 复制代码
# 设置默认语言
proxy_set_header Accept-Language $http_accept_language;
  1. 浏览器缓存清理
javascript 复制代码
// 在语言切换时添加时间戳
axios.interceptors.request.use(config => {
  if (config.url.includes('?')) {
    config.url += `&t=${Date.now()}`
  } else {
    config.url += `?t=${Date.now()}`
  }
  return config
})
  1. 多语言热更新
java 复制代码
// 开发环境开启热加载
spring:
  messages:
    reloadable: true # 生产环境应设为false

八、排错指南

问题现象 排查步骤 解决方案
切换语言后部分文本未更新 1. 检查Vue DevTools的i18n状态 2. 查看网络请求是否携带正确header 3. 检查后端消息文件编码 确保文件保存为UTF-8
后端返回的提示语仍是中文 1. 调试LocaleContextHolder.getLocale() 2. 检查拦截器是否生效 确认请求头传递正确
切换语言后页面样式错乱 检查Element Plus的locale是否同步 正确导入element语言包

此方案可实现:前端无刷新切换、后端动态响应、用户偏好持久化存储(本地+服务端)。建议先完成基础框架集成,再通过脚本批量提取现有中文文案进行翻译。

相关推荐
無限進步D1 小时前
Java 运行原理
java·开发语言·入门
難釋懷1 小时前
安装Canal
java
是苏浙1 小时前
JDK17新增特性
java·开发语言
不光头强2 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp5 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多5 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood5 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员5 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai