Эта статья о том, что такое «транслитерация URL-адреса», зачем она нужна и какие её виды нами используются. В статье рассказывается про «взаимно-однозначный транслит», а также приводятся другие интересные наборы правил для транслитерации адресов формата URL.
{{toc numerate=1}}
===О чём речь===
Почти всем из нас, кто решал проблему автоматической или полу-автоматической генерации «удобных для человека URL-адресов» (их ещё называют "((http://spectator.ru/technology/php/user_friendly_urls ЧПУ))" или pretty-urls), приходилось сталкиваться с задачей так называемой «транслитерации URL-адреса».
Эта задача имеет следующие ограничения и стартовые условия:
1. URL-адреса страниц могут состоять из ограниченного набора символов: цифр ##0–9##, латинских букв ##a-z## и ##A-Z##, слэша ##/##, тильды ##~## и следующих символов, указанных в ((http://www.ietf.org/rfc/rfc2396.txt RFC 2396)): ##-_.!*'()##
1. То, из чего мы должны программным путём сгенерировать URL — как правило, заголовок новости. Или документа. Словом, может включать и русские буквы, и знаки препинания, и почти всё, что угодно, включая HTML-таги.
Если разобраться, то перед нами встаёт не одна задача, а целых три:
1. **Получение красивого читаемого адреса URL** — задача, в которой мы жертвуем дотошностью в угоду красивости.
1. **Формирование короткого однозначного идентификатора** (мы его привыкли называть «супертагом») — задача, в которой мы жертвуем красивостью ради борьбы с опечатками.
1. **Передача англо-русскоязычного слова (или фразы) через URL без потерь** — для последующего восстановления исходного «текста» (слова или фразы). Именно эта задача и получила название «взаимно-однозначный транслит», и жертвует красивостью ради однозначности преобразования.
Для решения этих трёх разных задач мы разработали три разных способа транслитерации. Кроме того, мы разработали пару классов на языках PHP и ~JavaScript, осуществляющих все эти три способа. Ссылку на этот проект с открытым исходным кодом вы найдёте в самом низу документа.
===URL Translit: Красивый читаемый URL-адрес===
Красивый читаемый адрес нам нужно формировать автоматически для новостей, документов, статей, — как правило, на основе заголовка, формат которого мы условно называем «что-угодно». Т.е. там нам могут встретиться не только недопустимые в URL русские буквы, но ещё и знаки препинания.
Нам нужно добиться от автоматики такого преобразования, чтобы результат легко читался, не вызывая затруднений и недоумений. Такое преобразование мы называем «урл-транслит», а его результат, обычно, ##pretty_url##.
====Постановка задачи====
Из чего-угодно™ получать валидный урл, URL, который легко «прочитать» — понять содержимое, глядя на него в адресной строке или в строке, теле страницы или статус-баре.
Не требуется восстановление исходного чего-угодно™.
====Критерии, которые актуальны для данной задачи====
простота восприятия букв (например, ##Ц## проще воспринимается как ##ts##, чем как ##c##);
«похожесть» на ГОСТ (но отступления там, где простота важнее);
спецсимволы не имеют значения, а разделители (пробелы, например) — имеют;
разницы между ##Е## и ##Ё## в таких адресах быть не должно;
все буквы в нижнем регистре, потому что это не вызывает неоднозначности (и, как показал наш опрос — больше нравится людям);
буквы латинского алфавита не должны пострадать.
====Решение====
Решение реализовано на двух языках программирования (PHP и ~JavaScript), содержится в открытом проекте ((../Translit Translit)).
Метод называется ##~UrlTranslit()##.
===Supertag Translit: Короткий однозначный идентификатор===
Если то, о чём мы говорили раньше, своей первой и наиглавнейшей задачей ставило «понятность» адреса для читателя-человека, то задача номер два — уметь формировать адреса-идентификаторы (т.е. такие, по которым мы будем находить, скажем, в БД ту новость, которую следует показать).
Адрес-идентификатор в виде числа часто недостаточен для читателя-человека (например: http://somesite.ru/page/34722 ), поэтому его можно сопровождать «красивым и понятным» дополнением, которое наш программный механизм будет игнорировать (например, страница с адресом http://www.npj.ru/kuso/259318_dve_trenirovki_podrjad показывает то же, что и http://www.npj.ru/kuso/259318 ).
Однако, часто такое решение недостаточно, — ставится дополнительное условие «никаких цифр». Тогда нам ничего не остаётся, как использовать «красивое и понятное» дополнение в качестве идентификатора. В таком случае нам хочется обезопаситься хотя бы от того, что при переписывании забудут написать подчёркивание ##_## или напишут два подчерка вместо одного. Длину ключа тоже хочется сделать поменьше — дабы хоть немного ускорить поиск/сравнение строк. Такой ключ мы привыкли называть **супертаг** — ##supertag##.
====Постановка задачи====
Из чего-угодно™ получить максимально короткий идентификатор, который не не теряет смысла/однозначности и вместе с тем некритичен к лишним разделителям и большим/малым буквам.
Не требуется восстановление исходного чего-угодно™.
====Критерии, которые актуальны для данной задачи====
##Supertag(~UrlTranslit(x))## должен быть равен ##Supertag(x)##,
++иными словами, наше «укорачивающее» преобразование должно выдавать одинаковый результат, как на исходной строке, так и на «красивом URL», полученном из исходной строки;++
преобразование должно давать как можно меньше случайных совпадений результата (ошибок 1 рода);
буквы латинского алфавита не должны пострадать.
====Решение====
Решение реализовано на двух языках программирования (PHP и ~JavaScript), содержится в открытом проекте Translit.
Метод называется ##Supertag()##.
===~BiDi Translit: Взаимно-однозначный транслит===
В качестве исторического экскурса можно заметить, что вся эта «эпопея» с транслитерацией началась в своё время с постановки задачи «взаимно-однозначного транслита» — когда возникла необходимость передавать в URL-совместимом формате информацию таким образом, чтобы её можно было однозначно восстановить обратным преобразованием.
Передаваемая информация включала в себя только буквы, цифры, дефис и «слэш» ##/##. Это — именно та функциональность, которая используется в НПЖ при формировании ссылок **на создание новых страниц**. Для формирования «красивых» ссылок на уже существующие страницы используется «урл-транслит», а в БД хранятся «супертаги».
====Постановка задачи====
Из строки, содержащей русские и английские буквы, получать валидный URL (без использования ##%AB##-комбинаций), таким образом, чтобы затем было возможно восстановить оригинальную строку. Также уметь производить и обратную операцию.
В отличие от предыдущих рассмотренных способов здесь требуется восстановление исходной строки (допустима потеря запятых и прочих «слабозначащих» символов).
====Критерии, которые актуальны для данной задачи====
буквы латинского алфавита не должны пострадать в «преобразованной» строке;
русские и латинские буквы не должны пострадать в результате преобразования и последующего восстановления;
регистр букв не должен пострадать в результате преобразования и последующего восстановления;
желательно добиться максимальной читабельности «преобразованной» строки.
====Идея решения====
В отличие от предыдущих способов здесь, кроме банальной таблицы транслитерации (приведённой чуть ниже) использовано некоторое алгоритмически-изобретательское решение, идею которого мы сейчас осветим.
Идея решения заключается в подходе к преобразуемой строке как к чередующейся последовательности кириллических и английских «подстрок»:
#|
|| ##!!(green)~ThisIs!!!!Русский!!!!(green)Name/!!!!~ДляВас!!!!(green)/!!!!Демонстрация!!!!(green)~OfSwitching!!## ||
|#
!!(green)зелёным!! выделены «английские строки»;
!!красным!! — кириллические.
Очевидно, что каждая чётная подстрока в этом случае — точно «кириллическая», а каждая нечётная — «англоязычная». Из способа деления на подстроки следует, что в «кириллической» не встречаются буквы ##a-zA-Z##.
Если теперь мы вставим в строку «разграничители» подстрок, то смело можем перевести все «кириллические» строки в алфавит ##a-z## с помощью любого взаимно-однозначного преобразования, например, ГОСТ.
Наш пример тогда будет выглядеть так:
#|
|| ##!!(green)~ThisIs!!+Russkijj+!!(green)Name/!!+!!~DljaVas!!+!!(green)/!!+!!Demonstracija!!+!!(green)~OfSwitching!!## ||
|#
в качестве «разграничителя» (своеобразного «переключателя языка») мы используем «плюс»;
обратите внимание, что ##/## считается «англоязычным» символом.
Осталось решить вопрос с теми заглавными русскими буквами, что после транслитерации занимают более одного символа — мы их переводим как ##Ж## — ##Zh##, потому что так «читабельнее». Кроме того, есть некоторый нюанс с твёрдыми и мягкими знаками, пробелом и «экранированием» плюса. Все эти нюансы освещены в сводных таблицах, приведённых ниже.
====Решение====
Решение реализовано на двух языках программирования (PHP и ~JavaScript), содержится в открытом проекте Translit.
Метод называется ##~BiDiTranslit()##.
===Важное примечание===
Все функции/способы транслитерации должны уметь (и умеют) работать в двух режимах:
«уничтожать» все слэши ##/##, которые встретятся во входной строке;
пропускать сквозь себя все эти слэши неизменными.
Это необходимо для двух разных способов их использования: а) генерирования «имён папок» в виртуальном «дереве сайта», б) генерирования полных адресов/путей (как это делает WackoWiki или НПЖ).
===Сводные таблицы===
Все правила, по которым производится транслитерация URL-адресов, вынесены нами в отдельный документ ConversionTables, чтобы не затруднять чтение статьи.
===Авторы и благодарности===
Материал для этой статьи подготовлен Романом Ивановым и Кусо Мендокуси:.
Библиотеки ##translit.php## и ##translit.js## написаны Романом Ивановым и Кусо Мендокуси: соответственно и отлажены коллективным разумом. Test suite для обеих библиотек подобран коллективным разумом, а организован Романом Ивановым.
Текст этой статьи написан Кусо Мендокуси:, стёрт Кусо Мендокуси:, заботливо сохранён Романом Ивановым, восстановлен и переписан заново Кусо Мендокуси: и бережно вычитан Романом Ивановым.
Мы очень благодарны нам за весь этот цирк.
Ещё мы благодарны всем, спрашивавшим нас о «взаимно-однозначном транслите».
===Ссылки===
* RFC 2396. http://www.ietf.org/rfc/rfc2396.txt
* ГОСТ 16876–71. http://spelling.spb.ru/spell057.htm
* Опрос стиля ссылок в ваке. http://web.archive.org/web/20060208071822/http://wackowiki.com/WackoIdeas/OprosStiljaSsylok
* ЧПУ. http://spectator.ru/technology/php/user_friendly_urls
* Translit PHP+JS Classes. http://pixel-apes.com/translit
* Таблицы транслитерации. ((ConversionTables ConversionTables))