i18n
l10n

Все мы разные!

Язык и валюта

Культурные особенности

Символы
иконки
цвета

Но есть и кое что общее

सस्ती!

Это он нам?
Пойдемте-ка отсюда,
милочка.

localization
internationalization
l10n
i18n

l10n

l10n это адаптация продукта для его соответствия языковым, культурным и прочим требованиям конкретного рынка.

  • Cистема счисления, форматы даты и времени
  • Валюта
  • Использование систем ввода
  • Символы, иконки, цвета
  • Текст и графика, содержащие отсылки к идеям и действиям
  • Различие легислативный требований

i18n

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

  • Разработка продукта таким образом, что бы устранить барьеры для для его локализации.
  • Реализовать функционал, который не потребуются, пока не начнется локализация.
  • Писать код, который позволяет реализовать поддержку локальных, языковых и культурных особенностей.
  • Отделение элементов, которые требуют локализации от остальных.

Как задать
кодировку?

Content-Type: text/html; charset=UTF-8
<meta 
   http-equiv="Content-type" 
   content="text/html;charset=UTF-8">
<meta charset="UTF-8">
1024
<link charset="Windows-1251"
 rel="stylesheet" type="text/css"
 href="styles.css"/>

<script charset="UTF-8"
 src="script.js"></script>

<a charset="ISO-8859-1"
 href="page.html"></a>
<link charset="Windows-1251"
 rel="stylesheet" type="text/css"
 href="styles.css"/>

<script charset="UTF-8"
 src="script.js"></script>

<a charset="ISO-8859-1"
 href="page.html"></a>
Content-Type: text/css; charset=UTF-8
@charset "UTF-8";

Как обозначить
язык материалов?

Content-Language: de, fr, it
<meta 
   http-equiv="Content-Language" 
   content="de, fr, it" />
<meta 
   http-equiv="Content-Language" 
   content="de, fr, it" />
<meta 
   name="dc.language" 
   content="en" />
<meta 
   name="dc.language" 
   content="en" />
<html lang="en-UK">
</html>
<article lang="und">
</article>
<article lang="zxx">
</article>
:lang(en-UK){
  font-family: "PT Sans", sans-serif;
  }
:lang(zh-Hans){
  font-family: "SimSum-18030", 
               SimHei, serif;
  }
<a 
   href="http://goo.gl/GJ2MeM" 
   hreflang="en">
   The Battle for the Body Field
</a>
a[hreflang = 'sv']:after { 
    content: " [Swedish] "; 
    }
  1. language язык, en
  2. extlang уточнение, zh-yue
  3. script начертание, zh-Hans
  4. region регион, en-US, en-UK
  5. variant диалект, sl-nedis, sl-rozaj
  6. extension расширенное определение
  7. privateuse для частного использования

Как управлять роботами?

<meta 
     name="google" 
     value="notranslate"/>
<article class="notranslate">
</article>
<article translate="no">
</article>

Направление
письма

  • Сильно направленные
  • Нейтрально направленные
  • Слабо направленные
<p dir="rtl"></p>
<p dir="ltr"></p>
<p dir="auto"></p>

Сильно направленные

<p>раз اثنان три</p>
<p dir="RTL">раз اثنان три</p>

раз اثنان три

раз اثنان три

раз اثنان три

раз اثنان три

Нейтрально направленные

<p>
  раз, اثنان, три, أربعة.
</p>
<p dir="RTL">
  раз, اثنان, три, أربعة.
</p>

раз, اثنان, три, أربعة.

раз, اثنان, три, أربعة.

раз, اثنانтри, أربعة.

раз, اثنانтри, أربعة.

<p>
  раз, اثنان, три, четыре.
</p>
<p dir="RTL">
  раз, اثنان, три, четыре.
</p>

раз, اثنان, три, четыре.

раз, اثنان, три, четыре.

раз, اثنان, три, четыре.

раз, اثنان, три, четыре.

Слабо направленные

<p>Оруэл عربي ١٩٨٤, رسائل</p>
<p dir="rtl">Оруэл عربي 1984, رسائل</p>

Оруэл عربي ١٩٨٤, رسائل

Оруэл عربي 1984, رسائل

Оруэл عربي ١٩٨٤, رسائل

Оруэл عربي 1984, رسائل

&lrm; и &rlm;

<p dir="RTL">
  раз, اثنان, три, четыре.
</p>
<p dir="RTL">
  раз, اثنان, три, четыре.&lrm;
</p>

раз, اثنان, три, четыре.

раз, اثنان, три, четыре.‎

раз, اثنان, три, четыре.

раз, اثنان, три, четыре.

<bdi></bdi>
<bdo dir="rtl">
 Интересно, что получится?
