springboot多语言国际化

3 篇文章 0 订阅
1 篇文章 0 订阅

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值