Статьи

Многоязычная поддержка AngularJS

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

Мы создадим одностраничное приложение, которое требует многоязычной поддержки с несколькими языками, используя AngularJS, чтобы пользователь мог мгновенно переключаться между языками, не обновляя страницу. В этом случае нам нужно сделать больше вещей с нашим приложением, включая перевод его текста, мгновенное переключение между различными языками или изменение направления компоновки (с RTL на LTR).

Весь код, разработанный в этой статье , доступен на GitHub .

Настройка среды

В примере, который я собираюсь показать, я буду использовать Bower и Gulp, чтобы сделать нашу среду разработки максимально автоматизированной и гибкой. Если они еще не установлены в вашей системе или вы никогда не использовали их в своем рабочем процессе разработки, я настоятельно рекомендую установить и начать больше о них узнавать. Вот список статей, которые могут быть полезны для этой цели:

В качестве первой задачи давайте настроим Bower, запустив bower init в командной строке внутри каталога проекта, который мы назовем multilingualwithangular . bower init интерактивном режиме создаст файл манифеста с именем bower.json который будет содержать некоторую информацию о проекте, а также список ранее установленных зависимостей bower.json интерфейса.

Следующим шагом является установка начальных необходимых пакетов.

 bower install angular angular-translate --save 

Давайте настроим Gulp и установим эти основные пакеты. Сначала нам нужно выполнить команду npm init и выполнить несколько простых шагов, чтобы создать файл package.json который будет содержать некоторую информацию о проекте и о том, как управлять модулями Node.js.

Далее мы установим Gulp в рамках проекта:

 npm install gulp --save-dev 

Нам также понадобятся некоторые зависимости Gulp для JavaScript, Sass и других инструментов автоматизации.

 npm install gulp-sass gulp-uglify gulp-concat run-sequence browser-sync --save-dev 

На этом этапе нам нужно создать пустой gulpfile.js конфигурации gulpfile.js в каталоге проекта. Он будет использоваться для определения наших задач Gulp, таких как JavaScript и Sass. Вы можете посмотреть полный файл конфигурации в моем репозитории GitHub .