</bdo>

Интересно, что получится?

.some{
  direction: ltr
             rtl
             inherit;}
.some{
  unicode-bidi: normal 
                embed
                isolate
                bidi-override
                isolate-override
                plaintext
                inherit;}

и тут врываются китайцы

.some{
    writing-mode: horizontal-tb
                  vertical-rl
                  vertical-lr;
    }

时事专栏

台湾来鸿:升温中的台北市长选举

随着国民党荣誉主席连战之子连胜文正式宣布参选,台北市长选举选战逐渐升温。

时事专栏

台湾来鸿:升温中的台北市长选举

随着国民党荣誉主席连战之子连胜文正式宣布参选,台北市长选举选战逐渐升温。

МИД РФ возмущен "неуважением" НАТО к Украине

Российский МИД выпустил пространное заявление, в котором посоветовал Брюсселю уважать внеблоковый статус Киева и отказаться от заявлений в адрес Украины, которые Москва считает провокационными.

МИД РФ возмущен "неуважением" НАТО к Украине

Российский МИД выпустил пространное заявление, в котором посоветовал Брюсселю уважать внеблоковый статус Киева и отказаться от заявлений в адрес Украины, которые Москва считает провокационными.

МИД РФ возмущен "неуважением" НАТО к Украине

Российский МИД выпустил пространное заявление, в котором посоветовал Брюсселю уважать внеблоковый статус Киева и отказаться от заявлений в адрес Украины, которые Москва считает провокационными.

МИД РФ возмущен "неуважением" НАТО к Украине

Российский МИД выпустил пространное заявление, в котором посоветовал Брюсселю уважать внеблоковый статус Киева и отказаться от заявлений в адрес Украины, которые Москва считает провокационными.

.some{
    text-orientation: mixed
                      upright
                      sideways-right
                      sideways-left
                      sideways
                      use-glyph-orientation;
    }

МИД РФ возмущен

台湾来鸿:升温中的 20 台北市长选举

随着国民党荣誉主席连战之子连胜文正式宣布参选,台北市长选举选战逐渐升温。

МИД РФ возмущен

台湾来鸿:升温中的 20 台北市长选举

随着国民党荣誉主席连战之子连胜文正式宣布参选,台北市长选举选战逐渐升温。

МИД РФ возмущен

台湾来鸿:升温中的 20 台北市长选举

随着国民党荣誉主席连战之子连胜文正式宣布参选,台北市长选举选战逐渐升温。

МИД РФ возмущен

台湾来鸿:升温中的 20 台北市长选举

随着国民党荣誉主席连战之子连胜文正式宣布参选,台北市长选举选战逐渐升温。

.some{
    text-combine-horizontal:  none
                              all
                              digits [int];
    }

<ruby>
 <rb>Наш текст<rb>
 <rt>Как читать</rt>
</ruby>
紙芝居かみしばい
Наш текстКак читать
紙芝居かみしばい
Наш текстКак читать
<ruby>
 <rb>紙芝居<rb>
 <rp>(<rp>
 <rt>かみしばい</rt>
 <rp>)<rp>
</ruby>
紙芝居 (かみしばい)
紙芝居(かみしばい)
<ruby>
 <rbc>
   <rb>林<rb><rb>和</rb><rb>代</rb>
 </rbc>
 <rtc>
   <rt>はやし</rt><rt rbspan="2">かず</rt>
 </rtc>
</ruby>
<ruby>
 <rbc><rb>民政局</rb></rbc>
 <rtc><rt>みんせいきょく</rt></rtc>
 <rtc><rt>ガバメント・セクシヨン</rt></rtc>
</ruby>
民 政 局みんせいきょくガバメント・セクシヨン
.some{
    ruby-position:  over
                    under
                    
                    inter-character
                     
                    right
                    left;
    }
.some{
    ruby-merge:  separate
                 collapse
                 auto;
    }
.some{
    ruby-align:  start
                 center
                 space-between
                 space-around;
    }

Ну давай же,
расскажи что-то
еще про стандарты!
Это так … мммм …
захватывающе!

Как
происходит локализация?

Отдельные версии проекта

Шаблонизация

<section id="templates">
 <section id="comment-template">
   <article>
     <img>
     <p class="name"></p>
     <p class="message"></p>
   </article>
 </section>
</section>
#templates{
  display:none;
  }
var template = document.getElementById('comment-template'),
    template = template.cloneNode(true),
    img = template.querySelector('img'),
    nameit = template.querySelector('.name'),
    message = template.querySelector('.message');
 
img.setAttribute('src','http://goo.gl/nZ9uCF');
nameit.textContent = 'Анна Франк';
message.textContent = 'Комментарий';
 
