Отсутствует функциональность между заголовком Accept-Language и ResourceBundle - PullRequest
0 голосов
/ 24 июня 2019

Google foo подвел меня. Я хочу выяснить, существует ли стандартный «по книге» способ преобразования входных локалей из заголовка Accept-Language в правильный ResourceBundle.

ResourceBundle::getBundle() метод (ы) принимает одну локаль, но Accept-Language может иметь несколько локалей, взвешенных по индексу, например: de;q=1.0, sl;q=0.9.

Текущий код:

@Context
private HttpServletRequest request;

public String getString(String key) {
        ResourceBundle i18n = ResourceBundle.getBundle("locale/strings", this.request.getLocale());
        return i18n.getString(key);
}

Проблема в том, что getLocale() возвращает предпочтительный языковой стандарт, в данном случае de. Если доступных комплектов ресурсов равны sl и en, будет предпринята попытка найти de и затем выполнить откат к en, но фактический ожидаемый клиентом результат будет sl!

Мой вопрос в основном, должен ли я реализовать собственный резервный код, который повторяется по HttpServletRequest.getLocales() (я не хочу изобретать колесо ...), или есть более стандартный и простой способ сделать это? Я также согласился бы на какую-нибудь стороннюю библиотеку, которая восполняет этот пробел.

Пользовательское решение до сих пор:

@RequestScoped
public class Localization {

    @Context
    private HttpServletRequest request;

    private ResourceBundle i18n;

    @PostConstruct
    void postConstruct() {
        //List of locales from Accept-Language header
        List<Locale> locales = Collections.list(request.getLocales());

        if (locales.isEmpty()) {
            //Fall back to default locale
            locales.add(request.getLocale());
        }

        for (Locale locale : locales) {
            try {
                i18n = ResourceBundle.getBundle("bundles/translations", locale);
                if (!languageEquals(i18n.getLocale(), locale)) {
                    //Default fallback detected
                    //The resource bundle that was returned has different language than the one requested, continue
                    //Only language tag is checked, no support for detecting different regions in this sample
                    continue;
                }
                break;
            }
            catch (MissingResourceException ignore) {
            }
        }
    }

    private boolean languageEquals(Locale first, Locale second) {
        return getISO2Language(first).equalsIgnoreCase(getISO2Language(second));
    }

    private String languageGetISO2(Locale locale) {
        String[] localeStrings = (locale.getLanguage().split("[-_]+"));
        return localeStrings[0];
    }

    public ResourceBundle i18n() {
        return this.i18n;
    }
}

1 Ответ

0 голосов
/ 24 июня 2019

Я бы написал Interceptor, там вы можете установить нужный язык и применить желаемую логику к ThreadLocal или передать его.

т.е. вы проверяете доступные языки и определяете порядок или наборпо умолчанию.

Если вы используете Spring, вы можете затем установить LocaleContextHolder вручную или использовать LocaleContextResolver вместо написания собственного перехватчика.

...