Грамотно используй lang, dir и объявление кодировки и обходи ловушки нормализации Unicode и направления письма.
Открыть этот урок в KodokonАтрибут lang - гораздо больше, чем метаданные: он управляет голосами программ чтения с экрана, переносами слов (hyphens: auto без него не работает), выбором начертаний CJK (один и тот же иероглиф рисуется по-разному в японском и китайском), кавычками, которые генерирует <q>, и CSS-селектором :lang(). Он наследуется: объяви его на <html> с тегом BCP 47 (en, en-GB, ja) и переопределяй локально при каждой смене языка.
<html lang="en">
<p>She said: <q>hello</q>.</p>
<p lang="fr">Elle a dit : <q>bonjour</q>.</p>
</html>dir=rtl переворачивает выравнивание по умолчанию и визуальный порядок символов с нейтральным направлением. Для непредсказуемого пользовательского содержимого dir=auto выбирает направление по первому символу с сильным направлением. Элемент <bdi> изолирует фрагмент от двунаправленного алгоритма: без него у арабского имени пользователя, за которым идёт «: 12 очков», знаки препинания переставляются в бессмыслицу.
<p dir="auto">مرحبا - inferred direction: RTL</p>
<ul>
<li><bdi>مستخدم</bdi>: 12 points</li>
<li><bdi>Alice</bdi>: 8 points</li>
</ul>Объявление <meta charset=utf-8> должно находиться в пределах первых 1024 байт документа: браузер выполняет предварительное сканирование, ограниченное этим окном, прежде чем выбрать кодировку. Без объявления и без HTTP-заголовка кодировка по умолчанию зависит от локали пользователя (часто windows-1252 в Западной Европе) - и это никогда не UTF-8. А вот BOM в начале файла перекрывает всё остальное, включая HTTP-заголовок.
const composed = "\u00e9";
const decomposed = "e\u0301";
console.log(composed === decomposed); // false
const nfc = decomposed.normalize("NFC");
console.log(composed === nfc); // truemacOS хранит некоторые имена файлов в разложенной форме (NFD): «é», пришедший из загруженного файла, может поэтому отличаться от того, что набран на клавиатуре. Систематически нормализуй весь пользовательский ввод в NFC, прежде чем сохранять или сравнивать его. Наконец, .length считает кодовые единицы UTF-16, а не воспринимаемые символы: эмодзи семьи объединяет больше семи из них; используй Intl.Segmenter, чтобы считать настоящие графемы.
<meta charset=utf-8>?<head><body>dir=auto определяет направление элемента?<html>===?