Solon 采用了不同的方法。整个 i18n 模块围绕三个解析器、一个注解和一个工具类构建——无需 XML、无需拦截器注册、无需样板配置。
让我来详细介绍它是如何工作的。
资源约定
将消息文件放在 resources/i18n/ 目录下:
resources/i18n/messages.properties # 默认(如中文)
resources/i18n/messages_en_US.properties # 美式英语
resources/i18n/messages_ja_JP.properties # 日语
Enter fullscreen mode Exit fullscreen mode
文件格式为标准的 Java 属性文件:
# messages_en_US.properties
login.title=Sign In
login.welcome=Welcome, {0}!
app.name=Solon Application
Enter fullscreen mode Exit fullscreen mode
就是这样。没有 Bean 声明,没有位置配置。Solon 会自动发现这些文件。
三种区域设置解析器,一个开关
Solon 提供了三种内置的区域设置解析器。默认是基于请求头的,但你可以通过一行代码进行切换。
1. 请求头解析器(默认)
读取 Content-Language 请求头,如果没有则回退到 Accept-Language:
# app.yml — 无需配置,开箱即用
Enter fullscreen mode Exit fullscreen mode
要自定义请求头名称:
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeInit() {
LocaleResolverHeader resolver = new LocaleResolverHeader();
resolver.setHeaderName("lang"); // 现在读取 "lang" 请求头
return resolver;
}
}
Enter fullscreen mode Exit fullscreen mode
2. Cookie 解析器
默认从名为 SOLON.LOCALE 的 Cookie 中读取区域设置:
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeInit() {
return new LocaleResolverCookie();
}
}
Enter fullscreen mode Exit fullscreen mode
自定义 Cookie 名称:
resolver.setCookieName("lang");
Enter fullscreen mode Exit fullscreen mode
3. Session 解析器
从 Session 属性中读取区域设置:
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeInit() {
return new LocaleResolverSession();
}
}
Enter fullscreen mode Exit fullscreen mode
自定义属性名称:
resolver.setAttrName("lang");
Enter fullscreen mode Exit fullscreen mode
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);
}
}
Enter fullscreen mode Exit fullscreen mode
三种读取消息的方式
1. I18nUtil — 快速访问
在任何地方使用静态方法进行一次性查找:
// 从当前请求上下文(自动解析区域设置)
String title = I18nUtil.getMessage("login.title");
// 使用指定的 Locale
String welcome = I18nUtil.getMessage(locale, "login.welcome", "Alice");
// 使用 Context 对象
I18nUtil.getMessage(ctx, "app.name");
Enter fullscreen mode Exit fullscreen mode
2. I18nService — 命名 Bundle 访问
当你需要从特定 bundle 中读取消息时:
I18nService loginI18n = new I18nService("i18n.login");
@Mapping("/greet")
public String greet(Locale locale) {
return loginI18n.get(locale, "login.welcome", "Alice");
}
Enter fullscreen mode Exit fullscreen mode
3. I18nBundle — 编程式 Bundle
对于高级用法,获取原始 bundle:
I18nBundle bundle = I18nUtil.getBundle("i18n.messages", locale);
String value = bundle.get("login.title");
String formatted = bundle.getAndFormat("login.welcome", "Alice");
Enter fullscreen mode Exit fullscreen mode
@I18n — 模板集成
在控制器上添加 @I18n,每个模板都会获得一个 i18n 变量:
@I18n("i18n.login")
@Controller
public class LoginController {
@Mapping("/login")
public ModelAndView login() {
return new ModelAndView("login.ftl");
}
}
Enter fullscreen mode Exit fullscreen mode
如果不指定 bundle 名称,它默认使用 i18n.messages。
模板语法示例
Freemarker(login.ftl):
<h1>${i18n["login.title"]}</h1>
<p>${i18n.getAndFormat("login.welcome", session.user.name)}</p>
Enter fullscreen mode Exit fullscreen mode
Thymeleaf:
<h1 th:text="${i18n.get('login.title')}">Sign In</h1>
<p th:text="${i18n.getAndFormat('login.welcome', 'Alice')}">Welcome!</p>
Enter fullscreen mode Exit fullscreen mode
Beetl:
<h1>${i18n["login.title"]}</h1>
<p>${@i18n.getAndFormat("login.welcome", "Alice")}</p>
Enter fullscreen mode Exit fullscreen mode
Enjoy:
<h1>#(i18n.get("login.title"))</h1>
<p>#(i18n.getAndFormat("login.welcome", "Alice"))</p>
Enter fullscreen mode Exit fullscreen mode
分布式环境扩展
对于在配置中心或 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);
}
}
Enter fullscreen mode Exit fullscreen mode
注册它:
I18nUtil.setBundleFactory(new RemoteBundleFactory());
Enter fullscreen mode Exit fullscreen mode
Solon 内置的 I18nBundleFactoryLocal 从 resources/i18n/ 读取;工厂模式让你可以在不更改应用代码的情况下切换到 Redis、Nacos 或任何其他数据源。
最小依赖
solon-i18n 模块除了 solon-core 之外没有其他传递性依赖。如果你使用 solon-web,它已经包含在内。
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-i18n</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode
权衡
你得不到的:
- 没有
MessageSource式的层级结构(Solon 使用每个名称对应扁平的 bundle) - 没有开发模式下的自动重载(重启以获取更改,或实现带热重载的自定义
I18nBundleFactory) - 没有内置的语言切换端点(有意留给解析器——通过查询参数切换需要自定义
LocaleResolver)
你能得到的:
- 不到 60 秒即可让 i18n 工作
- 三种生产就绪的解析器策略
- 跨 5 种引擎的一流模板集成
- 用于分布式配置的可扩展工厂模式
- 零 XML、零拦截器、零仪式
总结
Solon 的 i18n 模型遵循框架的理念:约定优于配置,但需要时可以切换。一个依赖、一个用于模板的注解、一个用于编程访问的工具,以及三个即插即用的解析器,覆盖了绝大多数真实世界的场景。
如果你一直在与 MessageSource 配置纠缠,不妨试试这个——它会让人耳目一新。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.