Spring Boot国际化流程繁琐Solon另辟蹊径如果你在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.propertieslogin.titleSign Inlogin.welcomeWelcome, {0}!app.nameSolon Application不需要Bean声明不需要指定路径。Solon自动发现。三种解析器一键切换Solon内置三个地区解析器。默认是 **请求头解析器** 但可以一行代码换掉。1. Header 解析器默认读取 Content-Language 请求头降级到 Accept-Language_# app.yml -- 无需任何配置_自定义头部名称Configurationpublic class I18nConfig {Beanpublic LocaleResolver localeInit() {LocaleResolverHeader resolver new LocaleResolverHeader();resolver.setHeaderName(lang);return resolver;}}2. Cookie 解析器从cookie SOLON.LOCALE 读取语言Configurationpublic class I18nConfig {Beanpublic LocaleResolver localeInit() {return new LocaleResolverCookie();}}3. Session 解析器从session属性读取语言Configurationpublic class I18nConfig {Beanpublic LocaleResolver localeInit() {return new LocaleResolverSession();}}4. 自定义解析器如果三种都不够用直接实现 LocaleResolverComponentpublic class QueryParamResolver implements LocaleResolver {Overridepublic Locale getLocale(Context ctx) {String lang ctx.param(lang);if (lang ! null) {return LocaleUtil.toLocale(lang);}return Locale.getDefault();}Overridepublic 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)Controllerpublic 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实现 I18nBundleFactoryComponentpublic class RemoteBundleFactory implements I18nBundleFactory {Overridepublic 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的国际化模块延续了框架的哲学 **约定优先但需要时随时替换** 。一个依赖、一个注解用于模板、一个工具类用于编程访问、三个即插即用的解析器 ----覆盖了绝大多数真实场景。