Kl10 tch – paver.js + t.js
Transcript of Kl10 tch – paver.js + t.js
JavaScriptУкладка изображений в галереях
и интернационализация приложений
Денис Ольшинhttp://vk.com/denull
О чем пойдет речь
Задача: делаем Single page application.Или расширение для Хрома.Или приложение на PhoneGap.Или приложение для социальной сети.Или свою социальную сеть.Или вообще любой современный сайт.
О чем пойдет речь
Общие задачи решают фреймворки. Рассмотрим две частные (на самом деле не связанные, зато интересные) задачи:
1. Как вывести много картинок.2. Как выводить много текста.
Часть 1. Укладка изображений
(layout то есть, но раз уж вторая часть про локализацию…)
В этой части будет много картинок.
Цель?
● Выводим картинки или текст, HTML?● Много (лента), мало (альбом) или очень
мало (запись с аттачами)?● Можно сжимать и обрезать?● Можно менять местами?● Можно таскать, редактировать,
анимировать?
Тривиальный случай: квадратики!
Фиксируем соотношение сторон — соответственно, можем легко посчитать число строк и столбцов.
Используется: повсеместно.
Квадратики+Если на некоторых фотохочется сделать акцент —делаем их в 4 раза больше.
Требуется чтобы число столбцов было четным (бонус: укладка на одном CSS).
Используется: Facebook
А если не квадрат?Вариант А: сжимать+ всё на месте– дырки– всё мелкое
Вариант Б: обрезать+ аккуратные поля– потери– иногда потери критические (портреты?)
Сделаем поинтереснее
Пусть можно сжимать, но очень не хочется обрезать И оставлять пустые «дырки».
Bin Packing Problem: NP-полнота, нужен перебор всех комбинаций.
(Почти) идеальное решениеФиксированная высота строки, ничего не обрезано, везде равные поля.
Используется: поиск Google, Яндекс, iOS-приложение ВК (в альбомах).
Как эта магия работает?
Сколько бы картинок ни было в строке, их можно сделать равной высоты, а суммарную ширину — равной ширине строки.«Набиваем» их, пока высота > minRowHeight
...и когда эта магия не работает?
● В небольших альбомах висячая последняя строка портит вид.
● У широооких фоток (ландшафты, панорамы) преимущество перед высокими (портреты).
● Строки немного плавают по высоте.
Когда фоток совсем малоПеребираем, сколько положим на каждую строчку и из всех вариантов выбираем оптимальный попропорциям/размеру.
Используется: ВК(в постах)
А если столбики, а не строки?Если порядок не особоважен, можно простозаполнять столбики.
Чит: ещё можно выбирать ширину столбиков как-нибудь по-хитрому.Используется: tumblr, Pinterest, Lightbox
Дополнительный читПодгоняем под сетку, когда соседние столбики выровнялись — вставляем широкую картинку, становится не так скучно.
Столбики+
Фиксируем числостолбцов (немного)и перебираем разныеварианты. Можно делать акценты.
Используется: 500px
Экзотика: золотое сечениеПользуемся тем, что из двух ландшафтов четко формируется один портрет (и наоборот).
– У портретов илиу ландшафтов перевес.– Квадратам места нет.– Висячий хвост в конце.
Как всё это применить?Например, Masonry:Или Packery.Или Isotope.Все упаковывают,никто не масштабирует: не для картинок.http://masonry.desandro.com/http://packery.metafizzy.co/
Как всё это применить?Или вот Freewall:плагин jQuery.
Поддерживает4 укладки, перетаскивание и анимации.http://vnjs.net/www/project/freewall/
(Почти) идеальное решение+Внутрь строк прячем стопки! Наполняем почти как строки — пока можем.
Теперь широкие фотки складываются в стопки, а не забивают всю строку.
+ Можно гибко настраивать размеры строк и стопок.+ Альтернативный режим: подгонять все фото под искомую площадь. Бонус: можно делать акценты (задать искомую площадь побольше).– Те же проблемы с последней строкой.– Строки по-прежнему могут плавать.
Где используется?
А нигде.Но есть в виде библиотеки!
Paver.JShttps://github.com/deNULL/Paver.js
ИспользованиеPaver(dataSource, width, opts).render(el);
dataSource – массив, у элементов поляwidth, height.
width – ширина контейнера.opts – настройки.el – контейнер, куда всё отрисовать.
Для рендеринга в opts передаем функцию:renderTile: function(tile, path) { … return html;},
path содержит тройку индексов row, stack и tile – номер строчки, стопки и картинки в стопке.
Использование
ДемкаТут можно подергать за рычажки:http://denull.github.io/Paver.JS/
Часть 2. Об интернационализации
(и локализации тоже)
● язык = ru, en● локаль = ru_RU, en_US, en_GB● перевод = отображение ключей на строки● интернационализация = i18n = подготовка● локализация = l10n = процесс перевода
А нужны ли эти заморочки?
Нужны.● Задел на будущее.● Избавление от «Найдено 21 фотографий».● Возможность править тексты без правки
шаблонов.
Начнем с простогоВсе строки кладем в виде полей куда-нибудь в lang { … }, а потом достаем по lang[‘key’].
Сразу же хочется научиться делать подстановки (интерполировать) — пишем функцию:function T(key, substitute) { … }
А что там с числами?
Всё страшно.
В каждом языке своикатегории. До шестиштук. ШЕСТИ, КАРЛ.
http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
Половой вопрос
Тут чуть проще: только два случая (если не придираться).
Придется спрашивать ещё одно поле при регистрации, зато никаких уродливых«Загрузил(а)».
Число, род… и падеж, конечно
Тут всё совсем плохо. Даже для одного языка фамилии склоняются по неоднозначно определенным правилам:Чаплин → Чаплином или Чаплиным?Повезло тем, у кого в языке падежей нет.
Решение: Petrovich.js
Работа с датами
Тут всё чуть иначе: формат зависит от локали, а не от языка.Но от языка зависят фразы типа «3 часа назад», которые хорошо бы тоже уметь выводить с умом.
Решение: moment.js, разумеется.
Работа с числами
Всё аналогично датам, только заморочек с выводом относительного времени нет.
Решение: Numeral.js или сами фреймворки
А как же .toLocaleString()?В ванильном JS есть:Date.prototype.toLocaleString()Number.prototype.toLocaleString()
Есть давно, но настроить можно только с Chrome 24, FF 29, IE 11, Opera 15, а в Safari до сих пор нельзя.
А неплохо бы ещё...
...вспомнить о типографских правилах.● кроме «-» есть, как минимум, «—» и «–»● кавычки бывают “тоже” «разные»● автозамены: (c) , (r) , (tm) , ...● и вообще много всего
Решения: Типограф, mdash.ru, грамотность.
Итого:
Для i18n + l10n:● l10n.js● LocalePlanet● L20n от Mozilla
● Moment.js – даты● Numeral.js – числа● Petrovich.js – имена● Типограф – правила
Ближе всего к тому, что надо: FormatJS.io и i18next.com (но все равно не идеально)
+
У всех свои форматы :(
i18next:{ en: { translation: { girlsAndBoys: '$t(girls, {"count": __girls__}) and __count__ boy', girlsAndBoys_plural: '$t(girls, {"count": __girls__}) and __count__ boys' }, girls: '__count__ girl', girls_plural: '__count__ girls' } } };
FormatJS:‘You have {itemCount, plural, =0 {no items} one {1 item} other {{itemCount} items}}.’
Это хотя бы стандарт Message ICUНо он совсем адовый
Хочется всего и сразу
Но без излишеств (встроенного во фреймворки не хватает, тащить кучу библиотек ради базовых вещей грустно). К тому же, вещи реально тесно связанные.
Склонять имена вообще все боятся.
Мое решение?
Да, ещё один велосипед. С блекджеком и своим форматом шаблонов.
T.jshttps://github.com/deNULL/T.js
Это почти как шаблонизатор, но нетphotos: 'фотографий',liked: 'оценил',user_liked: '{user} {>liked} {photos} {>photos}',
Делаем: T(‘user_liked’, {user: ’Олег’, photos: 5}) и получаем: ‘Олег оценил 5 фотографий’.
Подставили входные данные + другие ключи.
Структура языковых данныхT.define({ ru: { key1: ‘str1’, key2: ‘str2’, key3: { subkey: { subsubkey: ‘wow’ } },}})
Можно вызывать сколько угодно раз. Вложенность любая. Достаем через точку:T(‘key3.subkey.subsubkey’)
А что с числами?
Чтобы не было ‘Олег оценил 1 фотографий’:photos: { $plural: { one: ‘фотографию’, few: ‘фотографии’, other: ‘фотографий’}}
Категории зависят от языка, есть поддержка ru/en. Можно добавить свою функцию определения категории.
А если не Олег, а Анна?liked: { $gender: { m: ‘оценил’, f: ‘оценила’}}Аналогично, но надо передавать пол как параметр в подставляемый шаблон:user_liked: ‘… {>user.name user.gender} …’
T(‘user_liked’, { user: { name: ‘Анна’, gender: ‘f’});
А добавить «... Ильи» в конце?user_liked: ‘… {$name.gen whom.name}’
= вызов функции $name с первым параметром whom.name, вторым — ‘gen’ (родительный падеж).Есть внутренние функции $name, $surname, $patronym для русского, их можно доопределить или переопределить.
Что за «вызов функции»?
Функция = шаблон. Подставить шаблон = вызвать функцию. Если имя функции или шаблона начинается с $, то при подстановке > не нужен ({>$some} = {$some}).То, что после точек (func.p1.p2) интерпретируется как дополнительные параметры.
Бонус: трансформации
Пишем:user: ‘пользователь’
Получаем:T(‘user’) => ‘пользователь’T(‘User’) => ‘Пользователь’T(‘USER’) => ‘ПОЛЬЗОВАТЕЛЬ’
Непривычно, но может быть удобно.
И внезапно — инлайн-переводT.inlineTranslation(true);
TA-DA! Все строки кликабельны и открывают окошко с текстом перевода.Сохранение изменений (и проверка прав) на сервере — лежит на вас.T.showAllKeys();
Показать вообще все ключики списком.
Такая вот красота
Это всё без дополнительных зависимостей.Можно и вырезать, если юзер не переводчик.
К слову
T(‘user!’) — Локально отключить инлайн.Инлайн-перевод генерит HTML — в шаблонизаторы надо подставлять результат в сыром виде (Angular: $sce + ng-bind-html).
Во входных данных эскейпятся < и > – а в самих переводах нет, будьте внимательны.
Чего пока нет
● форматирование дат ({$date …})● форматирование чисел ({$num …})
Под некоторым сомнением: применение типографских правил.
Конец второй части.
Есть вопросы?
Денис Ольшинhttp://vk.com/denull