Пользователь
0,0
рейтинг
23 сентября 2014 в 17:09

Разработка → Локализация с AngularJS из песочницы

imageДобрый день, уважаемые Хабражители.

AngularJS — отличный Framework для создания ваших сайтов. На Хабре уже достаточно много про него написано, но почему то ни разу не затрагивалась тема локализации приложений. Тем не менее, поддержка локализации в нем есть и сегодня мы попробуем с ней разобраться.

Существует несколько способов локализации и мы рассмотрим несколько из них.

Способ 1. Как в документации


В документе i18n and l10n указано, что Angularjs поддерживает отображение дат, чисел и валют в том виде, в котором их привыкли видеть пользователи соответствующих стран. Это означает, что при создании интернет магазина мы можем один раз написать {{amount | currency}}, подключить необходимую локаль и не думать о том, как будет выглядеть сумма покупки для России и США. AngularJS сам сделает это за вас:

1 234,56 руб — в России
$1,234.56 — в США


Аналогичная ситуация и с датами. Можно написать {{currentDate | date}} и мы увидим:

23 сент. 2014 г. — в России
Sep 23, 2014 — США


Если же необходимо использовать обе валюты на одной странице, то можно временно переопределить обозначение, указав явно единицу измерения {{amount | currency:"USD$"}}.
Пример работы с валютами и датами
<!doctype html>
<html ng-app="testApp">
<head>
  <meta charset="utf-8">
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.2/angular.js"></script>
  <!-- Оставьте только одно включение локали -->
  <!-- Локаль США -->
  <!-- <script src="https://code.angularjs.org/1.3.0-rc.2/i18n/angular-locale_en-us.js"></script>-->
  <!-- Русская локаль -->
  <script src="https://code.angularjs.org/1.3.0-rc.2/i18n/angular-locale_ru-ru.js"></script>
  <script>
    angular.module('testApp', [])
      .controller('mainController', ['$scope', function($scope){
        $scope.amount = 1234.56;
        $scope.currentDate = new Date();
    }])
  </script>
</head>
<body ng-controller="mainController">
  <input type="number" ng-model="amount"> <br>
  Стандартное обозначение валюты: <span id="currency-default">{{amount | currency}}</span><br>
  Определяем своё обозначение валюты (USD$): <span>{{amount | currency:"USD$"}}</span><br>
  Текущая дата: <span>{{currentDate | date}}</span>
</body>
</html>

Пример на Plunker.

Для некоторых преобразований текста, разработчики рекомендуют использовать ngPluralize, но для простой локализации приложения это нам не сильно поможет.

Способ 2. Велосипед


Предположим, что у нас есть два файла с переводами, хранящиеся в формате json.
translation_ru.json
{  
    "COLOR" : "Цвет",
    "HELLO" : "Привет",
    "HELLO_HABR" : "Привет Хабрахабр!"
}


translation_en.json
{  
    "COLOR" : "Color",
    "HELLO" : "Hello",
    "HELLO_HABR" : "Hello Habrahabr!"
}


И мы хотим, что бы на странице отображались эти сообщения в зависимости от того, какой язык выберет пользователь. Для этого, создадим приложение myApp.
angularApp.js
var app = angular.module('myApp', ['ngResource']);



И сервис translationService, который будет загружать нам эти файлы.
angularTranslationService.js
app.service('translationService', function($resource) {  

        this.getTranslation = function($scope, language) {
            var languageFilePath = 'translation_' + language + '.json';
            console.log(languageFilePath);
            $resource(languageFilePath).get(function (data) {
                $scope.translation = data;
            });
        };
    });


Так же, создадим контроллер myController, к которому подключим данный сервис:
angularController.js
app.controller('myController',['$scope', 'translationService', 
function ($scope, translationService){  

  //Выполняем перевод, если произошло событие смены языка
  $scope.translate = function(){
       translationService.getTranslation($scope, $scope.selectedLanguage);
   };
   // Инициализация
   $scope.selectedLanguage = 'en';
   $scope.translate();
  
}]);


И само тестовое предоставление index.html, в котором подключаем все скрипты.
Для того, чтобы вывести текст перевода, необходимо описать его следующим образом:

{{translation.HELLO_HABR}}


Прошу обратить внимание, что помимо angular.js мы подключаем еще и angular-resource.js для работы с ресурсами.
index.html
<!DOCTYPE html>
<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.2/angular.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.2/angular-resource.min.js"></script>
  <script src="angularApp.js"></script>
  <script src="angularController.js"></script>
  <script src="angularTranslationService.js"></script>
</head>

<body>
  <div ng-app="myApp" ng-controller="myController">
    <section>
      <h4>Select language for translation:</h4>
      <select ng-change="translate()" ng-model="selectedLanguage">
        <option value="en">English</option>
        <option value="ru">Русский</option>
      </select>
    </section>

    <h4>Выбранный язык: <strong>{{selectedLanguage}}</strong></h4>
    <h4>Перевод слов:</h4>
    <p>
      {{translation.HELLO_HABR}}
    </p>
  </div>
</body>
</html>



Получаем результат:



Рабочий пример вы можете посмотреть здесь.

Способ 3. Правильный


Что же предлагает нам мировая общественность для решения вопроса локализации? Я нашел только два крупных проекта с хорошей документацией. Это Angular-Translate и Angular-Gettext.
Пример использования Angular-Translate
var app = angular.module('at', ['pascalprecht.translate']);

