Конвертер регистра

Преобразуйте текст между camelCase, snake_case, kebab-case, Title Case и другими: регистр с учётом языка и инструменты для строк.

Текст

Формы записи

  • UPPERCASE
    XMLHTTPREQUEST HANDLER FOR ISTANBUL CITY GUIDE
  • lowercase
    xmlhttprequest handler for istanbul city guide
  • Title Case
    Xmlhttprequest Handler For Istanbul City Guide
  • Sentence case
    Xmlhttprequest handler for istanbul city guide
  • camelCase
    xmlHttpRequestHandlerForIstanbulCityGuide
  • PascalCase
    XmlHttpRequestHandlerForIstanbulCityGuide
  • snake_case
    xml_http_request_handler_for_istanbul_city_guide
  • kebab-case
    xml-http-request-handler-for-istanbul-city-guide
  • CONSTANT_CASE
    XML_HTTP_REQUEST_HANDLER_FOR_ISTANBUL_CITY_GUIDE
  • dot.case
    xml.http.request.handler.for.istanbul.city.guide

Операции со строками

Результат
XMLHttpRequest handler for istanbul city guide

Строк: 1

Одно разбиение на слова, десять форм записи

Изменение регистра выглядит самым простым, что может делать программа, и служит источником удивительно большого числа настоящих ошибок. Отчасти потому, что «регистр» — это на самом деле две разные задачи под одним названием. Перевод текста в UPPERCASE или lowercase — это посимвольное отображение, определённое в Unicode. Перевод в camelCase или snake_case — совсем другое: сначала нужно решить, где находятся слова, и только потом менять регистр и собирать заново. Этот инструмент делает и то и другое, показывает все результаты сразу и, что важнее, говорит, какие из ответов зависят от выбранного языка.

Все шесть программистских форм выводятся из одного разбиения, поэтому они никогда не могут противоречить друг другу. Вот его границы:

  • Строчная буква, за которой идёт заглавная, начинает новое слово: именно так fooBar превращается в foo и Bar.
  • Последовательность заглавных заканчивается там, где начинается следующее слово, то есть на последней заглавной перед строчной. XMLHttpRequest даёт XML, Http и Request; IOError даёт IO и Error.
  • Цифра, за которой идёт заглавная, начинает новое слово, поэтому html5Parser даёт html5 и Parser.
  • Всё, что не буква, не цифра и не комбинирующий знак, разделяет слова: пробелы, подчёркивания, дефисы, точки, знаки препинания.

В остальном цифры остаются приклеенными к предшествующему слову, поэтому utf8 и address2 проходят целиком. Это осознанное расхождение с lodash, чей разделитель считает каждую последовательность цифр отдельным словом и превращает utf8 в utf_8 — технически последовательно, но не то, что имел в виду человек, называвший переменную.

Разбиение написано через свойства Unicode, а не через класс символов ASCII, поэтому оно не ограничено английским. Идентификатор на кириллице делится по границам регистра точно так же, как латинский. А в письменности, где регистра нет вовсе — иврит, арабский, китайский, японский, корейский, — границ регистра просто не существует, поэтому слова там делятся только по разделителям, и это правильный ответ, а не ограничение.

Аббревиатуры: два правильных ответа

После того как XMLHttpRequest разбит на XML, Http и Request, сборка обратно в PascalCase ставит вопрос, у которого нет единственно верного ответа. Если поменять регистр у каждого слова, получится XmlHttpRequest — то, что выдаёт lodash и чего требует руководство по стилю Google для Java: считать аббревиатуру обычным словом, чтобы границы оставались видны в именах вроде ParseXmlDocument. Если сохранить последовательность заглавных, получится XMLHttpRequest — настоящее имя браузерного API и соглашение, которому следует большая часть DOM и многое в ‎.NET.

Обе формы встречаются в настоящих кодовых базах, поэтому инструмент предлагает обе, а по умолчанию меняет регистр. Опция влияет только на camelCase и PascalCase — остальные формы всё равно перебирают слово целиком, так что XMLHttpRequest в любом случае даёт xml_http_request и XML_HTTP_REQUEST.

Одна деталь зафиксирована и не выбирается: первое слово имени в camelCase всегда переводится в нижний регистр полностью, даже если это аббревиатура. Сохранить её значило бы получить PascalCase, а сохранить наполовину — xMLHttpRequest, форму, которую не рекомендует ни одно руководство по стилю и на которой спотыкается любой читатель. Поэтому XMLHttpRequest в обоих режимах даёт xmlHttpRequest, тогда как parseXMLDocument сохраняет аббревиатуру в середине, когда вы этого просите.

