Языки. Локаль

Кодовое обозначение локали

Чтобы различать локали, принято использовать следующие кодовые обозначения:

[язык]-[СТРАНА]
  • [язык] — двухбуквенный код языка по стандарту ISO 639-1.
  • [СТРАНА] — двухбуквенный код страны по стандарту ISO 3166-1.

Например, ru-RU (Русский язык в России) или uk-UA (Украинский язык в Украине).

Один язык в нескольких странах

Один язык может использоваться в нескольких странах. Например, для английского языка в Британии и США используют кодовые обозначения en-GB и en-US соответственно.

В контексте локализации различие языка заключается в написании одного и того же слова, а иногда — формата дат.

en-GBen-USru-RU
Upload cancelled.Upload canceled.Загрузка отменена.
Invitation to a dialogue.Invitation to a dialog.Приглашение к диалогу.
Due date 31.01.20Due date 01.31.20Срок до 31 января 2020

Несколько языков внутри одной страны

Wasaby Framework позволяет поддерживать несколько языков внутри одной страны. Например, для России можно создать словарь как на русском, так и на татарском языке. Такой функционал даёт преимущество Wasaby Framework в том, что можно создавать продукт с возможностью выбора локализации веб-приложения на негосударственный язык (язык меньшинства).

Язык ключей перевода

Не имеет значения какой язык используется в исходных файлах. Строки могут быть как на русском, так и на украинском или китайском языках. При локализации в Genie в окне назначения переводов будут отображены все строки, найденные в исходных файлах. Они будут соответствовать локали, установленной для веб-приложения (при конвертации ресурсов проекта или развороте отладочного стенда) по умолчанию.

Для изменения текущего языка веб-приложения, как правило, используют меню выбора языка. Для крупных веб-приложений, например online.sbis.ru, оно уже создано. Такое меню выполняет, как правило, два действия:

  1. Устанавливает cookie с названием lang (см. Как устанавливается cookie с названием lang).
  2. Устанавливает язык в настройках профиля пользователя.
  3. Перезагружает веб-страницу.

Ниже приведён пример кода, изменяющего локаль в cookie:

define(
   ...
   [
      ... ,
      'Core/i18n'
   ],
   function(... , i18n) {
      ...
      this.getChildControlByName('УстановитьУкраинскийЯзык').subscribe('onClick', function() {
          
         // Устанавливаем локализацию на украинский язык
         i18n.setLang('uk-UA');
          
         // Перезагружаем страницу
         window.location = '/';
          
         // альтернативный способ перезагрузки страницы
         // window.location.reload();
      });
   }
);

Язык, на котором пользователь увидит интерфейс, определяется по содержимому cookie с названием lang. Значение lang устанавливается по следующим правилам:

  1. В функции init прикладной разработчик устанавливает значение lang с помощью метода save статического класса Configuration, экспортируемого из библиотеки I18n/i18n.
  2. Если в init этого не производилось, то механизм синглтона производит поиск параметра lang в запросе к странице (адресная строка веб-браузера). Если параметр найден, то его значение будет установлено в качестве cookie lang.
  3. Если параметр lang отсутствует, то в cookie lang устанавливается кодовое обозначение языка веб-браузера пользователя.

Механизм получения языка

В приложениях СБИС выбор языка основывается на настройках профиля авторизованного пользователя. В зависимости от типа приложения механизмы получения языка могут варьироваться.

Глобально можно выделить две стратегии получения языка:

  • Из настроек профиля авторизованного пользователя.
  • Из настроек системы, если не нет доступа к профилю.

Приложения без аутентификации пользователей используют язык системы. Например, showcase, отправщик SMS.

Для web-приложений.

  • Из настроек профиля авторизованного пользователя, через провайдер языка посредством транспорта cookie lang.
  • Из настроек браузера, если нет доступа к профилю.

Для offline-приложений существует две базовых стратегии получения языка:

  • Из настроек профиля авторизованного пользователя через провайдер языка.
  • Из настроек ОС, если нет доступа к профилю.

Для приложения SBIS3 Plugin существует специфичный сценарий. В приложении одновременно может быть авторизовано несколько пользователей, которые принадлежат разным профилям. В таком случае настройки языка задаются настройками приложения.

Реализация провайдера языка

