Solon I18n:三种解析器,零样板代码
如果你在 Spring Boot 里做过国际化,流程大概是这样的:配 MessageSource 的 Bean、写 ReloadableResourceBundleMessageSource、声明 LocaleResolver、注册 LocaleChangeInterceptor…五六步之后才能看到第一行翻译文本。
Solon 换了个方式。整个 i18n 模块围绕三个解析器、一个注解、一个工具类展开——没有 XML、没有拦截器注册、没有样板配置。
资源文件的约定
把文件放到 resources/i18n/ 下:
resources/i18n/messages.properties # 默认语言(如中文)
resources/i18n/messages_en_US.properties # 美式英语
resources/i18n/messages_ja_JP.properties # 日文
格式是标准的 Java properties:
# messages_en_US.properties
login.title=Sign In
login.welcome=Welcome, {0}!
app.name=Solon Application
不需要 Bean 声明,不需要指定路径。Solon 自动发现。
三种解析器,一键切换
Solon 内置三个地区解析器。默认是 请求头解析器,但可以一行代码换掉。
1. Header 解析器(默认)
读取 Content-Language 请求头,降级到 Accept-Language:
# app.yml — 无需任何配置
自定义头部名称:
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeInit() {
LocaleResolverHeader resolver = new LocaleResolverHeader();
resolver.setHeaderName("lang");
return resolver;
}
}
2. Cookie 解析器
从 cookie SOLON.LOCALE 读取语言:
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeInit() {
return new LocaleResolverCookie();
}
}
3. Session 解析器
从 session 属性读取语言:
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeInit() {
return new LocaleResolverSession();
}
}
4. 自定义解析器
如果三种都不够用,直接实现 LocaleResolver:
@Component
public class QueryParamResolver implements LocaleResolver {
@Override
public Locale getLocale(Context ctx) {
String lang = ctx.param("lang");
if (lang != null) {
return LocaleUtil.toLocale(lang);
}
return Locale.getDefault();
}
@Override
public void setLocale(Context ctx, Locale locale) {
ctx.setLocale(locale);
}
}
三种读消息的方式
1. I18nUtil — 快捷工具
随处可用的静态方法:
// 从当前请求上下文(自动解析 locale)
String title = I18nUtil.getMessage("login.title");
// 指定 Locale
String welcome = I18nUtil.getMessage(locale, "login.welcome", new Object[]{"Alice"});
// 指定 Context
I18nUtil.getMessage(ctx, "app.name");
2. I18nService — 命名包访问
从特定语言包读取消息:
I18nService loginI18n = new I18nService("i18n.login");
@Mapping("/greet")
public String greet(Locale locale) {
return loginI18n.get(locale, "login.welcome", new Object[]{"Alice"});
}
3. I18nBundle — 编程式操作
获取原始 bundle 做更复杂的操作:
I18nBundle bundle = I18nUtil.getBundle("i18n.messages", locale);
String value = bundle.get("login.title");
String formatted = bundle.getAndFormat("login.welcome", "Alice");
@I18n — 模板集成
给 Controller 加上 @I18n,所有模板自动获得 i18n 变量:
@I18n("i18n.login")
@Controller
public class LoginController {
@Mapping("/login")
public ModelAndView login() {
return new ModelAndView("login.ftl");
}
}
不指定包名则默认使用 i18n.messages。
各模板引擎语法
Freemarker:
${i18n["login.title"]}
${i18n.getAndFormat("login.welcome", user.name)}
Thymeleaf:
登录
Beetl:
${i18n["login.title"]}
分布式扩展
如果翻译内容来自配置中心或 CMS,实现 I18nBundleFactory:
@Component
public class RemoteBundleFactory implements I18nBundleFactory {
@Override
public I18nBundle create(String bundleName, Locale locale) {
Props props = getRemoteProps(bundleName, locale);
return new I18nBundleImpl(props, locale);
}
}
手动注册方式:
I18nUtil.setBundleFactory(new RemoteBundleFactory());
内置的 I18nBundleFactoryLocal 从 resources/i18n/ 读取;工厂接口让你在不改业务代码的前提下换用 Redis、Nacos 或其他数据源。
诚实说三条边界
不含的:
- 没有
MessageSource层级体系(Solon 用扁平 bundle) - 没有开发模式热加载(重启生效,或自定义
I18nBundleFactory实现热加载) - 没有内置的语言切换端点(切换逻辑留给 Resolver——如果要通过 URL 参数切换,写个自定义 Resolver 即可)
含的:
- 60 秒内跑起国际化
- 三种生产可用的解析器策略
- 五种模板引擎的一等集成
- 支持分布式配置的工厂模式
- 零 XML、零拦截器、零仪式感
总结
Solon 的国际化模块延续了框架的哲学:约定优先,但需要时随时替换。一个依赖、一个注解用于模板、一个工具类用于编程访问、三个即插即用的解析器——覆盖了绝大多数真实场景。
原文地址: https://www.cveoy.top/t/topic/qHhF 著作权归作者所有。请勿转载和采集!