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:

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

相关推荐
掉鱼的猫1 小时前
Solon I18n:三种解析器,零样板代码
java
程序员阿明1 小时前
spring boot3访问resources下的文件,使得在浏览器url中可以直接访问
java·spring boot·后端
wuminyu2 小时前
JUC包的AQS与JVM的ObjectMonitor机制对比分析
java·linux·c语言·jvm·c++
霸道流氓气质2 小时前
SpringBoot中使用OAuth2 认证与 JWT Token — 概念、原理与实践
java·spring boot·后端
dogstarhuang2 小时前
Kimi K3 本地部署实战:从 1.56TB 权重到推理服务的完整成本分析
java·人工智能·后端·ai·开源·接口·程序员创富
就不掉头发2 小时前
C++函数模板
java·开发语言·c++
用户298698530142 小时前
Word 转 PDF 的 3 种自动化实现:从桌面操作到后端服务集成
java·人工智能·后端
Zane19942 小时前
线程池进阶:如何科学设置线程数(结合实际业务场景复盘)
java·后端
SeaTunnel2 小时前
Apache SeaTunnel 提交一个任务都经过了什么?
java·大数据·服务器·apache·etl·seatunnel