无法获得针对特定国家/地区的本地化响应

时间:2016-05-04 17:36:01

标签: java rest localization spring-boot internationalization

我有一个春季启动应用程序,我们正在尝试支持多种语言和国家/地区。

这些是我们现在计划支持的代码。 en,es,fr,en-gb,es-mx,zh,ja。

我正在努力获取一些字段以响应本地化。 当我在本地(eclipse)中运行应用程序并检查邮递员的响应时,我可以看到它正在拾取特定于国家/地区的文件并正确地给出响应,但是当我将更改推送到AWS服务器时它不是,它只是获取特定于语言的文件并忽略国家/地区代码。

例如,如果我将Accept-Language设置为da,es-MX; q = 0.8我期望es-MX中的响应(我们不支持da)但它只是在es中给出响应(我有es和es-MX文件)并忽略国家代码MX。

但是,如果我将Accept-Language作为fr,es-MX; q = 0.8,它将获取messages_fr.properties文件,并以法语给出我期望的响应。

以下是我的课程。

@Configuration
@ComponentScan("com.hsf")
@EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter {

    @Value("${spring.application.name}")
    String appName;

    @Bean
    public LocaleResolver localeResolver() {
        return new SmartLocaleResolver();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        final DispatcherServlet servlet = new DispatcherServlet();
        servlet.setDispatchOptionsRequest(true);
        return servlet;
    }

    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:i18n/messages");
        // If true, the key of the message will be displayed if the key is not
        // found, instead of throwing an exception
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        // The value 0 means always reload the messages to be developer friendly
        messageSource.setCacheSeconds(10);
        return messageSource;
    }

}

此类解析Accept-Language标头并设置Locale。

public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {

        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            try {
                List<LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
                if (!list.isEmpty()) {
                    for (LanguageRange s : list) {
                        if (ApplicationConstants.LOCALE.contains(s.getRange())) {
                            return Locale.forLanguageTag(s.getRange());
                        }
                    }
                }
            } catch (IllegalArgumentException e) {
                throw e;
            }
            return request.getLocale();
        }
    }

我的ApplicationConstants类中有以下常量

public static final List<String> LOCALE              = Collections
            .unmodifiableList(Arrays.asList("en", "es", "fr", "es-mx", "zh", "ja"));

这是MessageHandler类

@Component
public class MessageHandler {
    @Autowired
    private MessageSource messageSource;

    public String localizeMessage(String errorCode, Object args[]) {
        Locale locale = LocaleContextHolder.getLocale();
        String message = messageSource.getMessage(errorCode, args, locale);
        return message;

    }

    public String localizeMessage(String errorCode) {
        return localizeMessage(errorCode, null);
    }

}

对此有何想法?

2 个答案:

答案 0 :(得分:2)

您似乎只是重新实现了Locale的lookup()方法:

    List<Locale> locales = Arrays.asList(new Locale("en"),
                                         new Locale("es"),
                                         new Locale("fr"),
                                         new Locale("es", "MX"),
                                         new Locale("zh"),
                                         new Locale("ja"));
    List<Locale.LanguageRange> ranges = Locale.LanguageRange.parse("da,es-MX;q=0.8");
    Locale best = Locale.lookup(ranges, locales);
    System.out.println("best = " + best); // prints best = es_MX

    ranges = Locale.LanguageRange.parse("fr,es-MX;q=0.8");
    best = Locale.lookup(ranges, locales);
    System.out.println("best = " + best); // prints best = fr

答案 1 :(得分:1)

有几种方法可以实现正确的区域设置查找方法(顺便说一下i18n最佳实践的祝贺方式)。
一种方法是在您尝试时“手动”解析标题 但请注意,HttpServletRequest从其超类中派生出一种奇特的方法,即ServletRequest#getLocales()。 我的观点是,您将收到已经解析的Locales优先级Enumeration。顺便说一句,我肯定会使用HashSet来比较区域设置(出于性能原因)。

足够说话:

public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        List<Locale> locales = Collections.list(request.getLocales());
        return getBestMatchingOrDefault(locales);
    }

    private Locale getBestMatchingOrDefault(List<Locale> locales) {
        Optional<Locale> maybeLocale = locales.stream()
            .filter(locale -> ApplicationConstants.LOCALE.contains(locale))
            .findFirst();
        return maybeLocale.orElse(ApplicationConstants.DEFAULT_LOCALE);
    }
}

在这种情况下,需要修改ApplicationConstants类:

public static final Set<Locale> LOCALE = Collections.unmodifiableSet(
    new HashSet<Locale>(
        Arrays.asList(Locale.ENGLISH, 
                      Locale.forLanguageTag("es"), 
                      Locale.FRENCH, 
                      Locale.forLanguageTag("es-MX"), 
                      Locale.JAPANESE, 
                      Locale.forLanguageTag("zh-Hans"), 
                      Locale.PRC, 
                      Locale.CHINESE)));
public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; // or something else?

关于中文的一句话。有两种中文口味 - 简体中文(zh-Hans)和繁体中文(zh-Hant)。最好不要混合它们。我不确定将简体中文版本作为默认中文版本服务是否是个好主意。好吧,也许是,但我怀疑。我会特别使用zh-Hans和zh-CN(即使这意味着我会在CI构建期间复制相同的文件)。