Skip to content

国际化配置

概述

ruoyi-admin 提供了完善的国际化(i18n)支持,实现了系统消息的多语言切换:

  • 多语言支持 - 内置中文和英文,支持扩展其他语言
  • 消息分类管理 - 按功能模块组织消息Key
  • 参数化消息 - 支持动态参数插值
  • 自动切换 - 根据请求自动选择语言
  • 默认回退 - 未翻译消息自动回退到默认语言

文件结构

资源文件位置

src/main/resources/i18n/
├── messages.properties         # 默认消息(中文)
├── messages_zh_CN.properties   # 中文消息
└── messages_en_US.properties   # 英文消息

文件命名规范

文件名说明
messages.properties默认语言,当找不到对应语言时使用
messages_zh_CN.properties简体中文
messages_en_US.properties美式英语
messages_zh_TW.properties繁体中文(可扩展)
messages_ja_JP.properties日语(可扩展)

命名格式:messages_{语言代码}_{国家代码}.properties

应用配置

Spring配置

yaml
spring:
  messages:
    # 消息文件基础名
    basename: i18n/messages
    # 消息编码
    encoding: UTF-8

配置说明

配置项说明默认值
basename消息文件基础路径i18n/messages
encoding文件编码格式UTF-8
cache-duration消息缓存时间-
fallback-to-system-locale是否回退到系统语言true

消息分类

系统消息按功能模块进行分类管理:

通用验证消息(common.*)

properties
# 通用验证消息
common.required=* 必须填写
common.length.invalid=长度必须在{min}到{max}个字符之间
common.id.required=主键ID不能为空
Key中文English
common.required* 必须填写* Required
common.length.invalid长度必须在{min}到{max}个字符之间Length must be between {min} and {max} characters
common.id.required主键ID不能为空Primary key ID cannot be empty

操作结果消息(operation.*)

properties
# 通用操作结果消息
operation.success=操作成功
operation.fail=操作失败
operation.query.success=查询成功
operation.add.success=新增成功
operation.update.success=修改成功
operation.delete.success=删除成功
operation.import.success=导入成功
operation.export.success=导出成功
Key中文English
operation.success操作成功Operation successful
operation.fail操作失败Operation failed
operation.query.success查询成功Query successful
operation.add.success新增成功Create successful
operation.update.success修改成功Update successful
operation.delete.success删除成功Delete successful
operation.import.success导入成功Import successful
operation.export.success导出成功Export successful

用户认证消息(user.*)

账号状态

properties
user.account.not.exists=对不起, 您的账号:{0} 不存在
user.password.mismatch=用户不存在/密码错误
user.account.disabled=对不起,您的账号:{0} 已禁用,请联系管理员
Key中文English
user.account.not.exists对不起, 您的账号:{0} 不存在Sorry, your account: {0} does not exist
user.password.mismatch用户不存在/密码错误User does not exist or password is incorrect
user.account.disabled对不起,您的账号:{0} 已禁用,请联系管理员Sorry, your account: {0} has been disabled. Please contact the administrator

登录注册

properties
user.login.success=登录成功
user.logout.success=退出成功
user.register.success=注册成功
user.register.account.exists=保存用户 {0} 失败,注册账号已存在
user.register.failed=注册失败,请联系系统管理人员
Key中文English
user.login.success登录成功Login successful
user.logout.success退出成功Logout successful
user.register.success注册成功Registration successful
user.register.account.exists保存用户 {0} 失败,注册账号已存在Failed to save user {0}, the account already exists
user.register.failed注册失败,请联系系统管理人员Registration failed, please contact system administrator

会话状态

properties
user.session.expired=会话已过期,请重新登录
user.admin.force.logout=管理员强制退出,请重新登录
user.system.error.relogin=系统异常,请重新登录
Key中文English
user.session.expired会话已过期,请重新登录Session has expired, please login again
user.admin.force.logout管理员强制退出,请重新登录Administrator forced logout, please login again
user.system.error.relogin系统异常,请重新登录System error, please login again

密码重试限制

properties
user.password.retry.count=密码输入错误{0}次
user.password.retry.locked=密码输入错误{0}次,帐户锁定{1}分钟
Key中文English
user.password.retry.count密码输入错误{0}次Password input error {0} times
user.password.retry.locked密码输入错误{0}次,帐户锁定{1}分钟Password input error {0} times, account locked for {1} minutes

