[Grails] Сегодня опубликовал стабильную версию grails-locale-configuration-plugin


Всем привет.
Пару недель назад я накидал заметку с решением проблемы неподдерживаемых локакелей.
Спустся несколько дней Бари Норман, автор сайта для продаж монет www.pecunem.com, попробовал моё ршение и обнаружил несколько багов-недочётов.
Немного вкурив проблемы стало ясно что решение через фильтр описаное оказалось в корне неверным: нужно было всё таки писать свой LocaleResolver. Точнее достаточно переопределить SessionLocaleResolver.
Вместе с Бари мы создали свой класс SmartLocaleResolver который я оформил в виде плагина grails-locale-configuration-plugin.
Работает он примерно так: Допустим мы поддерживаем французкую fr_FR, и канадскую локаль fr_CA французкого языка и английский язык en.
Мы хотим чтобы если пользователь запросил не поддерживаемый язык, например испанский es то переключать сайт на английский en.
При этом если пользователь запросит например локаль для Монако fr_MC которая у нас напрямую не поддерживается, то возьмётся первая же локаль которая поддерживает французкий язык.
?lang=fr_FR -> response.locale == fr_FR
?lang=fr_CA -> response.locale == fr_CA
?lang=fr_MC -> response.locale == fr_FR
?lang=fr -> response.locale == fr_FR
?lang=es -> response.locale == en

В grails-app/i18n/ соответсвено должны быть:
messages_fr_FR.properties
messages_fr_CA.properties
messages_en.properties

Для того чтобы этого добиться, установите плагин и пропишите в конфигурацию:

name.stokito.smartLocaleResolver.supportedLocales = [Locale.FRANCE, Locale.CANADA_FRENCH, Locale.ENGLISH]
name.stokito.smartLocaleResolver.defaultLocale = Locale.ENGLISH

На самом деле всё ещё круче: плагин ещё и смотрит на заголовок Accept-Language который задаётся в настройках броузера пользователя и может подобрать локаль из них.
chrome languages settings

И сегодня я рад сообщить что его версия 0.4 уже вполне стабильна, и может использоваться в продакшене.
К сожалению там еще предстоит поменять архитектуру, создать отдельный класс резолвера который гибче в конфигурации но больше не будет наследоваться от SessionLocale поскольку не сможет повторять его контракт.
После чего я выпущу версию 1.0.
Но все эти изменения уже будут обратно совместимы.
Из новых фич я планирую только сделать возможность интеграции с спринг секьюрити, таким образом чтобы при входе на сайт пользователю устанавливалась его локаль из настроек.
Но это будет ещё один отдельный плагин.

Пока же этого можно добиться создав своего слушателя события авторизации.
Для этого создайте такой класс:

/**
 * Setting Grails/Spring Locale during Spring Security login
 * http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/single.html#events
 **/
class UserAuthListener implements ApplicationListener<AuthenticationSuccessEvent> {

    @Override
    void onApplicationEvent(AuthenticationSuccessEvent event) {
        User user = User.findByUsername(event.authentication.name)
        if (user?.locale) {
            setUserPreferredLocale(user)
        }
    }

    void setUserPreferredLocale(User user) {
        LocaleContextHolder.setLocale(user.locale)
        HttpServletRequest request = SecurityRequestHolder.request
        HttpServletResponse response = SecurityRequestHolder.response
        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request)
        localeResolver.setLocale(request, response, user.locale)
    }
}

Этот класс ожидает что у класса пользователя User есть поле Locale locale а также ищет пользователя по полю username. Проверьте что у вас такие же настройки.

И пропишите создание бина этого класса в resources.groovy

beans = {
    ...
    userAuthListener(UserAuthListener)
    ...
}

Всё, теперь после успешной авторизации пользователь получить свою любимую локаль.

Но учтите, если пользователь переключит локаль через lang параметр в запросе, т.е.:

http://localhost:8080/page/1?lang=es

..то локаль переключится, это даже сохранится в сессию, но это не сохранится в настройках пользователя. Для этого есть два решения:

  • либо бог с ним, просто создать в личном кабинете пользователя опцию где бы он переключал локаль
  • Писать Interceptor который бы проверял этот параметр и сохранял в настройки пользователя. Но делать это придётся строго после выполнения стандартного ParamsAwareLocaleChangeInterceptor, потому что точная локаль может не совпадать с той что запросил пользователь. Например если пользователь запросил en_US но мы поддерживаем только en то поставится en. Можно ли выставлять интерцепторы в определённом порядке это ещё предстовить выяснить.

Кстати про ParamsAwareLocaleChangeInterceptor — именно этот интерцептор наследует спринговый LocaleChangeInterceptor но работает также. Правда с каким то небольшим приколом: он может принимать список локалей, но берёт всегда всё равно первую. Т.е. он может хавать такие запросы:

http://localhost:8080/page/1?lang=es&lang=it

Но возмёт первую локаль, т.е. es. Нафиг такое надо — непонятно, и что самое интересное нигде и не используется и не описано в документации. Пока создал тикет с предложением его депрекейтнуть и удалить этот класс за ненадобностью.

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s