Solon I18n:三种解析器,零样板代码

如果你在 Spring Boot 里做过国际化,流程大概是这样的:配 MessageSource 的 Bean、写 ReloadableResourceBundleMessageSource、声明 LocaleResolver、注册 LocaleChangeInterceptor...五六步之后才能看到第一行翻译文本。

Solon 换了个方式。整个 i18n 模块围绕三个解析器、一个注解、一个工具类展开------没有 XML、没有拦截器注册、没有样板配置。

资源文件的约定

把文件放到 resources/i18n/ 下:

bash 复制代码
resources/i18n/messages.properties            # 默认语言(如中文)
resources/i18n/messages_en_US.properties      # 美式英语
resources/i18n/messages_ja_JP.properties      # 日文

格式是标准的 Java properties:

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

yaml 复制代码
# app.yml --- 无需任何配置

自定义头部名称:

java 复制代码
@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeInit() {
        LocaleResolverHeader resolver = new LocaleResolverHeader();
        resolver.setHeaderName("lang");
        return resolver;
    }
}

从 cookie SOLON.LOCALE 读取语言:

java 复制代码
@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeInit() {
        return new LocaleResolverCookie();
    }
}

3. Session 解析器

从 session 属性读取语言:

java 复制代码
@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeInit() {
        return new LocaleResolverSession();
    }
}

4. 自定义解析器

如果三种都不够用,直接实现 LocaleResolver

java 复制代码
@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 --- 快捷工具

随处可用的静态方法:

java 复制代码
// 从当前请求上下文(自动解析 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 --- 命名包访问

从特定语言包读取消息:

java 复制代码
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 做更复杂的操作:

java 复制代码
I18nBundle bundle = I18nUtil.getBundle("i18n.messages", locale);
String value = bundle.get("login.title");
String formatted = bundle.getAndFormat("login.welcome", "Alice");

@I18n --- 模板集成

给 Controller 加上 @I18n,所有模板自动获得 i18n 变量:

java 复制代码
@I18n("i18n.login")
@Controller
public class LoginController {
    @Mapping("/login")
    public ModelAndView login() {
        return new ModelAndView("login.ftl");
    }
}

不指定包名则默认使用 i18n.messages

各模板引擎语法

Freemarker

ftl 复制代码
<h1>${i18n["login.title"]}</h1>
<p>${i18n.getAndFormat("login.welcome", user.name)}</p>

Thymeleaf

html 复制代码
<h1 th:text="${i18n.get('login.title')}">登录</h1>

Beetl

beetl 复制代码
<h1>${i18n["login.title"]}</h1>

分布式扩展

如果翻译内容来自配置中心或 CMS,实现 I18nBundleFactory

java 复制代码
@Component
public class RemoteBundleFactory implements I18nBundleFactory {
    @Override
    public I18nBundle create(String bundleName, Locale locale) {
        Props props = getRemoteProps(bundleName, locale);
        return new I18nBundleImpl(props, locale);
    }
}

手动注册方式:

java 复制代码
I18nUtil.setBundleFactory(new RemoteBundleFactory());

内置的 I18nBundleFactoryLocalresources/i18n/ 读取;工厂接口让你在不改业务代码的前提下换用 Redis、Nacos 或其他数据源。

诚实说三条边界

不含的:

  • 没有 MessageSource 层级体系(Solon 用扁平 bundle)
  • 没有开发模式热加载(重启生效,或自定义 I18nBundleFactory 实现热加载)
  • 没有内置的语言切换端点(切换逻辑留给 Resolver------如果要通过 URL 参数切换,写个自定义 Resolver 即可)

含的:

  • 60 秒内跑起国际化
  • 三种生产可用的解析器策略
  • 五种模板引擎的一等集成
  • 支持分布式配置的工厂模式
  • 零 XML、零拦截器、零仪式感

总结

Solon 的国际化模块延续了框架的哲学:约定优先,但需要时随时替换。一个依赖、一个注解用于模板、一个工具类用于编程访问、三个即插即用的解析器------覆盖了绝大多数真实场景。

相关推荐
weixin_399380692 小时前
TongWeb7.0.4.9M11一键安装脚本(by sy)
java·spring
CallFay云起未来2 小时前
AI客服能不能减少人工回复?从重复咨询到人机协同的落地分析
java·大数据·人工智能·文心一言
我命由我123452 小时前
Android 开发 - 获取当前设备屏幕的旋转角度
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
Elastic 中国社区官方博客2 小时前
OpenTelemetry Java 扩展:无需分叉 agent 即可自定义追踪
java·大数据·运维·开发语言·数据库·人工智能·elasticsearch
京东云开发者2 小时前
上游给空、下游拿到 -1,我把故障一直追到了 commons-beanutils 的构造函数
java·ai编程
阿无,2 小时前
Java多线程面试题之AQS
java·开发语言
数据狐(Datafox)2 小时前
京东商品列表API技术解析与落地应用(含标准 JSON 示例)
java·大数据·前端·人工智能·python·数据分析·json
摇滚侠2 小时前
《SpringBoot 3:入门与应用实战》第 7 章 AOP 思想与实现 阅读笔记 15
java·spring boot·笔记
风尘小子3 小时前
java静态类使用@Value动态绑定环境变量
java
重生之后端学习3 小时前
239. 滑动窗口最大值[困难]✅
java·数据结构·算法·leetcode·职场和发展