用户信息验证

properties
user.username.required=用户名不能为空
user.username.format.invalid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.username.length.invalid=账户长度必须在{min}到{max}个字符之间
user.password.required=用户密码不能为空
user.password.length.invalid=用户密码长度必须在{min}到{max}个字符之间
user.password.format.invalid=* 密码格式不正确,5-50个字符
user.password.complexity.invalid=密码必须同时包含字母和数字
user.email.format.invalid=邮箱格式错误
user.email.required=邮箱不能为空
user.phone.required=用户手机号不能为空
user.phone.format.invalid=手机号格式错误

认证方式消息(auth.*)

properties
auth.type.unsupported=认证方式不支持
auth.type.disabled=认证方式已禁用
auth.type.required=认证方式不能为空
Key中文English
auth.type.unsupported认证方式不支持Authentication type is not supported
auth.type.disabled认证方式已禁用Authentication type is disabled
auth.type.required认证方式不能为空Authentication type cannot be blank

验证码消息(verify.code.*)

图形验证码

properties
verify.code.captcha.required=图形验证码不能为空
verify.code.captcha.invalid=图形验证码错误
verify.code.captcha.expired=图形验证码已失效
Key中文English
verify.code.captcha.required图形验证码不能为空Captcha cannot be blank
verify.code.captcha.invalid图形验证码错误Captcha is invalid
verify.code.captcha.expired图形验证码已失效Captcha has expired

短信验证码

properties
verify.code.sms.required=短信验证码不能为空
verify.code.sms.retry.count=短信验证码输入错误{0}次
verify.code.sms.retry.locked=短信验证码输入错误{0}次,帐户锁定{1}分钟
verify.code.sms.invalid=短信验证码错误
verify.code.sms.expired=短信验证码已过期
Key中文English
verify.code.sms.required短信验证码不能为空SMS verification code cannot be blank
verify.code.sms.invalid短信验证码错误SMS verification code is invalid
verify.code.sms.expired短信验证码已过期SMS verification code has expired

邮箱验证码

properties
verify.code.email.required=邮箱验证码不能为空
verify.code.email.retry.count=邮箱验证码输入错误{0}次
verify.code.email.retry.locked=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
verify.code.email.invalid=邮箱验证码错误
verify.code.email.expired=邮箱验证码已过期
Key中文English
verify.code.email.required邮箱验证码不能为空Email verification code cannot be blank
verify.code.email.invalid邮箱验证码错误Email verification code is invalid
verify.code.email.expired邮箱验证码已过期Email verification code has expired

第三方登录消息(social.*)

properties
social.login.source.required=第三方登录平台[source]不能为空
social.login.auth.code.required=第三方登录平台[code]不能为空
social.login.state.required=第三方登录平台[state]不能为空
Key中文English
social.login.source.required第三方登录平台[source]不能为空Social login platform [source] cannot be blank
social.login.auth.code.required第三方登录平台[code]不能为空Social login platform [code] cannot be blank
social.login.state.required第三方登录平台[state]不能为空Social login platform [state] cannot be blank

文件上传消息(file.*)

properties
file.upload.size.exceed.limit=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
file.upload.filename.too.long=上传的文件名最长{0}个字符
file.upload.type.not.supported=文件类型不支持
file.upload.failed=文件上传失败
Key中文English
file.upload.size.exceed.limit上传的文件大小超出限制!允许最大:{0}MBThe uploaded file size exceeds the limit! Maximum: {0}MB
file.upload.filename.too.long上传的文件名最长{0}个字符Maximum filename length is {0} characters
file.upload.type.not.supported文件类型不支持File type is not supported
file.upload.failed文件上传失败File upload failed

权限控制消息(permission.*)

properties
permission.no.access=没有访问权限,请联系管理员添加权限 {0} [{1}]
permission.no.create=您没有创建数据的权限,请联系管理员添加权限 [{0}]
permission.no.update=您没有修改数据的权限,请联系管理员添加权限 [{0}]
permission.no.delete=您没有删除数据的权限,请联系管理员添加权限 [{0}]
permission.no.export=您没有导出数据的权限,请联系管理员添加权限 [{0}]
permission.no.view=您没有查看数据的权限,请联系管理员添加权限 [{0}]
Key中文English
permission.no.access没有访问权限,请联系管理员添加权限Access denied, please contact administrator
permission.no.create您没有创建数据的权限You do not have permission to create data
permission.no.update您没有修改数据的权限You do not have permission to update data
permission.no.delete您没有删除数据的权限You do not have permission to delete data
permission.no.export您没有导出数据的权限You do not have permission to export data
permission.no.view您没有查看数据的权限You do not have permission to view data