app.config(function ($translateProvider) {
  $translateProvider.translations('en', {
    TITLE: 'Hello',
    FOO: 'This is a paragraph.',
    BUTTON_LANG_EN: 'english',
    BUTTON_LANG_DE: 'german'
  });
  $translateProvider.translations('de', {
    TITLE: 'Hallo',
    FOO: 'Dies ist ein Paragraph.',
    BUTTON_LANG_EN: 'englisch',
    BUTTON_LANG_DE: 'deutsch'
  });
  $translateProvider.preferredLanguage('en');
});

app.controller('Ctrl', function ($scope, $translate) {
  $scope.changeLanguage = function (key) {
    $translate.use(key);
  };
});


Пример использования Angular-Gettext
angular.module("myApp").controller("MyCtrl", function ($scope, gettextCatalog) {
    // Do things with gettextCatalog
});


Лично мне симпатизирует Angular-Gettext. Перевод в нем осуществляется в несколько шагов:

  1. Создаем сайт полностью на одном языке;
  2. Приписываем в html слово translate возле тех строк, которые необходимо перевести;
  3. Используем angular-gettext tools для генерации шаблона «po»;
  4. Переводим на все необходимые нам языки используя Poedit. Хотя лучше — отдать переводить профессионалам;
  5. Подключаем gettext и шаблоны;
  6. Используем.


С Angular-Translate придется писать все переводы в файлах.

Заключение


Сегодня мы изобрели велосипед в учебных целях. Но использовать такой код в реальных проектах нельзя. Использование проработанных библиотек — это хорошо. Какую из них выбрать — решать вам. Каждая заслуживает отдельной статьи и имеет свои особенности. В следующих постах, опишу каждую из них подробнее.

Спасибо за внимание и удачных вам разработок и локализаций!

Ссылки


Файлы поддержки локалей для версии 1.3.0-rc2 Вы можете найти здесь;
1-й пример на Plunker;
2-й пример на Plunker;
Angular-Gettext;
Angular-Translate.
Алексей Ревин @cdmax2002
карма
26,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (10)

  • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Это означает, что при создании интернет магазина мы можем один раз написать {{amount | currency}}, подключить необходимую локаль и не думать о том, как будет выглядеть сумма покупки для России и США. AngularSJ сам сделает это за вас:

    1 234,56 руб — в России
    $1,234.56 — в США

    Это конечно круто, белорусы будут рады.
    А если серьезно — как в реальности работает этот механизм в angular?
    • +1
      Это обычный ангуляровский фильтр docs.angularjs.org/api/ng/filter/filter, не понимаю зачем автор поста делает решение через костыль. Ну и задача локализации гораздо сложнее, есть сортировки для ng-repeat, есть множестенные формы, есть значение по умолчанию, есть места где надо сразу вывести текст в двух локалях, ну и главная проблема — seo, локаль нужно брать из урла, а не писать в конфиге.

      Ну, автор написал, что это велосипед, но непонятно, зачем такое демонстрировать, если он приносит больше проблем, чем поясняет или помогает в задаче.
  • 0
    Дополню ссылкой на www.npmjs.org/package/ng-babelfish
  • +2
    Пусть здесь полежит.

    Gulp plugin for parsing angular templates with angular-gettext attributes (translate) and replacing contents with translated .po files passed as an argument. It allows to translate templates as much as possible during build process and reduce the amount of work each browser does during templates rendering.
  • 0
    Приписываем в html слово translate возле тех строк, которые необходимо перевести;

    Это чревато проблемами в будущем:
    — невозможно одну и ту же строку перевести по-разному. Напр.: login = вход = войдите
    — правка строки вынуждает править ключи во всех переводах. Напр.: Hello {{name}} стало Hello {{user.name}}
    — далеко не все переводы содержатся в шаблонах. Сообщения ошибок и т.п. часто берутся из js

    Angular-Translate так же имеет грунтовый плагин, выдирающий строки с ключами из кода. А так же богатое АПИ и документацию на русском. Я бы пристальнее к нему присмотрелся ;-)
  • +1
    1. Как перевести атрибуты элемента? (
    <img src="" title="{{title}}"/>
    
    будет работать?)
    2. Как автоматически переключить стили? (замена CSS файлов)
    3. Можно ли накладывать пачку json`ов? Например, сначала грузим:
    {
        name: "Имя" 
    }

    Потом грузим:
    {
        family: "Фамилия"
    }

    И получаем на выходе конфиг:
    {
        name: "Имя" 
        family: "Фамилия"
    }

    4. Можно ли задавать иерархию и вложения? Например:
    {
        name: {
            first: "Миша"
            last: "Катя"
        }
        family: "Фамилия"
    }
    
    {{ name.first }}
    

    5. Если у нас ключ перевода привязан к DOM, значит он сохранит ссылку на элемент в памяти. Если мы удалим DOM элемент у нас будет утечка памяти? Было:
    <div><div>{{name}}</div></div>
    

    Стало:
    <div></div>
    

    Система же локализации не оставит у себя ссылку на удаленный элемент?
  • 0
    У меня вопрос, касающийся как локализации на фронте в частности, так и ко всей идеи разворачивания приложения в браузере, в целом. вместо «старомодного» отдавания готовой страницы с бекенда.
    Давно интересовалися темой: научились ли поисковики это дело красиво индексировать?
    Помнится пару тройку лет назад из-за отрицательного ответа на этот вопрос была похоронена не одна идея «одностраничного» приложения…
    • 0
      Не научились и не собираются и правильно делают. Изменение содержания должны отражаться в URL и именно страницу в таком виде должен отдавать сервер.
  • 0
    [зануда]
    ['$scope', 'translationService', function ($scope, translationService){

    Объясните мне — зачем вы так делаете?
    Еще есть мнение что way «app = angular.module» сейчас не является разработчиками рекомендуемым
    [/зануда]

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.