В задачу JavaScript мы добавим два файла, angular и angular-translate , а также основной файл JavaScript в каталоге /js . Затем мы объединяем их вместе и используем библиотеку для Node.js, которая называется Uglify, чтобы сжать и уменьшить размер нашего файла.

 'use strict'; var gulp = require('gulp'); var sass = require('gulp-sass'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var runSequence = require('run-sequence'); var browserSync = require('browser-sync'); gulp.task('js', function(){ return gulp.src([ './bower_components/angular/angular.js', './bower_components/angular-translate/angular-translate.js', './js/app.js']) .pipe(concat('app.min.js')) .pipe(uglify()) .pipe(gulp.dest('./js')) }); gulp.task('serve', function() { browserSync({ server: { baseDir: "./" } }); }); gulp.task('build', [], function() { runSequence('js'); }); gulp.task('default', ['build'], function() {}); 

После этого мы можем запустить задачу gulp build gulp, которую мы создали ранее. Он запустит задачу js а затем сгенерирует файл /js/app.min.js который будет включен в простой файл HTML.

 <!DOCTYPE HTML> <html> <head> <title>Multilingual AngularJS</title> <meta charset="utf-8"> </head> <body> <script src="js/app.min.js"></script> </body> </html> 

Чтобы открыть проект в среде localhost, запустите gulp serve после чего автоматически откроется вкладка браузера, ориентированная на localhost: 3000 .

Добавление перевода с помощью Angular-Translate

Выполнив эти первые задачи по настройке, пришло время сделать шаг вперед и добавить поддержку перевода для текста приложения. Мы будем работать с арабским и английским как нашими основными языками. Это совершенно разные языки в отношении грамматики, синтаксиса и направлений письма (арабский справа налево и английский слева направо).

angular-translate — это модуль AngularJS, который мы можем использовать для перевода текста. Он предоставляет множество интересных функций, таких как фильтры, директивы и асинхронная загрузка данных i18n.

Прежде всего, давайте настроим AngularJS и настроим его с помощью angular-translate

 // js/app.js var app = angular.module('Multilingual', ['pascalprecht.translate']); app.config(['$translateProvider', function($translateProvider) { $translateProvider .translations('ar', { 'HELLO': 'مرحبا' }) .translations('en', { 'HELLO': 'Hello' }) .preferredLanguage('ar'); }]); 

Затем давайте немного изменим HMTL:

 <html ng-app="Multilingual"> 

Затем запустите gulp build из командной строки, чтобы внести новые изменения в файл JavaScript. В предыдущем фрагменте кода мы имеем:

  • Создан угловой модуль под названием Multilingual .
  • Внедрил модуль angular-translate в качестве зависимости в наше приложение как pascalprecht.translate .
  • Внедрил $translateProvider в метод .config() .
  • Регистрировали таблицы перевода на разных языках, используя метод .translations() и устанавливая языковой ключ, такой как en или ar в качестве первого параметра.
  • Установите предпочитаемый язык с помощью .preferredLanguage() (это важно, поскольку мы используем более одного языка, поэтому мы можем научить angular-translate какой использовать при первой загрузке).

Давайте посмотрим пример angular-translate с использованием фильтра translate

 <h2>{{ 'HELLO' | translate }}</h2> 

Наличие слишком большого количества фильтров в представлении устанавливает слишком много выражений наблюдения, как описано в документации по директиве перевода . Лучший и более быстрый способ реализовать это — использовать директиву translate . Другая причина использования этой директивы заключается в том, что есть вероятность, что пользователь увидит необработанный текст {{ 'HELLO' | translate }} {{ 'HELLO' | translate }} перед тем, как наш шаблон рендерится AngularJS во время загрузки.

Мы можем использовать директиву, чтобы передать идентификатор перевода в качестве значения атрибута директивы translate .

 <h2 translate="HELLO"></h2> 

Иногда нам может понадобиться узнать, пропустили ли мы некоторые идентификаторы перевода. angular-translate-handler-log помогает нам решить эту проблему, предоставляя очень хороший метод useMissingTranslationHandlerLog() который записывает предупреждения в консоль для любого отсутствующего идентификатора перевода. Чтобы использовать его, мы должны сначала установить его. Вы можете сделать это с Бауэром:

 bower install angular-translate-handler-log --save 

Затем обновите задачу JavaScript Gulp:

 gulp.task('js', function(){ return gulp.src([ './bower_components/angular/angular.js', './bower_components/angular-translate/angular-translate.js', // New file './bower_components/angular-translate-handler-log/angular-translate-handler-log.js', './js/app.js']) .pipe(concat('app.min.js')) .pipe(uglify()) .pipe(gulp.dest('./js')); }); 

Наконец, запустите gulp build используя этот метод непосредственно на $translateProvider как:

 $translateProvider .translations('ar', { 'HELLO': 'مرحبا' }) .translations('en', { 'HELLO': 'Hello' }) .preferredLanguage('ar') .useMissingTranslationHandlerLog(); 

Если мы пропустили перевод для HELLO , благодаря этому методу мы получим предупреждающее сообщение с надписью «Перевод для HELLO не существует».

Загрузка файлов перевода асинхронно

Вместо добавления данных перевода для разных языков непосредственно в методе .config() , есть другой способ загрузить их в асинхронной и отложенной загрузке. На самом деле, есть несколько способов решения этой задачи , но в этом уроке мы будем использовать только расширение angular-translate-loader-static-files .

Для начала нам нужно установить расширение с помощью Bower:

 bower install angular-translate-loader-static-files --save 

После установки нам нужно обновить задачу Gulp, указав путь к файлу расширения, а затем запустить gulp build .

 gulp.task('js', function(){ return gulp.src([ './bower_components/angular/angular.js', './bower_components/angular-translate/angular-translate.js', './bower_components/angular-translate-handler-log/angular-translate-handler-log.js', // New file 'bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js', './js/app.js']) .pipe(concat('app.min.js')) .pipe(uglify()) .pipe(gulp.dest('./js')); }); 

На этом этапе нам нужно создать каталог /translations и добавить файлы переводов языков. Структура будет выглядеть следующим образом:

 translations ├── ar.json └── en.json 

Внутри файла ar.json напишите содержание, указанное ниже:

 { "HELLO": "مرحبا", "BUTTON_LANG_AR": "العربية", "BUTTON_LANG_EN": "الإنجليزية", "WELCOME_MESSAGE": "مرحباً في موقع AngularJS المتعدد اللغات" } 

Напротив, в файле en.json сохраните следующий контент:

 { "HELLO": "Hello", "BUTTON_LANG_AR": "Arabic", "BUTTON_LANG_EN": "English", "WELCOME_MESSAGE": "Welcome to the AngularJS multilingual site" } 

Теперь мы можем использовать метод useStaticFilesLoader чтобы сообщить angular-translate какие языковые файлы загружать, используя определенный шаблон, используя следующий подход:

 prefix - specifies file prefix suffix - specifies file suffix 

А вот как меняется файл JavaScript:

 // js/app.js app.config(['$translateProvider', function($translateProvider) { $translateProvider .useStaticFilesLoader({ prefix: '/translations/', suffix: '.json' }) .preferredLanguage('ar') .useMissingTranslationHandlerLog(); }]); 

Если мы хотим добавить префикс к файлам, мы можем переименовать каждый из них, используя префикс (в данном случае, locale- ):

 translations ├── locale-ar.json └── locale-en.json 

app.js это изменение, мы должны обновить файл app.js следующим образом:

 // js/app.js app.config(['$translateProvider', function($translateProvider) { $translateProvider .useStaticFilesLoader({ prefix: '/translations/locale-', suffix: '.json' }) .preferredLanguage('ar') .useMissingTranslationHandlerLog() }]); 

Здесь angular-translate объединит наш код как {{prefix}}{{langKey}}{{suffix}} , а затем, например, загрузит файл /translations/locale-en.json .

Переключение между разными языками

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

 <div ng-controller="LanguageSwitchController"> <button ng-show="lang == 'en'" ng-click="changeLanguage('ar')" translate="BUTTON_LANG_AR"></button> <button ng-show="lang == 'ar'" ng-click="changeLanguage('en')" translate="BUTTON_LANG_EN"></button> </div> 

Мы также можем создать некоторые свойства $rootScope и использовать их в нашем HTML-коде, чтобы установить начальное направление макета и атрибут lang при первой загрузке, связывая их позже при каждом изменении языка.

 // js/app.js app.run(['$rootScope', function($rootScope) { $rootScope.lang = 'en'; $rootScope.default_float = 'left'; $rootScope.opposite_float = 'right'; $rootScope.default_direction = 'ltr'; $rootScope.opposite_direction = 'rtl'; }]) 

angular-translate предоставляет удобный метод с именем use который принимает параметр и устанавливает для нас язык на основе переданного параметра. Кроме того, мы будем слушать событие $translateChangeSuccess , которое вызывается после успешного изменения перевода, чтобы убедиться, что язык изменился. Затем мы можем изменить свойства $rootScope на основе выбранного языка:

 // js/app.js app.controller('LanguageSwitchController', ['$scope', '$rootScope', '$translate', function($scope, $rootScope, $translate) { $scope.changeLanguage = function(langKey) { $translate.use(langKey); }; $rootScope.$on('$translateChangeSuccess', function(event, data) { var language = data.language; $rootScope.lang = language; $rootScope.default_direction = language === 'ar' ? 'rtl' : 'ltr'; $rootScope.opposite_direction = language === 'ar' ? 'ltr' : 'rtl'; $rootScope.default_float = language === 'ar' ? 'right' : 'left'; $rootScope.opposite_float = language === 'ar' ? 'left' : 'right'; }); }]); 

А также примените следующее изменение к разметке:

 <html lang="{{ lang }}" ng-app="Multilingual"> 

В моей статье « Использование вспомогательных классов для сушки и масштабирования CSS» вы можете увидеть еще один пример использования этих свойств направления в HTML в качестве вспомогательных классов:

 <div class="text-{{ default_float }}"></div> 

Помни язык

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

Мы научим наше приложение запоминать язык, используя браузер localStorage для хранения выбранного языка, и будем использовать расширение angular-translate-storage-local для этой цели. Как вы можете себе представить, следующим шагом является его установка. Мы сделаем это с Бауэром:

 bower install angular-translate-storage-local --save 

Запустив эту команду, мы также установим angular-cookies и angular-translate-storage-cookie качестве зависимостей. После установки нам нужно обновить задачу Gulp новыми файлами, выполняющими gulp build :

 gulp.task('js', function(){ return gulp.src([ './bower_components/angular/angular.js', './bower_components/angular-translate/angular-translate.js', './bower_components/angular-translate-handler-log/angular-translate-handler-log.js', 'bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js', // New files './bower_components/angular-cookies/angular-cookies.js', './bower_components/angular-translate-storage-cookie/angular-translate-storage-cookie.js', './bower_components/angular-translate-storage-local/angular-translate-storage-local.js', './js/app.js']) .pipe(concat('app.min.js')) .pipe(uglify()) .pipe(gulp.dest('./js')); }); 

С этим кодом, следующие шаги:

  • Добавление ngCookies в качестве зависимости.
  • Указание $translateProvider использовать localStorage с помощью useLocalStorage()

Вот как мы должны действовать:

 var app = angular.module('Multilingual', [ 'pascalprecht.translate', 'ngCookies' ]); app.config(['$translateProvider', function($translateProvider) { $translateProvider .useStaticFilesLoader({ prefix: '/translations/', suffix: '.json' }) .preferredLanguage('ar') .useLocalStorage() .useMissingTranslationHandlerLog() }]); 

angular-translate сохранит исходный язык, который мы установили посредством NG_TRANSLATE_LANG_KEY preferredLanguage() с ключом NG_TRANSLATE_LANG_KEY . Он назначит язык в качестве значения в браузере localStorage и будет обновлять его каждый раз, когда пользователь переключает язык. Когда пользователь открывает приложение, angular-translate извлекает его из localStorage.

Локальное хранилище

Работа с направлением макета

Мы дошли до презентационной части. Если вы работаете с двумя языками с одинаковыми направлениями письма (например, английский и французский), настройка завершена. Если одно из языковых направлений — RTL, а другое — LTR, нам нужно проделать дополнительную работу для настройки некоторых сценариев компоновки.

Допустим, это код CSS для языка LTR (английский):

 .media-image { padding-right: 1rem; } 

Когда дело доходит до языка RTL, приведенный выше код должен быть зеркально отображен так, чтобы он был padding-left а не padding-right :

 .media-image { padding-left: 1rem; } 

Тем не менее, это не очень хорошая практика, так как требует много времени и повторений кода:

 [lang='ar'] .media-image { padding-right: 0; padding-left: 1rem; } 

Чтобы решить эту проблему, нам нужно написать некоторый код CSS и обеспечить эффективную, автоматизированную и динамическую поддержку как языка RTL, так и языка LTR. При таком подходе нам не придется повторять или переопределять правила CSS. Я рекомендую вам прочитать мою статью под названием « Управление RTL CSS с помощью Sass и Grunt», чтобы узнать больше об этой технике и о том, как использовать ее в своих проектах.

Мы реализуем это в этом руководстве с использованием Gulp и добавлением задачи Sass, которая принимает ltr-app.scss и rtl-app.scss . Мы импортируем основной файл Sass в дополнение к указанным в них переменным направления:

 gulp.task('sass', function () { return gulp.src(['./sass/ltr-app.scss', './sass/rtl-app.scss']) .pipe(sass()) .pipe(gulp.dest('./css')); }); // Update the build task with sass gulp.task('build', [], function() { runSequence('js', 'sass'); }); 

Файл sass/ltr-app.scss должен быть следующим:

 // LTR language directions $default-float: left; $opposite-float: right; $default-direction: ltr; $opposite-direction: rtl; @import 'style'; 

И это код sass/rtl-app.scss :

 // RTL language directions $default-float: right; $opposite-float: left; $default-direction: rtl; $opposite-direction: ltr; @import 'style'; 

Наконец, это пример того, как выглядит sass/style.scss :

 body { direction: $default-direction; } .column { float: $default-float; } .media-image { padding-#{$opposite-float}: 1rem; } 

Имея весь этот код, вы можете запустить gulp build и задача Sass сгенерирует два файла. css/rtl-app.css будет иметь код, указанный ниже:

 /* css/rtl-app.css */ body { direction: rtl; } .column { float: right; } .media-image { padding-left: 1rem; } 

Файл css/ltr-app.css будет содержать содержимое, указанное ниже:

 /* css/ltr-app.css */ body { direction: ltr; } .column { float: left; } .media-image { padding-right: 1rem; } 

Следующим и последним шагом является динамическое использование этих сгенерированных файлов на основе текущего языка. Мы будем использовать $rootScope default_direction в $rootScope чтобы задать направление при первой загрузке, а затем привязать его при смене языка.

 <link ng-href="css/{{ default_direction }}-app.css" rel="stylesheet"> 

Выводы

Как мы уже видели, использование angular-translate — это путь, когда дело доходит до перевода AngularJS. Он предлагает множество удобных фильтров, директив и интересных инструментов для использования. Мы рассмотрели процесс перевода по-разному, изучая, как переключаться между двумя языками. Мы также обсудили, как сохранить выбранный язык в хранилище браузера пользователя и как работать с CSS, чтобы сделать уровень представления более отзывчивым к языковым указаниям.

Я надеюсь, вам понравился этот урок. Я создал репозиторий GitHub для этой статьи, и вы можете проверить код здесь . Не стесняйтесь делиться своими комментариями в разделе ниже.