document.body.appendChild(template);
<template></template>
  1. До момента его активации шаблон инертен.
  2. Скрипты не запускаются, аудио и видео не играет, картинки не подгружаются
  3. Его невозможно напрямую получить
  4. Можно разместить где захочется
<template id="comment-template">
  <article>
    <img>
    <p class="name"></p>
    <p class="message"></p>
  </article>
</template>
var template = document.getElementById('comment-template'),
    template = template.content.cloneNode(true),
    img = template.querySelector('img'),
    nameit = template.querySelector('.name'),
    message = template.querySelector('.message');
 
img.setAttribute('src','http://goo.gl/nZ9uCF');
nameit.textContent = 'Анна Франк';
message.textContent = 'Комментарий';
 
document.body.appendChild(template);
var template = document.getElementById('comment-template'),
    template = template.content.cloneNode(true),
    img = template.querySelector('img'),
    nameit = template.querySelector('.name'),
    message = template.querySelector('.message');
 
img.setAttribute('src','http://goo.gl/nZ9uCF');
nameit.textContent = 'Анна Франк';
message.textContent = 'Комментарий';
 
document.body.appendChild(template);

Handlebars

<script src="handlebars.min.js"></script>
<template id="comment-template">
  <article>
    <img src="{{img.src}}" alt="{{img.alt}}"/>
    <p class="name">{{name}}</p>
    <p class="message">{{comment}}</p>
  </article>
</template>
var template = document.getElementById('comment-template'),
    template = Handlebars.compile(template.innerHTML),
    data = {
            img:{
              src:"http://goo.gl/nZ9uCF",
              alt:"Аватар"
              },
            name:"Анна Франк",
            comment:"Комментарий"
            };
document.body.innerHTML+=template(data);

Но ведь
можно
лучше?

i18next

Основы
<script src="js/jquery.js">
</script>
<script src="js/i18next-1.7.2.min.js">
</script>
./locales/en/translation.json
         /ru/translation.json
         /ru-RU/translation.json
{"app": {
    "name": "i18next эксперимент"
  },
  "nav": {
    "about": "О проекте",
    "contacts": "Контакты"
 }}
<title data-i18n="app.name">
	Значение по умолчанию
</title>
$.i18n.init().done(function(){
  var node = document.querySelector(
        '[data-i18n="app.name"]');
  node.textContent = document.i18n.t(
        'app.name');
});
$.i18n.init({
  lng: 'ru', 
  fallbackLng: false
}).done(function(){
  $('html').i18n();
});
i18next эксперимент
Получение
информации
о языке
  1. Параметр в url (?setLng=en-US)
  2. Cookie (i18next)
  3. Язык установленный в браузере по умолчанию
  4. fallbackLng, по умолчанию — dev
i18n.detectLanguage();
ru
i18next.setLng(
	"en-UK", 
	function(){}
);
Переменные
<p data-i18n="apple-count"></p>
{
"apple-count":
"Количество яблок: __apple-count__"
}
$.i18n.init({
  lng: 'ru-RU'
}).done(function(){
  $('[apple-count]').i18n({
    "apple-count":3
});});
Количество яблок: 3
Падежи
<p data-i18n="ihave"></p>
{
  "ihave":"У меня __count__ яблок",
  "ihave_plural_1": "У меня __count__ яблоко",
  "ihave_plural_2": "У меня __count__ яблока",
  "ihave_plural_5": "У меня __count__ яблок",
}
$.i18n.init({
  lng: 'ru-RU'
}).done(function(){
  $('[ihave]').i18n({
    "count":302
});});
У меня 302 яблока
$.i18n.init({
  lng: 'ru-RU'
}).done(function(){
  $('[ihave]').i18n({
    "count": 0
});});
У меня 0 яблок
i18n.pluralExtensions.addRule("ru", {
  "name": "Rusian", 
  "numbers": [1,2,5],
  "plurals": function (a){
    return Number(1 … ?1:2);
  }
});
i18n.pluralExtensions.addRule("ru", {
  "name": "Rusian", 
  "numbers": [1,2,5,0],
  "plurals": function (a){
    return Number(0==a?3:1 … ?1:2);
  }
});
{
  "ihave":"У меня __count__ яблок",
  "ihave_plural_0": "У меня нет яблок",
  "ihave_plural_1": "У меня __count__ яблоко",
  "ihave_plural_2": "У меня __count__ яблока",
  "ihave_plural_5": "У меня __count__ яблок"
}
У меня нет яблок
Пол
<p data-i18n="ipublish"></p>
{
  "ipublish":"__name__ опубликовало статью",
  "ipublish_male":"__name__ опубликовал статью",
  "ipublish_female":"__name__ опубликовала статью"
}
$('.static, title').i18n({
  "context": "male", 
  "name":"Костя"
});
Костя опубликовал статью
Логотип jQ