请求控制消息(request.*)

properties
request.control.duplicate.submit=不允许重复提交,请稍候再试
request.control.rate.limit.exceeded=访问过于频繁,请稍候再试
Key中文English
request.control.duplicate.submit不允许重复提交,请稍候再试Duplicate submission is not allowed, please try again later
request.control.rate.limit.exceeded访问过于频繁,请稍候再试Too many requests, please try again later

租户管理消息(tenant.*)

properties
tenant.id.required=租户ID不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
tenant.disabled=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员
tenant.mode.not.enabled=当前未开启租户模式
tenant.sync.package.success=同步租户套餐成功
tenant.sync.roles.success=同步租户角色成功
tenant.sync.dicts.success=同步租户字典成功
Key中文English
tenant.id.required租户ID不能为空Tenant ID cannot be blank
tenant.not.exists对不起,您的租户不存在Sorry, your tenant does not exist
tenant.disabled对不起,您的租户已禁用Sorry, your tenant is disabled
tenant.expired对不起,您的租户已过期Sorry, your tenant has expired
tenant.mode.not.enabled当前未开启租户模式Tenant mode is not enabled
tenant.sync.package.success同步租户套餐成功Tenant package synchronized successfully
tenant.sync.roles.success同步租户角色成功Tenant roles synchronized successfully
tenant.sync.dicts.success同步租户字典成功Tenant dictionaries synchronized successfully

消息Key命名规范

命名结构

{模块}.{子模块}.{功能}.{状态}

命名示例

Key模块子模块功能状态
user.account.not.existsuseraccount-not.exists
verify.code.sms.invalidverifycode.sms-invalid
permission.no.accesspermission--no.access
operation.add.successoperation-addsuccess

命名建议

  1. 使用小写字母:所有Key使用小写字母
  2. 点号分隔:使用.分隔层级
  3. 语义清晰:Key名应能表达消息含义
  4. 状态后缀:操作结果使用success/fail等后缀
  5. 错误前缀:错误消息可使用invalid/not等前缀

参数化消息

占位符语法

使用 {index} 作为参数占位符:

properties
# 单个参数
user.account.not.exists=对不起, 您的账号:{0} 不存在

# 多个参数
user.password.retry.locked=密码输入错误{0}次,帐户锁定{1}分钟

使用示例

java
// 单个参数
String message = MessageUtils.message("user.account.not.exists", "admin");
// 输出:对不起, 您的账号:admin 不存在

// 多个参数
String message = MessageUtils.message("user.password.retry.locked", 5, 10);
// 输出:密码输入错误5次,帐户锁定10分钟

参数类型支持

类型示例说明
字符串{0}直接替换
数字{0}自动转换为字符串
日期{0,date}支持日期格式化
数字格式{0,number}支持数字格式化

代码中使用国际化

MessageUtils工具类

框架提供了MessageUtils工具类简化国际化消息的获取:

java
import plus.ruoyi.common.core.utils.MessageUtils;

// 获取消息(无参数)
String msg = MessageUtils.message("operation.success");

// 获取消息(带参数)
String msg = MessageUtils.message("user.account.not.exists", username);

// 获取消息(多个参数)
String msg = MessageUtils.message("user.password.retry.locked", retryCount, lockTime);

在Service中使用

java
@Service
public class SysUserServiceImpl implements ISysUserService {

    @Override
    public void checkUserAllowed(SysUser user) {
        if (user.isAdmin()) {
            throw new ServiceException(MessageUtils.message("permission.no.update", "管理员"));
        }
    }
}

在Controller中使用

java
@RestController
public class AuthController {

    @PostMapping("/login")
    public R<LoginVo> login(@RequestBody LoginBody loginBody) {
        // 业务处理...
        return R.ok(loginVo, MessageUtils.message("user.login.success"));
    }
}

在异常中使用

java
// 抛出带国际化消息的异常
throw new ServiceException(MessageUtils.message("user.account.disabled", username));

// 全局异常处理器会自动处理

语言切换机制

请求头方式

通过HTTP请求头 Accept-Language 切换语言:

