Последние записи в блоге

Работа с Intl

Автор: Raymond Camden
Дата: 12 May 2014

Интернационализация – это та вещь, о которой мы постоянно слышим в разговорах разработчиков, но очень редко встречаем ее реальное, практическое применение, и это та самая вещь которая получает хороший пинок с выходом нового ECMAScript Internationalization API. В настоящее время все это поддерживается Chrome 24, Chrome для Android, Firefox 29, IE 11 и Opera 15, правда, к сожалению, поддержки от Safari нет. В итоге, мы получаем новое пространство имен Intl с предоставлением широкого выбора функциональных возможностей для включения интернационализации в наши числа, даты и сортировки. Стоит разобраться в основных чертах Intl и стать на путь, где всегда есть поддержка от миллионов людей из множества других стран.

Основные возможности

Пространство имен Intl охватывает три основные области функциональности:

  • форматирование чисел;
  • форматирование даты;
  • сортировку строк.

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

Чтобы понять это точнее, нужно рассмотреть несколько примеров.

Наше приложение

Наше первое приложение является простым data reporter, использующим AJAX для извлечения набора данных, которые содержат в себе даты и числа. Во-первых, HTML:

Listing 1: test1.html:

<!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
            <title></title>
            <meta name="description" content="">
            <meta name="viewport" content="width=device-width">
        </head>
        <body>
 
            <h2>Current Stats</h2>
            <table id="stats">
                <thead>
                    <tr>
                        <th>Date</th>
                        <th>Visits</th>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
 
            <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
            <script src="app1.js"></script>
        </body>
    </html>

Обратите внимание на совершенно пустой стол – вот где мы будем работать с нашими данными. А теперь взглянем на JavaScript.

Listing 2: app1.js:

$(document).ready(function() {
 
        //get the table dom
        $table = $("#stats tbody");
        //now, get our data from the api, which is fake btw
        $.getJSON("stats.json").done(function(s) {
            //iterate over stats and add to table
            for(var i=0; i < s.length; i++) {
                $table.append(""+s[i].date+""+s[i].views+"");
            }
        }).fail(function(e) {
            console.log("we failed");
            console.dir(e);
        });
    });

Все, чем занимается здесь код – заставляет AJAX вызвать файл и предоставить результат с данными. Файл данных stats.json – это простой массив из десятка сложно обозначенных значений. Ниже представлена часть этого файла:

[{"date":"4/1/2013","views":938213},{"date":"4/2/2013","views":238213},

Как мы видим, даты представлены в формате месяц/день/год и представленная дата уже прошла, как таковая. В итоге, у нас получается:

Но обратите внимание на то, что без форматирования читать эти числа довольно трудно. Поэтому начать нужно, пожалуй, с добавления этим числам форматирования.

Добавляем форматирование чисел

Текущие изменения можно увидеть в app2.js и протестировать в test2.html. Да и сам код придется немного изменить для вызова новой функции numberFormat:

$table.append(""+s[i].date+""+numberFormat(s[i].views)+"");

А вот и наша функция:

function numberFormat(n) {
        //cache the formatter once
        if(window.Intl && !window.numberFormatter) window.numberFormatter = window.Intl.NumberFormat();
 
        if(window.numberFormatter) {
            return window.numberFormatter.format(n);
        } else {
            return n;   
        }
    }

Функция начинается с проверки в том случае, если Intl существует как часть объекта window. Если это происходит, тогда мы можем проверить и посмотреть, делали ли мы ранее форматирование. Объект Intl создает объект форматирования, который можно использовать повторно, и так как мы форматируем множество чисел, нашим единственным деланием станет, пожалуй, только одномоментное выполнение всего форматирования. Это – то, что мы делаем первым делом после возникновения такой необходимости. Сейчас мы не занимаемся никакими опциями, а просто максимально упрощаем все. В конце концов, если бы не было никакой поддержки на Intl, мы бы просто возвращали все так, как есть. Конечным результатом будет значительное улучшение с минимумом работы:

Отлично! Итак, как мы тестируем языки? Если хотите, можете проверить настройки своего браузера: выбор языка есть во всех браузерах, но простого изменения языка в браузере будет недостаточно. Его изменение повлияет на общее поведение браузера.

Если мы откроем наши средства разработки и посмотрим на сетевые запросы, то увидим, что заголовок носит название "Accept-Lanage" и будет меняться в зависимости от настроек. Если мы добавляем французский язык, например, при условии, что французский не наш национальный язык, то увидим буквы "fr" в заголовке. Но это не имеет ничего общего с Intl, более того, нужно изменит язык операционной системы и перезапустить браузер. И, на самом деле, все это не так страшно, как выглядит.

Когда мы тестировали это впервые, то волновались, что вся операционная система изменится в один момент. Но в ходе теста у нас была возможность менять язык и перезагружать браузер, чтобы видеть изменения, а по завершению теста мы все вернули на круги своя. В Intl функции форматирования позволяют переопределить текущий стандарт и заменить его. Мы изменили приложение, чтобы дать конечному пользователю возможность указывать язык с помощью выпадающего списка. Все изменения внесены в HTML, а эту модификацию можно рассмотреть в test3.html.

<select id="langDropdown">
        <option value="">None Specified</option>
        <option value="en-US">English (US)</option>
        <option value="fr-FR">French (France)</option>
        <option value="lt">Lithuanian</option>
    </select>

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

Listing 3: app3.js:

function numberFormat(n) {
 
        if(window.Intl) {
            var lang = $("#langDropdown").val();
            if(lang === "") lang = navigator.language;
            var formatter = new window.Intl.NumberFormat(lang);
            return formatter.format(n);
        } else {
            return n;   
        }
    }
 
    function getStats() {
 
        $.getJSON("stats.json").done(function(s) {
            //iterate over stats and add to table
            for(var i=0; i < s.length; i++) {
                $table.append(""+s[i].date+""+numberFormat(s[i].views)+"");
            }
        }).fail(function(e) {
            console.log("we failed");
            console.dir(e);
        });
 
    }
 
    $(document).ready(function() {
 
        //get the table dom
        $table = $("#stats tbody");
 
        //notice changes to drop down
        $("#langDropdown").on("change", function(e) {
            $table.empty();
            getStats();
        });
 
        getStats();
    });

Начиная с самого низа смотреть код, обратите внимание, что мы добавили простой обработчик событий для просмотра изменений в выпадающем списке. Когда изменение обнаруживается, таблица пустеет и запускается функция getStats. Эта новая функция просто извлекает код AJAX, который использовался ранее. Реальные изменения происходят в numberFormat: мы проверяем возможный выбранный язык и если он все-таки выбран, то принимаем его за локаль в конструкторе NumberFormat. Если же что-то не было определено, по умолчанию ставится navigator.language. Это теперь дает нам возможность быстро протестировать различные языковые стандарты и посмотреть, как они работают с числами.

Добавляем форматирование даты

А теперь самое время подумать о другом столбце данных - числах. Работать будем в том же стиле, что и раньше, и добавим вызов ново функции dateFormat.

$table.append(""+dateFormat(s[i].date)+""+numberFormat(s[i].views)+"");

А вот и dateFormat, код которого находится в app4.js, который используется в test4.html.

function dateFormat(n) {
 
    //Used for date display
    var opts = {};
    opts.day = "numeric";
    opts.weekday = "long";
    opts.year = "numeric";
    opts.month = "long";
 
    if(window.Intl) {
        var lang = $("#langDropdown").val();
        if(lang === "") lang = navigator.language;
 
        var formatter = new window.Intl.DateTimeFormat(lang, opts);
        n = new Date(n);
        return formatter.format(n);
    } else {
        return n;   
    }
}

Это очень похоже на форматирование чисел, но на этот раз мы используем несколько опций, пока будем создавать форматирование. Опции указывают одновременно и на видимость полей в дате и на их внешний вид. Мы можем показывать в разных вариациях или не показывать вовсе каждую из частей даты. Опции включают в себя:

  • будний день;
  • эпоху;
  • год;
  • месяц;
  • день;
  • час;
  • минуту;
  • секунду;
  • часовой пояс.

Дл получения полного списка используемых значений нужно ознакомиться с MDN документацией для DateTimeFormat, но в качестве примера можно заметить, что месяц может отображаться в виде числа или в текстовой форме. И что же это дает? Посмотрим в английском варианте:


Или во французском варианте:

Уже можно начинать удивляться – что же покрывает местоположение каждого поля? Особого контроля у нас нет: можно создать несколько форматирований, а затем объединить их, но использование одного дает возможность располагать поля согласно внутренней логике. Например, если мы выключим день месяца, то получим April 2013 Monday. Почему так? Не ясно. Однако, обратите внимание, что вам нужно передать форматированию значение даты, а не строку. Вы можете видеть, где мы использовали конструктор даты в форматировании.

А деньги где?

Форматирование валюты – это не отдельный объект, а скорее дополнительная возможность использования форматирования чисел. В следующем демо мы создаем новый файл данных stats2.json и добавляем колонку «Продажи» к нашим данным. К примеру, вот так:

{"date":"4/1/2013","views":938213,"sales":3890.21},{"date":"4/2/2013","views":238213,"sales":9890.10}

Колонка была добавлена в HTML (test5.html), добавлена в JavaScript путем перебора строк данных (app5.js) и появилась в новой функции currencyFormat. Давайте взглянем на это.

function currencyFormat(n) {
 
    var opts = {};
    opts.style = "currency";
    opts.currency = "USD";
 
    if(window.Intl) {
        var lang = $("#langDropdown").val();
        if(lang === "") lang = navigator.language;
        var formatter = new window.Intl.NumberFormat(lang,opts);
        return formatter.format(n);
    } else {
        return n;   
    }
}

Для показа чисел как валюты понадобится два дополнительных значения: во-первых, стиль валюты, а во-вторых – тип валюты. Другие опции вроде отображения названия валюты тоже присутствуют. Правда, есть одна часть, которая может немного сбить нас с толку – необходимость указании типа валюты. Вы, наверное, думаете о том, как же можно выяснить тип валюты для всех возможных значений?

Возможные значения присутствуют в спецификации по ссылке http://www.currency-iso.org/en/home/tables/table-a1.html и теоретически мы даже можем разобрать их XML. Можем, но не хотим. Мы не хотим просто заново отобразить текущее число в локали специфической валюты, потому что 10 американских долларов это не десять долларов, но в песо. При использовании кода выше мы видим соответствующий результат во французской локали: числа отформатированы в соответствии с локалью, а символ валюты расположен после числа.

Сортировка через Collator

Последний пример рассмотрим на конструкторе Collator, который позволяет работать с сортировкой текста. С некоторыми языками все просто: они складываются от А до Я; другие же складываются по иным правилам. А после расстановки акцентов все становится гораздо увлекательнее: можем ли мы быть вверены, что «ä» следует за «a»? Конструктор Collator берет номер аргумента и помогает определить его порядковый номер.

В test6.html мы сделали совершенно новую таблицу Students. Наш новый код загружает список студентов JSON и сортирует их на стороне клиента. Данные JSON это просто массив имен, так что мы посмотрим на логику приложения. 

Listing 4: app6.js:

function sorter(x,y) {
 
        if(window.Intl) {
            var lang = $("#langDropdown").val();
            if(lang === "") lang = navigator.language;      
            return window.Intl.Collator(lang).compare(x,y);
        } else {
            return x.localeCompare(y);  
        }
    }
 
    function getStudents() {
 
        $.getJSON("students.json").done(function(s) {
            //iterate over stats and add to table
            s.sort(sorter);
            for(var i=0; i < s.length; i++) {
                $table.append(""+s[i]+"");
            }
        }).fail(function(e) {
            console.log("we failed");
            console.dir(e);
        });
 
    }
 
    $(document).ready(function() {
 
        //get the table dom
        $table = $("#students tbody");
 
        //notice changes to drop down
        $("#langDropdown").on("change", function(e) {
            $table.empty();
            getStudents();
        });
 
        getStudents();
    });

Этот код очень похож на тот, который использовался в предыдущих примерах, поэтому мы сосредоточимся на getStudents. Самое важное место здесь это «s.sort(sorter)»: мы используем встроенную функцию для массивов, чтобы сделать сортировку с помощью пользовательской функции. Эта функция будет брать два объекта для сравнения и возвращать -1 , 0 или 1, чтобы представить способ сортировки этих двух элементов. Теперь давайте посмотрим на сортировщик.

Если у нас есть Intl объект, мы создаем новый Collator (помните о локали), а затем запускаем функцию сравнения. Если вариант изменения способа сортировки данных, можно использовать и стандартные настройки. Есть и запасной вариант localeCompare, который позволяет использовать определенное языковое форматирование и имеет улучшенную поддержку. В первой части мы стали использовать шведский в качестве основного языка, потому что согласно документации MDN – это отличный способ посмотреть на сортировку в действии. Смотрим сначала английский, а затем и шведский вариант.

В общем, Intl класс дает возможность добавить в код языковое форматирование далеко не одним способом. Смотрим и в множестве библиотек JavaScript, и в самом языке браузеров, куда замечательные разработчики все-таки добавили такую возможность. К сожалению, поддержки в iOS пока нет, но ее появление ожидается в скором времени. Ну а пока можно поблагодарить разработчиков Mozilla за прекрасную документацию по Intl.

Источник: code.tutsplus.com

 

Агрегатор фриланс бирж FreelanceGrab, искать заказы на фрилансе стало еще проще.
8 крупных бирж, удобный поиск и фильтрация по проектам,
моментальное обновление ленты без перезагрузки страницы