Для создания собственного провайдера языка необходимо реализовать интерфейс IAppLanguageProvider и зарегистрировать его в фабрике обработчиков.

  1. Создайте класс-наследник IAppLanguageProvider и переопределите у него метод ResolveCurrentLanguage().
    Пример реализации провайдера, который возвращает русский язык.
    class MyProvider : public IAppLanguageProvider
    {
    private:
       String ResolveCurrentLanguage() const noexcept override
       {
          return L"ru-RU";
       }
    };
  2. Зарегистрируйте в фабрике обработчиков. Пример регистрации обработчика языка.
    Factory::TemplateItem< IAppLanguageProvider, MyProvider > LANG ( GetLanguageProviderFactory() );

Определение языка по настройкам браузера без доступа к профилю пользователя

Предпочитаемый язык определяется по заголовку "Accept-Language". Если заголовок не задан, то языком страницы объявляется Английский — 'en'.

Формальное описание грамматики заголовка:

Accept-Language = "Accept-Language" ":"
                      1#( language-range [ ";" "q" "=" qvalue ] )
    language-range  = ( ( 1 * 8ALPHA *( "-" 1 * 8ALPHA ) ) | "*" )
    qvalue          = ( "0" [ "." 0 * 3DIGIT ] )
                    | ( "1" [ "." 0 * 3("0") ] )

Подробнее о заголовке читайте здесь:

Пример: Accept-Language: da, en-GB; q = 0.8, fr; q = 0.7

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

Рассмотрим внимательно синтаксис этого выражения:

<Заголовок>: <предпочитаемый язык>, <альтернативный язык>; <коэффициент предпочтительности q>

Для предпочитаемого языка коэффициент не указывается — что q=1.

Алгоритм выбора языка

  1. Разделяем переданный заголовок по "весам".
  2. Сортируем полученные наборы в порядке уменьшения "весов".
  3. Обходим полученные наборы языков в порядке уменьшения "весов".
  4. Для каждого языка ищем соответсвующий ему словарь:
    • Если словарь есть, то этот язык объявляем "текущим", и завершаем цикл и возвращаем его.
    • Если словаря нет, язык задан с уточнением региона и "наиболее подходящий" язык не определен.
      • Ищем словарь без учета региона. Если нашли словарь, то сохраняем его как "наиболее подходящий".
      • Иначе возвращаем 'en' — английский.

Пример реализации алгоритма для выбора предпочитаемого языка

langs = parse( headers['Accept-Language'] )
prefer = None
for lang in langs:
    if dict_exists( lang ):
        return lang  # Ищем точное совпадение словаря и переданного языка.
    delim = lang.find('-')
    if delim != -1 and prefer is None and dict_exists( lang[ :delim ] ):
        prefer = lang[ :delim ]  # Наиболее подходящим языком объявляем язык с наибольшим весом без учета региона.
if prefer:
    return prefer
else:
    return 'en'

Получение текущей локали

  • JavaScript
define('MyControl', ['UI/Base', 'I18n/i18n'], function(Base, i18n) {
   var ModuleClass = Base.Control.extend({
      _template: template,
      _beforeMount: function() {
          
         // Вернет строку в формате [язык]-[СТРАНА], где
         // [язык] — двухбуквенный код языка по стандарту ISO 639-1.
         // [СТРАНА] — двухбуквенный код страны по стандарту ISO 3166-1.
         this._locale = i18n.Configuration.load();
      }
   });
   return ModuleClass;
});
  • Python
from sbis import *
# Вернет строку в формате [язык]-[СТРАНА], где
# [язык] — двухбуквенный код языка по стандарту ISO 639-1.
# [СТРАНА] — двухбуквенный код страны по стандарту ISO 3166-1.
# Если параметры языка не заданы, то возвращает ru-RU.
lang_str = CurrentLanguageIsoCode()
  • C++
#include <sbis-lib/forward/string.hpp>
#include <sbis-lib/utils/rk.hpp>
 
/**
* Вернет ISO code текущей локали в формате [язык]-[СТРАНА], где
* [язык] — двухбуквенный код языка по стандарту ISO 639-1.
* [СТРАНА] — двухбуквенный код страны по стандарту ISO 3166-1.
* Если не заданы параметры языка, то возвращает ru-RU.
*/
sbis::String lang = sbis::CurrentLanguageIsoCode();