Турецкая i и почему язык — это выбор

В JavaScript два метода перевода в верхний регистр, и разница между ними роняла системы в продакшене. Метод toUpperCase применяет не зависящее от языка отображение Unicode: буква i становится I. Метод toLocaleUpperCase применяет правила конкретного языка, и в турецком и азербайджанском заглавной для i является İ с точкой, потому что в этих алфавитах есть ещё и ı без точки, заглавной для которой служит I. Переведите ввод турецкого пользователя в верхний регистр по его языку, сравните с ключевым словом протокола — и сравнение провалится на символе, который выглядит почти так же.

Это самая частая ошибка интернационализации при работе со строками, и она невидима, пока не случится с вами. Поэтому инструмент никогда не угадывает. По умолчанию используется не зависящее от языка отображение — именно то, что нужно идентификаторам, токенам протоколов, заголовкам HTTP и ключам в базе данных. Выбор языка переключает на его правила, и всякий раз, когда выбор меняет результат, инструмент показывает рядом не зависящий от языка ответ, чтобы было видно, что именно сделал язык.

'i'.toUpperCase()             // 'I'
'i'.toLocaleUpperCase('tr')   // 'İ'  dotted capital
'I'.toLocaleLowerCase('tr')   // 'ı'  dotless lowercase
'ISTANBUL'.toLocaleLowerCase('tr')  // 'ıstanbul'

Ещё три языка меняют ответ, и все они есть в списке. Литовский сохраняет точку над i, когда переводит в нижний регистр заглавную с ещё одним знаком: Ì становится i, за которой следуют комбинирующая точка и комбинирующий гравис, а не единственная предсоставленная ì. Греческий убирает тонос при переводе в верхний регистр, поэтому άνθρωπος даёт ΑΝΘΡΩΠΟΣ, а не ΆΝΘΡΩΠΟΣ, ведь по-гречески ударение на заглавных не пишут. Азербайджанский разделяет с турецким i с точкой и без неё.

Два знаменитых случая правилами языка не являются, и стоит понимать, где что. Немецкая ß переходит в SS в любом языке, потому что это правило SpecialCasing из Unicode, а не немецкое, — по той же причине отображение необратимо, и SS переходит обратно в ss. А греческая сигма в конце слова переходит в конечную форму ς, а не σ, везде: это контекстное условие на позицию символа, а не языковая настройка. Оба видны в этом инструменте вообще без выбора языка.

Title case и Sentence case здесь механические

Форма Title Case в этом инструменте делает первую букву каждого слова заглавной, а остальные строчными. Форма Sentence case делает заглавной первую букву текста и первую букву каждого следующего предложения, где предложение заканчивается точкой, восклицательным или вопросительным знаком либо многоточием, за которым идёт пробел. Обе сначала переводят ввод в нижний регистр — именно поэтому они полезны для текста, пришедшего КРИКОМ.

Это намеренно механические правила, и альтернативу стоит объяснить. Английские руководства по стилю определяют заголовочный регистр через список коротких слов, остающихся строчными, — и расходятся друг с другом и в самом списке, и в вопросе о слове после двоеточия, и о предлогах из четырёх букв и длиннее. Применить одно из них здесь значило бы поставлять американские газетные соглашения читателям, которые пишут на иврите, японском или турецком, где это понятие вообще не переносится. Делать заглавным каждое слово предсказуемо, объяснимо на любом языке и легко правится вручную.

Условие пробела в Sentence case и не даёт 3.5 metres превратиться в 3.5 Metres, ведь точка там стоит перед цифрой. Сокращения оно не решает: e.g. this по-прежнему начинает ложное предложение, потому что знание о том, что «e.g.» — не конец предложения, требует списка сокращений этого языка. Апостроф внутри слова тоже не начинает новое слово, поэтому it's получает заглавную один раз и даёт It's, а не It'S.

Сортировка: алфавитный порядок или порядок байтов

Строковые операции обрезают пробелы, удаляют дубликаты, сортируют, переворачивают и нумеруют, и сортировка поднимает вопрос о языке во второй раз. Алфавитный порядок — свойство языка, а не Unicode. Немецкий ставит ä рядом с a: a, ä, z. Шведский считает её отдельной буквой в конце алфавита: a, z, ä. Оба варианта верны в своей стране, и инструмент использует тот язык, который вы выбрали вверху страницы.