http://storify.com/

$ bower install i18next

Но ведь можно ещё лучше?

l20n

Логотип Mozilla
Основы
<h1 data-l10n-id="title"></h1>
<script 
   src="l20n.min.js">
</script>
<link 
   rel="localization" 
   href="browser.json">
{
  "locales": ["ru","en"],
  "default_locale": "ru",
  "resources": [
    "locales/{{locale}}/website.l20n"
  ]
}
<title "Simplest example">
<title "Простой пример">
Простой пример
<input 
	data-l10n-id="nameinput" 
	placeholder="default placeholder">
<nameinput 
	placeholder: "Как тебя зовут?">
Многострочные вставки
<p data-l10n-id="save">
	<input type="submit">
	<a href="#" class="btn-cancel"></a>
</p>
<save """
	<input 
		value="Сохранить" 
		type="text"> или
	<a title="Вернуться назад">
		отменить?</a>.
""">
Переменные
<p data-l10n-id="message"></p>
<name "Настя">
<message """
	Привет, {{ name }}!
	""">
Привет, Настя!
<name {
	short: "Настя"
	long: "Настасья Николаевна"}>
<message """
	Привет, {{ name.long }}!
	""">
Привет, Настасья Николаевна!
<name {
	*short: "Настя"
	 long: "Настасья Николаевна"}>
<message """
	Привет, {{ name }}!
	""">
Привет, Настя!
<title["long"] {
	short: "Настя",
	long: "Настасья Николаевна"
}>
Привет, Настасья Николаевна!
Глобальные
переменные
<p data-l10n-id="os"></p>
<os "ОС: {{ @os }}">
ОС: mac
<p data-l10n-id="hour"></p>
<hour "Часов: {{ @hour }}">
Часов: 17
<p data-l10n-id="screen"></p>
<screen "Ширина экрана: 
	{{ @screen.width.px }} px">
Ширина экрана: 1263 px
Как передать
данные из вне?
<script type="application/l10n-data+json">
	{
		"name": "Настя"
	}
</script>
document.l10n.ready(function() {
  var message = document.querySelector(
        '[data-l10n-id="message"]');
  message.textContent = document.l10n.getSync(
    "message",
    {
      "name": "Настя"
    });
});
Привет, Настя
Выбор перевода
взависимости
от переменной
<p data-l10n-id="publishing"></p>
<publishing[$gender] {
	masculine: "{{$name}} написал вам сообщение",
	feminine: "{{$name}} написала вам сообщение",
	*unknown: "{{$name}} написало вам сообщение"
}>
document.l10n.ready(function() {
  var node = document.querySelector(
        '[data-l10n-id="publishing"]');
  node.textContent = document.l10n.getSync(
    "publishing",
    {
      "name": "Настя",
      "gender": "feminine"
    });
});
Настя написала вам сообщение
Падежи и макросы
<plural($n) { 
	$n == 0 ? "zero" : 
	($n%100)>10&&($n%100)<20 ? "five" : 
	($n%10) == 1 ? "one" :
	($n%10)>1&&($n%10)<5 ? "two" : "five"
}>
<messages[plural($newmessages)] {
	zero: "У вас нет новых сообщений",
	one: "У вас {{$newmessages}} новое сообщение",
	two: "У вас {{$newmessages}} новых сообщения",
	five: "У вас {{$newmessages}} новых сообщений"
}>
<plural($n) { 
	$n == 0 ? "zero" : 
	$n == 1 ? "one": "many"
}>
<messages[plural($newmessages)] {
	zero: "There are no new messages",
	one: "You have {{$newmessages}} new message",
	many: "You have {{$newmessages}} new messages"
}>
document.l10n.ready(function() {
  var node = document.querySelector(
        '[data-l10n-id="messages"]');
  node.textContent = document.l10n.getSync(
    "messages",
    {
      newmessages: 1
    });
});
У вас 1 новое сообщение
Адаптивность
<h1 data-l10n-id="settingsTitle"></h1>
<formFactor($n) { 
   $n.px > 1200 ? "large" : 
   $n.px >  980 ? "desktop" : 
   $n.px >  768 ? "landscapeTablet" :  
   $n.px >  480 ? "landscapePhone" :  
                  "portraitPhone"  
}>
<dataSettings[formFactor(@screen.width)] {
 *large: "Настройки оповещений",
  portraitPhone: "Настройки"
}>
$ bower install l20n

— Jan Mühlemann @jamuhl

«@SilentImp looks interesting…if the demand for this raises i'm sure it will be added.»

О, да, вопросы!