1、springboot配置
实现LocalResolve接口,定义MessageService bean
@Configuration
public class MyLocalResolve implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String lang = httpServletRequest.getHeader("lang");
if (StringUtils.isNotBlank(lang)) {
String[] str = lang.split("_");
if (str.length == 2) {
return new Locale(str[0], str[1]);
}
}
return Locale.getDefault();
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
/**
* 自定义messages服务,指定国际化配置路径
* @return
*/
@Bean(name = "messageSource")
public ResourceBundleMessageSource getMessageResource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "i18n/messages");
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
return messageSource;
}
}
2、工具类
@Component
public class MultiLanguageUtils {
@Resource
private MessageSource messageSource;
private static MessageSource localMessageSource;
@PostConstruct
private void init() {
localMessageSource = messageSource;
}
/**
* 获取国际化messages
* @param key
* @return
*/
public static String getMessage(String key) {
return getMessage(key, null);
}
/**
* 获取国际化messages
* @param key messages.properties的key
* @param args messages的占位符{}
* @return
*/
public static String getMessage(String key, Object[] args) {
return localMessageSource.getMessage(key, args, key, LocaleContextHolder.getLocale());
}
}
3、新建多语言文件
在resources下新建多语言配置文件,这里我配置的是message和i18n/message,如果未配置MessageSource Bean,默认是resource下面的messages
4、springboot多语言配置原理
核心就是MessageSource.getMessage(key, args, key, LocaleContextHolder.getLocale())
local通过localContext获取,localContext又由LocaleContextHold获取
FrameworkServlet
LocaleContext localeContext = buildLocaleContext(request);
DispatcherServlet
重写buildLocaleContext(request)
public class DispatcherServlet {
@Override
protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
LocaleResolver lr = this.localeResolver;
if (lr instanceof LocaleContextResolver) {
return ((LocaleContextResolver) lr).resolveLocaleContext(request);
}
else {
//核心在这里,回调实现接口LocaleResolver的resolveLocale方法,获取真正的Locale
return () -> (lr != null ? lr.resolveLocale(request) : request.getLocale());
}
}
}
Locale locale = LocaleContextHolder.getLocale()
public class LocaleContextHolder{
//通过ThreadLocal保证线程安全性
private static final ThreadLocal<LocaleContext> localeContextHolder = new NamedThreadLocal<>("LocaleContext");
public static Locale getLocale() {
return getLocale(getLocaleContext());
}
public static LocaleContext getLocaleContext() {
LocaleContext localeContext = localeContextHolder.get();
if (localeContext == null) {
localeContext = inheritableLocaleContextHolder.get();
}
return localeContext;
}
public static Locale getLocale(@Nullable LocaleContext localeContext) {
if (localeContext != null) {
Locale locale = localeContext.getLocale();
if (locale != null) {
return locale;
}
}
return (defaultLocale != null ? defaultLocale : Locale.getDefault());
}
}
在springboot中,借助约定大于配置的思想,通过默认配置实现springboot的国际化默认配置。通过MessageSourceAutoConfiguration实现。
在没有配置messageSource bean时,默认生成一个messageSource bean
@Configuration(proxyBeanMethods = false)
// public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = {};
@Bean
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
protected static class ResourceBundleCondition extends SpringBootCondition {
private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
ConditionOutcome outcome = cache.get(basename);
if (outcome == null) {
outcome = getMatchOutcomeForBasename(context, basename);
cache.put(basename, outcome);
}
return outcome;
}
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {
ConditionMessage.Builder message = ConditionMessage.forCondition("ResourceBundle");
for (String name : StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename))) {
for (Resource resource : getResources(context.getClassLoader(), name)) {
if (resource.exists()) {
return ConditionOutcome.match(message.found("bundle").items(resource));
}
}
}
return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());
}
private Resource[] getResources(ClassLoader classLoader, String name) {
String target = name.replace('.', '/');
try {
return new PathMatchingResourcePatternResolver(classLoader)
.getResources("classpath*:" + target + ".properties");
}
catch (Exception ex) {
return NO_RESOURCES;
}
}
}
}
public class MessageSourceProperties {
private String basename = "messages";
private Charset encoding = StandardCharsets.UTF_8;
@DurationUnit(ChronoUnit.SECONDS)
private Duration cacheDuration;
private boolean fallbackToSystemLocale = true;
private boolean alwaysUseMessageFormat = false;
private boolean useCodeAsDefaultMessage = false;
}
github源码:https://github.com/XiYiYe/practise