Работаем с кириллическими доменами
Собственно речь пойдет о том, как работать с кирилическими (и не только) доменами в PHP, Node.js, Bash/Zsh и Python. Почему такой выбор языков? Это стек проекта GeekJob.ru, над которым я работаю.
IDN (Internationalized Domain Names — интернационализованные доменные имена) — это доменные имена, которые содержат символы национальных алфавитов.
К IDN относятся как адреса с нелатинскими буквами на традиционных доменах верхнего уровня, так и нелатинские домены — домены верхнего уровня, составленные из букв нелатинских алфавитов планеты: кириллица, арабский алфавит и др. IDN домены также существуют в крупных международных доменных зонах.
Punycode — стандартизированный метод преобразования последовательностей Unicode-символов в так называемые ACE-последовательности (ASCII Compatible Encoding — кодировка, совместимая с ASCII), которые состоят только из алфавитно-цифровых символов, как это разрешено в доменных именах. Punycode был разработан для однозначного преобразования доменных имен в последовательность ASCII-символов.
Что из себя представляет такой домен в виде пуникода:
https://александр-майоров.рф
В виде punycode последовательности будет выглядеть так:
https://xn----7sbabkjf0beiqjsayfh.xn--p1ai/
В такой вид, например, приводит JS класс URL:
new URL('https://александр-майоров.рф')
href: "https://xn----7sbabkjf0beiqjsayfh.xn--p1ai/"
origin: "https://xn----7sbabkjf0beiqjsayfh.xn--p1ai"
protocol: "https:"
host: "xn----7sbabkjf0beiqjsayfh.xn--p1ai"
hostname: "xn----7sbabkjf0beiqjsayfh.xn--p1ai"
А каким образом получить назад читаемый вид? Я выбрал стратегию, в которой в базе хранится оригинальное название домена, без конвертации. Поэтому на бэкенде у меня такие ссылки преобразуются. Пойдем по порядку, как сделать это в Node.js ?
Node.js
В Ноде до недавнего времени модуль punycode был встроен, но затем был вынесен (пруф https://nodejs.org/api/punycode.html)
Теперь модуль существует независимо от ноды в виде Punycode.js
Устанавливаем:
npm install punycode --save
Ну и далее работаем так:
punycode.toUnicode('xn----7sbabkjf0beiqjsayfh.xn--p1ai');
Важно! Конвертер должен принимать домен, без префикса протокола. Иначе на выходе будет неожиданный результат.
PHP
В PHP работа с IDN встроена и достаточно просто вызвать встроенную функцию:
<?php
echo idn_to_utf8('xn----7sbabkjf0beiqjsayfh.xn--p1ai');
Пруф: https://www.php.net/manual/ru/function.idn-to-utf8.php
Python
В питоне уже встроена возможность работать с IDN доменами и есть кодек idna, и все это доступно в строках. Но есть нюансы.
Если мы хотим получить punycode, то мы можем вызвать метод encode у обычной строки:
uri = "александр-майоров.рф"
print(uri.encode("idna"))
И на выходе мы получаем бинарную строку. А вот если хотим сделать наоборот, то мы должны так же работать с бинарными строками. Поэтому если у вас на входе обычная строка, то чтобы ее преобразовать пишем такой код:
uri = bytearray('xn----7sbabkjf0beiqjsayfh.xn--p1ai', 'utf8')
print(uri.decode("idna"))
Bash/Zsh
Если же вам нужно работать с доменами в шеле, то тут тоже есть нужные утилиты, одна из которых idn
Она есть почти под все платформы. Умеет как кодировать, так и декодировать:
idn "александр-майоров.рф"
и наоборот:
idn -u "xn----7sbabkjf0beiqjsayfh.xn--p1ai"