http
GET /api/user/info HTTP/1.1
Accept-Language: en-US

请求参数方式

通过URL参数切换语言:

GET /api/user/info?lang=en_US

Cookie方式

通过Cookie存储语言偏好:

http
Cookie: lang=en_US

优先级顺序

  1. URL参数 lang
  2. Cookie lang
  3. 请求头 Accept-Language
  4. 系统默认语言

添加新语言

步骤一:创建语言文件

复制默认语言文件,创建新语言版本:

bash
cp messages.properties messages_ja_JP.properties

步骤二:翻译消息内容

编辑新语言文件,翻译所有消息:

properties
# messages_ja_JP.properties
common.required=* 必須
operation.success=操作成功
user.login.success=ログイン成功

步骤三:配置语言支持

如需添加语言选择器,在前端配置可用语言列表:

javascript
const locales = [
  { code: 'zh_CN', name: '简体中文' },
  { code: 'en_US', name: 'English' },
  { code: 'ja_JP', name: '日本語' }
]

最佳实践

1. 统一使用工具类

java
// 推荐:使用MessageUtils
throw new ServiceException(MessageUtils.message("user.account.disabled", username));

// 不推荐:硬编码中文
throw new ServiceException("账号已禁用");

2. 消息Key分模块管理

properties
# 按模块组织,便于维护
user.login.success=登录成功
user.logout.success=退出成功

# 避免混乱的命名
login.ok=登录成功           # 不推荐
success.login=登录成功      # 不推荐

3. 保持各语言文件同步

确保所有语言文件包含相同的Key:

bash
# 检查Key一致性
diff <(grep "^[^#]" messages_zh_CN.properties | cut -d= -f1 | sort) \
     <(grep "^[^#]" messages_en_US.properties | cut -d= -f1 | sort)

4. 参数化而非拼接

properties
# 推荐:使用参数
user.welcome=欢迎您,{0}

# 不推荐:需要多个消息拼接
user.welcome.prefix=欢迎您,

5. 提供清晰的错误提示

properties
# 推荐:具体明确
user.password.complexity.invalid=密码必须同时包含字母和数字

# 不推荐:含糊不清
user.password.invalid=密码无效

常见问题

1. 消息显示Key而非内容

问题原因:

  • 消息文件路径配置错误
  • Key拼写错误
  • 消息文件编码问题

解决方案:

yaml
# 确保配置正确
spring:
  messages:
    basename: i18n/messages
    encoding: UTF-8
java
// 检查Key是否存在
log.debug("消息Key: {}", MessageUtils.message("user.login.success"));

2. 中文显示乱码

问题原因:

  • 文件编码不是UTF-8
  • IDE编码设置问题

解决方案:

  1. 确保properties文件使用UTF-8编码保存
  2. 在IDE中设置properties文件编码为UTF-8
  3. 或使用Unicode转义:
properties
# Unicode转义形式
user.login.success=\u767b\u5f55\u6210\u529f

3. 参数替换不生效

问题原因:

  • 占位符格式错误
  • 参数数量不匹配

解决方案:

properties
# 正确的占位符格式
user.account.not.exists=账号:{0} 不存在

# 确保传递参数
MessageUtils.message("user.account.not.exists", username);

4. 语言切换不生效

问题原因:

  • LocaleResolver未配置
  • 请求头/参数格式错误

解决方案:

java
// 确保LocaleResolver已配置
@Bean
public LocaleResolver localeResolver() {
    CookieLocaleResolver resolver = new CookieLocaleResolver();
    resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
    return resolver;
}

5. 新增消息Key未生效

问题原因:

  • 消息文件未重新加载
  • 缓存问题

解决方案:

yaml
# 开发环境禁用缓存
spring:
  messages:
    cache-duration: 0

或重启应用使新消息生效。

消息Key完整列表

消息统计

分类数量前缀
通用验证3common.*
操作结果8operation.*
用户认证18user.*
认证方式3auth.*
验证码11verify.code.*
第三方登录3social.*
文件上传4file.*
权限控制6permission.*
请求控制2request.*
租户管理8tenant.*
总计66-

扩展建议

如需添加新的消息,建议按照以下分类:

新模块前缀建议示例
商城模块mall.*mall.order.create.success
支付模块pay.*pay.balance.insufficient
工作流workflow.*workflow.task.completed
通知消息notify.*notify.send.success