Второй вариант — порядок кодовых точек, и он нужен, когда ответ обязан совпасть с чем-то ещё. Сравнение по кодовой точке есть порядок байтов в UTF-8, поэтому оно в точности воспроизводит то, что выдают LC_ALL=C sort, git и сравнение строк по умолчанию в большинстве языков программирования: все заглавные раньше всех строчных, так что B идёт перед a. Выбирайте его, когда сравниваете с выводом инструмента, и алфавитный порядок — когда список будет читать человек.

Естественный порядок чисел — item2 перед item10, а не после — это возможность языкового сравнения, поэтому он доступен только с алфавитным правилом. У порядка байтов нет понятия числа внутри строки, как нет его и у той сортировки, которую он повторяет, так что предлагать их вместе значило бы обещать то, чего вывод не сможет выполнить.

Операции выполняются в фиксированном порядке, и порядок имеет значение. Обрезка идёт до удаления дубликатов, поэтому две строки, различавшиеся только пробелом в конце, распознаются как одна. Сортировка идёт до переворота, поэтому переворот всегда означает «переверни то, что я вижу». Сообщается и число пустых строк, и число удалённых дубликатов, потому что молчаливый подсчёт — это как раз то, из-за чего не замечают, что список был не таким, как вы думали.

Где это работает

Всё происходит в вашем браузере. Отображения регистра берутся из собственной реализации Unicode в движке JavaScript, а сравнение — из Intl.Collator, поэтому ответы согласуются с той средой выполнения, которая на самом деле будет обрабатывать ваши строки, а не с таблицей, скопированной кем-то много лет назад. Ничего из вставленного не загружается, не сохраняется и не логируется, так что инструментом безопасно пользоваться на именах, строках логов и всём остальном, что вы предпочли бы не отправлять на сервер только ради смены регистра.

Частые вопросы

Отправляется ли мой текст на сервер?
Нет. Каждое преобразование выполняется в вашем браузере с помощью собственных отображений регистра Unicode в движке JavaScript, и ничего из вставленного не загружается и не логируется.
Почему при выборе турецкого показываются два разных результата?
Потому что турецкий меняет ответ. В его алфавите есть и i с точкой, и ı без неё, поэтому заглавной для i служит İ, а не I. Всякий раз, когда язык меняет результат, инструмент показывает рядом не зависящий от языка ответ, ведь именно он нужен идентификаторам и токенам протоколов.
Использовать отображение без языка или языковое?
Используйте не зависящее от языка отображение для всего, что читает машина: идентификаторов, заголовков HTTP, ключей в базе данных, токенов протоколов, имён файлов. Языковое — для текста, который человек прочитает на этом языке. Их смешение и есть классическая ошибка с турецкой i.
Почему XMLHttpRequest превращается в XmlHttpRequest?
Потому что по умолчанию у аббревиатур меняется регистр — так делают lodash и несколько крупных руководств по стилю. Включите опцию аббревиатур, чтобы сохранить последовательность заглавных и вернуть XMLHttpRequest. Она влияет только на camelCase и PascalCase, ведь остальные формы всё равно перебирают каждое слово.
Почему utf8 остаётся одним словом, а lodash его делит?
Потому что здесь цифры приклеиваются к предшествующему слову, поэтому utf8 и address2 проходят целиком. А вот цифра, за которой идёт заглавная, новое слово начинает — именно это делит html5Parser на html5 и Parser.
Почему Title Case делает заглавными короткие слова вроде «of» и «the»?
Потому что правило намеренно механическое. Английские руководства по стилю расходятся в том, какие короткие слова остаются строчными, а их соглашения не переносятся на остальные языки этого сайта. Делать заглавным каждое слово предсказуемо и легко правится вручную.
Почему нельзя включить естественный порядок чисел вместе с порядком кодовых точек?
Естественный порядок — возможность языкового сравнения. Порядок кодовых точек — это порядок байтов, у которого нет понятия числа внутри строки, как нет его и у LC_ALL=C sort, который он воспроизводит. Предлагать оба вместе означало бы выдавать результат, уже не совпадающий с тем инструментом, который он повторяет.
Почему ß становится SS даже без выбранного языка?
Потому что это отображение — правило SpecialCasing из Unicode, а не правило немецкого языка, поэтому оно действует везде. Оно также необратимо: SS переходит в ss, а не обратно в ß. Греческая конечная сигма работает так же — она зависит от позиции буквы, а не от языка.