Регистр символов в JavaScript основан на Unicode и не является универсально обратимым преобразованием: перевод строки в верхний или нижний регистр зависит не только от самих символов, но и от языковой локали. Это приводит к тому, что одна и та же операция может давать разные результаты в зависимости от региональных правил, особенно в языках с особыми алфавитными особенностями.
Базовые методы работы с регистром в Jav * aScript:
String.prototype.toLowerCase()
String.prototype.toUpperCase()
локализованные версии:
String.prototype.toLocaleLowerCase()String.prototype.toLocaleUpperCase()Стандартные методы без локали используют Unicode Simple Case Mapping. Он быстрый и предсказуемый, но не учитывает языковые особенности. Например, в некоторых языках существуют контекстные преобразования, которые невозможно выразить одной статической таблицей соответствий.
Unicode определяет не только простое сопоставление символов, но и полное case folding — более сложный процесс, используемый для сравнения строк без учета регистра. Однако case folding не всегда совпадает с визуальным преобразованием регистра, что важно при работе с локализованными интерфейсами.
Методы toLocaleLowerCase(locales) и
toLocaleUpperCase(locales) учитывают языковые правила.
Параметр locales позволяет указать BCP 47 тег языка:
str.toLocaleLowerCase('tr')
str.toLocaleUpperCase('tr')
Если параметр не указан, среда выполнения использует локаль по умолчанию.
Ключевая особенность: в некоторых языках преобразование регистра зависит от контекста символов, а не только от их кода.
Наиболее известный пример локально-зависимого поведения — турецкий и азербайджанский языки.
В этих языках существуют четыре различных символа:
i (латинская i с точкой)I (латинская I без точки)İ (I с точкой сверху)ı (i без точки)Стандартное преобразование без локали:
"I".toLowerCase()
// "i"
Однако в турецкой локали:
"I".toLocaleLowerCase('tr')
// "ı"
И наоборот:
"i".toLocaleUpperCase('tr')
// "İ"
Такое поведение критично при обработке пользовательского ввода, поиске и нормализации строк.
В контексте Intl API важно различать два процесса:
Это преобразование регистра для отображения текста:
toUpperCase, toLowerCase,
toLocaleLowerCase, toLocaleUpperCaseИспользуется для сравнения строк без учета регистра:
Пример различия:
"Straße".toLowerCase()
// "straße"
"Straße".toLocaleLowerCase('de')
// "straße"
Но при case folding для сравнения:
ß может быть эквивалентен "ss" в некоторых
алгоритмахДля корректной работы с регистром в сравнении строк используется
Intl.Collator.
const collator = new Intl.Collator('en', { sensitivity: 'base' });
collator.compare('a', 'A')
// 0
Параметр sensitivity управляет учетом регистра:
"base" — игнорируются регистр и диакритика"accent" — игнорируются только диакритические
знаки"case" — игнорируется диакритика, но учитывается
регистр"variant" — учитываются все различияconst c1 = new Intl.Collator('en', { sensitivity: 'case' });
c1.compare('a', 'A')
// 0 (регистронезависимо)
const c2 = new Intl.Collator('en', { sensitivity: 'variant' });
c2.compare('a', 'A')
// -1 или 1 (зависит от порядка)
Таким образом, Intl.Collator обеспечивает более точный
контроль над тем, как регистр влияет на сравнение строк, чем простые
методы преобразования.
Локаль влияет не только на отдельные символы, но и на правила сортировки и сравнения, что косвенно связано с регистром.
В немецком языке символ ß имеет особое поведение:
"ß".toLocaleUpperCase('de')
// "SS"
При этом обратное преобразование может не быть симметричным:
"SS".toLocaleLowerCase('de')
// "ss"
Это создает неоднозначность при обратимых преобразованиях регистра.
В этих языках регистр в основном следует стандартному
Unicode-мэппингу, но сортировка может учитывать дополнительные правила
акцентуации, что влияет на поведение Intl.Collator.
Преобразования регистра не гарантируют обратимость:
const a = "Straße";
const b = a.toLocaleUpperCase('de').toLocaleLowerCase('de');
a === b
// false
Причина:
ß → SS при upper-caseSS не всегда возвращается обратно в ßЭто важно учитывать при нормализации данных, особенно при хранении идентификаторов или сравнении строк в базах данных.
Если локаль не указана:
str.toLocaleLowerCase()
str.toLocaleUpperCase()
движок использует системную локаль окружения. Это может приводить к различным результатам в разных средах выполнения (браузер, Node.js, серверные контейнеры).
Поэтому в прикладной логике часто фиксируют локаль явно:
str.toLocaleLowerCase('en')
При обработке пользовательских данных регистр часто используется как часть логики идентификации:
Однако использование toLowerCase() без локали может
привести к ошибкам в международных системах. Более корректный подход —
сочетание:
NFC/NFKC)Intl.Collator для сравненияconst normalized = str.normalize('NFKC').toLocaleLowerCase('tr');
Локализованные методы:
toLocaleLowerCasetoLocaleUpperCaseIntl.Collatorмогут быть медленнее простых Unicode-операций, поскольку требуют доступа к языковым таблицам и правилам сопоставления.
В высоконагруженных системах это учитывается при выборе стратегии:
Хотя Intl API напрямую не предоставляет отдельного “case conversion service”, он определяет поведение через:
Intl.Collator)Таким образом, работа с регистром в JavaScript является не изолированной операцией, а частью более широкой системы локализованных правил обработки текста, где Intl API выступает координирующим слоем между Unicode-стандартом и языковыми особенностями.