Т.к. нет ответов, отвечаю сам на свой же вопрос:
Делают это браузеры, согласно
спецификации.
Line breaks, as in multi-line text field values, are represented as CR LF pairs, i.e. `%0D%0A'.
Связано это с тем, что в разных ОС используются разные переводы строк:
\r = CR (Carriage Return) // Unix
\n = LF (Line Feed) // Mac OS
\r\n = CR + LF // Windows
и таким образом формат \r\n должен удовлетворять всех одним махом:
Как решать это, пока не ясно, но вроде бы в HTML5 спеке есть заметка
о трех состояниях контента:
- сырой (raw), в котором ничего не должно быть конвертировано
- внутренее (api value), в котором все приводится только к LF (\n)
- для отправки (submission value), в котором все приводится к \r\n
For historical reasons, the element’s value is normalized in three different ways for three different purposes. The raw value is the value as it was originally set. It is not normalized. The API value is the value used in the value IDL attribute. It is normalized so that line breaks use U+000A LINE FEED (LF) characters. Finally, there is the value, as used in form submission and other processing models in this specification. It is normalized so that line breaks use U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pairs, and in addition, if necessary given the element’s wrap attribute, additional line breaks are inserted to wrap the text at the given width.
Соответственно, в теории можно работать напрямую с текстом без конвертации .. но, как всегда, есть куча но, с поддержкой спеки.. надо тестировать..
Для тех кто столкнется с такой же проблемой, источник знаний - англоязычный стековерфлоу, вот по таки запросам: "ENTER converted from \n to \r\n on POST" и "What character represents a new line in a text area"
P.S. Если решу проблему через отлов вставки из буфера, замену и конвертацию через плейсхолдеры, добавлю пример позже.