I. В чём проблема


Многие компьютерные технологии, разработанные изначально в мире аналитического языка , при переносе в сообщество с синтетическим языком натыкаются на дополнительные трудности.

Например, поиск с учётом морфологии в английских и русских текстах требует разного уровня сложности. Разветвлённость русского словоизменения уже давно стала предметом расхожих шуток о мучениях иностранцев, изучающих русскую грамматику со всеми нормами и исключениями.

Один из примеров того, как технология натыкается на разницу языков, являют теги в английских и русских блогах и социальных сетях. Пока теги выделяются в отдельный блок (как это реализовано на Хабрахабре или в ЖЖ), проблем нет: в обоих языках используются начальные формы слов, иногда множественное число (и тут даже английский язык являет остатки былого синтетизма). Но как только теги попадают в текст, разница обостряется. И порой кажется, что, например, хештеги Твиттера становятся мощным фактором усиливающегося аналитизма в русском языке. То и дело натыкаешься на фразы вроде:

Мы с #муж в ресторане.

С завтрашнего дня в #Москва.

Вернулись с #море.

Возникает очень странное чувство, некоторое языковое головокружение и раздвоение.

Люди, которые не хотят мириться с такое выпирающей неестественностью, решают проблему по-разному.

Кто-то выносит теги в конец твита, как бы в отдельный блок.

Вернулись. #море #отпуск

Кто-то выносит теги вперёд, как бы дополнительно вычленяя тему следующего предложения.

#Москва. С завтрашнего дня.

Кто-то превращает в теги все словоформы. Хотя это существенно уменьшает потенциал поиска по тегам — ведь морфологию к ним ещё не прикрутили.

Мы с #мужем в ресторане.

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

Мы с #муж-ем в ресторане.

Мы с #муж'ем в ресторане.

Мы с #муж ем в ресторане.

Такое решение хорошо лишь для тех слов, у которых в начальной форме так называемое нулевое окончание, то есть у которых начальная форма совпадает с основой слова, к которой прибавляются окончания других падежей. С другими словами такое оформление не имеет смысла, ведь никто не будет искать по тегу, который не является полноценным словом. Сравните:

Улицы #Киев-а. Вечерним #Киев-ом. Мы в #Киев-е.

Улицы #Москв-ы. Вечерней #Москв-ой. Мы в #Москв-е.

Но всё же слов с совпадающей основой и начальной формой достаточно много, чтобы найти для их тегирования читабельную реализацию.

II. Два возможных решения и их особенности


На самом деле в наборах символов есть два подходящих знака, которые одновременно и прерывают тегирование, и не оставляют никакого видимого промежутка между тегом-основой и окончанием: это мягкий перенос (сокращённо «shy») и пробел нулевой ширины (сокращённо «zwsp»).

Их применение позволяет создавать, например, вот такие хештеги в Твиттере:



Или такие в Фейсбуке (предыдущий вариант не получится, потому что Фейсбук фильтрует одинаковые хештеги, оставляя тегом только первый встретившийся):



У двух символов есть ряд общих черт.

1. В большинстве случаев они невидимы внутри слова.

2. Они становятся местом переноса части слова на другую строку, когда слово уже не помещается целиком.

3. Они не дают разрываться частям слова при выравнивании текста по ширине (по крайней мере, в основных браузерах последних версий).

4. Обнаружить оба знака в слове можно следующими способами: а. при перемещении каретки от символа к символу стрелками с клавиатуры каретка на месте невидимого символа один раз «пробуксовывает», как бы не реагирует на нажатие клавиши; б. при добавлении пробелов перед словом оно рано или поздно, походя к концу строки, «разламывается» на две части в месте вставленного знака; в. некоторые текстовые редакторы (чаще программистские) отображают невидимые символы (например, нулевой пробел можно увидеть в известном сервисе jsfiddle.net, а вот мягкий перенос там не отображается; кстати, можно понаблюдать за поведением текста с такими скрытыми символами, если поперемещать рамки блоков: jsfiddle.net/k37ssezj (в первом отрезке после каждого «слова» вставлен нулевой пробел, во втором — мягкий перенос)).

Но есть и различия между двумя знаками, и все они в пользу мягкого переноса:

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

2. При переносе части слова на новую строку знак переноса более логичен и более явно обозначает единство слова.

Некоторые различия зависят от приложений.

1. Знаки могут по-разному влиять на поиск по странице (перенос не мешает поиску в Chrome и Firefox, но мешает в IE; пробел не мешает поиску в Chrome, но мешает в Firefox и IE).

2. При двойном щелчке на слове, содержащем невидимый знак, иногда выделяется только часть слова под курсором, иногда всё слово (при вставке переноса выделение делится только в гибридных тегах Твиттера в Chrome; при вставке пробела — делится во всех словах в Chrome и IE).

3. Нужно помнить про баг IE11: при работе с продвинутыми полями ввода (так называемыми Rich Editor, позволяющими видеть оформление в реальном времени в стиле редакторов WYSIWYG; их порождают свойства element.contentEditable и document.designMode) иногда не работает вставка из буфера обмена — в таком случае в консоли разработчика нужно переключаться из режима Edge в режим совместимости с более низкой версией браузера (начиная с IE10). Например, такая проблема проявляется при попытке что-то вставить в текст заметки (Note) в Фейсбуке.

Наконец, на вставку знаков могут по-разному влиять сайты.

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

2. При попытках вставить знак в Twitter скриптом, нужно помнить про этот досадно застаревший баг в Firefox. Придётся или пользоваться другими способами вставки, или устанавливать ключ security.csp.enable из about:config в false, что, наверное, будет слишком радикальным способом решения типографических проблем.

III. Способы реализации


1. Ввод вручную.

Если вы нечасто вводите редкие символы с клавиатуры, возможно, вам будет полезно ознакомиться с этой небольшой статьёй. В ней описывается два метода ввода символов: в десятеричном счислении (надёжно работает только с начальным блоком Юникода и некоторыми символами распространённых кодировок) и в шестнадцатеричном. Для удобства упомянутого в статье редактирования реестра можно сохранить этот текст в файл с расширением .reg и кодировкой Юникод, щёлкнуть на нём и согласиться на внесение данных в реестр.

EnableHexNumpad.reg
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Control Panel\Input Method]
"EnableHexNumpad"="1"



Итак, при вводе вручную мы должны знать порядковый номер наших символов в двух счислениях:

Мягкий перенос: 0173 и 00ad (соответственно Alt + '0173' и Alt + '+00ad').

Нулевой пробел: 8203 и 200b (соответственно Alt + '8203' и Alt + '+200b').

(Кстати, интересно, что нулевой пробел не входит в класс пробельных символов JavaScript '\s': перечисление одной из групп пробелов прерывается как раз перед ним).

Что нужно учесть:

а. Десятеричный ввод символов с большим порядковым номером работает очень редко. Иногда ничего не вставляется, иногда вставляется что-то совсем неожиданное.

б. При вводе шестнадцатеричных кодов, содержащих буквы, часто срабатывают заданные в приложениях клавиатурные сокращения (shortcuts), и ввод символа срывается. Иногда это зависит от текущей клавиатурной раскладки, иногда нет.

2. Вставка из буфера обмена.

Можно где-то сохранить эти два символа, чтобы копировать их оттуда и вставлять через Ctrl+C/Ctrl+V. Хотя хранить и копировать невидимые символы немного труднее, чем видимые: придётся или сохранить каждый в отдельном файле, или знать их место и выделять при помощи клавиатуры (Shift+стрелки).

В Windows можно воспользоваться привычной утилитой, выбрав не самый бедный шрифт, выставив нужные опции, введя порядковый шестнадцатеричный номер и нажав нужные кнопки:





3. Ввод при помощи скрипта (букмарклета).

Чуть дальше будут представлены две программки, идентичные во всём, кроме кода символа и его переменной. Они последовательно перебирают три варианта обстоятельств:

а. Фокус ввода находится в простом однострочном или многострочном текстовом поле. Используются обычные для таких случаев свойства и методы.

б. Фокус ввода находится в поле с расширенными возможностями типа Rich Editor (учитываются варианты как с элементом текущего окна/документа, так и с полностью выделенной под редактор iframe со своим окном/документом). Используется document.execCommand().

в. Фокус не в текстовом поле или нужный метод работы с Rich Editor не реализован в браузере (в IE11 команда insertText не поддерживается, но будет реализована в Edge). В этом случае перед пользователем всплывает окошко с текстовым полем, в котором необходимый символ уже выделен (хоть он при этом и невидим). Остаётся лишь нажать Ctrl+C, закрыть окно (можно нажатием Enter или Esc), потом вставить курсор в нужное место текста и нажать Ctrl+V. Можно считать этот вариант удобной реализацией предыдущего способа копирования и вставки из файла или утилиты.

Мягкий перенос
javascript: (function(d, e, shy, s1, s2, v, sy, sx) {
    if (e.type == 'textarea' || e.type == 'text') {
        s1 = e.selectionStart;
        s2 = e.selectionEnd;
        sy = e.scrollTop;
        sx = e.scrollLeft;
        v = e.value;
        e.value = v.substring(0, s1) + shy + v.substring(s2);
        e.selectionStart = e.selectionEnd = ++s1;
        e.scrollTop = sy;
        e.scrollLeft = sx;
        e.focus();
    } else if ((e.isContentEditable || d.designMode == 'on') && d.queryCommandSupported('insertText') || (d = e.contentDocument) && (d.activeElement.isContentEditable || d.designMode == 'on') && d.queryCommandSupported('insertText')) {
        d.execCommand('insertText', false, shy);
    } else {
        prompt('Copy and paste in the text:', shy);
    }
})(document, document.activeElement, '\u00ad')


Нулевой пробел
javascript: (function(d, e, zwsp, s1, s2, v, sy, sx) {
    if (e.type == 'textarea' || e.type == 'text') {
        s1 = e.selectionStart;
        s2 = e.selectionEnd;
        sy = e.scrollTop;
        sx = e.scrollLeft;
        v = e.value;
        e.value = v.substring(0, s1) + zwsp + v.substring(s2);
        e.selectionStart = e.selectionEnd = ++s1;
        e.scrollTop = sy;
        e.scrollLeft = sx;
        e.focus();
    } else if ((e.isContentEditable || d.designMode == 'on') && d.queryCommandSupported('insertText') || (d = e.contentDocument) && (d.activeElement.isContentEditable || d.designMode == 'on') && d.queryCommandSupported('insertText')) {
        d.execCommand('insertText', false, zwsp);
    } else {
        prompt('Copy and paste in the text:', zwsp);
    }
})(document, document.activeElement, '?\u200b')


В Chrome или Firefox можно создать новую закладку на произвольную страницу, а затем вставить код (от самой первой буквы javascript: до последней скобки после '\u00ad' или '?\u200b' включительно) в поле адреса, изменив по необходимости название закладки.

В Chrome можно просто выделить код и перетащить выделенный текст сразу в закладки, потом изменив название на более читаемое.

В IE оба этих способа невозможны (зато возможен описанный здесь способ создания файла в папке «Избранного»).

Наконец, во всех браузерах можно перетащить в закладки ссылки с этой страницы (в IE11 нужно будет согласиться с сохранением букмарклета).

IV. Особенности применения в разных браузерах и на разных сайтах.


Вот небольшая таблица с результатами испытаний трёх браузеров последних версий в Windows 7 SP 1. Минус или плюс отображают текущий конечный результат, зависящий от суммы обстоятельств. Он может измениться в процессе развития браузеров или сайтов. Более общие обстоятельства отображены в примечаниях к заголовкам строк и колонок, более частные обстоятельства отмечаются в ячейках. Раздельные варианты с постом и заметкой Фейсбука взяты ради разной реализации Rich Editor в них: в первом случае как элемента страницы, во втором — как целого встроенного фрейма со своим окном/документом.



(картинка без уменьшения)

Примечания в таблице:
1. Хештеги в заметках Фейсбука (пока?) не работают, будь то с невидимыми символами, будь то без них.

2. При вводе десятеричного кода нулевого пробела вместо ожидаемого знака во всех браузерах появляется символ > (порядковый номер — 10: 9794, 16: 2642).

3. В Фейсбуке нулевой пробел удаляется сайтом сразу после вставки или после сохранения поста.

4. Вместо мягкого переноса срабатывают клавиатурные сокращения.

5. Предотвратить срабатывание клавиатурного сокращения в Firefox в данном случае можно добавочным зажатием клавиши Win.

6. Проблема зависит от раскладки клавиатуры: или срабатывает клавиатурное сокращение, или вообще ничего не происходит.

7. В Фейсбуке программно вставленные мягкие переносы не всегда сразу «подхватываются» страницей и тем самым не всегда сохраняются при публикации текста, иногда требуется подвигать курсор внутри слова стрелками клавиатуры. Возможно, есть другие способы «актуализации» вставки. Иногда вставка не распознаётся при первоначальном создании поста, но распознаётся при редактировании уже сохранённого.

8. В IE11 в полях типа Rich Editor будет работать только компромиссный метод букмарклета, с копированием и вставкой символа из буфера.

(P.S. Проверка через виртуальную машину подтверждает реализацию insertText в MS Edge версии 11.00.10240.16397 от 7.22.2015 (по версии файла), она же 20.10240.16384.0 (по информации в настройках): document.queryCommandSupported('insertText') возвращает true).

9. В IE11 поле поста Фейсбука становится обычным полем, без возможностей Rich Editor.


По наглядным итогам испытаний в таблице выделены колонки с самыми универсальными — кроссбраузерными и кроссайтовыми — способами.

Спасибо за внимание.

Комментарии (32)


  1. elingur
    07.08.2015 10:23
    +2

    Не очень понятно, вы хотите, чтобы пользователи озаботились правописанием хеш-тегов? Думаю, мало кто этим будет заниматься: читающий может в уме сам достроить/поправить кривую грамматику. Ну а для обработки текста — да, точность упадет, но систему можно обучить на кривизну текста.


    1. maaGames
      07.08.2015 10:29

      Обучение «кривому» тексту практически равняется полнотекстовому поиску (в чуть меньшем масштабе, потому что #), так что тэги вообще не нужны будут.


    1. vmb
      07.08.2015 10:30
      +2

      Нет, я не пытаюсь к чему-то обязать пользователей. Просто делюсь уловками с теми, кто тоже захочет сделать теги более читабельными и естественными. Мне всегда казалось, что автору лучше самому заботиться о ясности письма: потраченное им время с лихвой окупиться временем, которое многие читатели не будут тратить на достраивание чего-то в уме. Там ведь из секунд образуются годы, если помножить на количество читающих во всём времени существования текста.


  1. maaGames
    07.08.2015 10:27
    +2

    #читаю #хабр, #яумный.

    А если серьёзно, разве нынче можно что-то найти по тэгам, когда все в каждый пост по куче тэгов пихают и такая мешанина получается, что всё равно ничего не найдёшь?


    1. vmb
      07.08.2015 10:43

      В общем, в тегах сейчас и правда избыточность и путаница. А тут ещё и экономические и политические технологии примешиваются (рекламный спам при помощи тегов, деятельность ботов). Но, с другой стороны, возрастают и способы увеличения релевантности результатов поиска.

      Для многих Твиттер сейчас становится чуть ли не основным источником оперативной информации. Многие читают в соцсетях очень много по профессии (взять хотя бы репортёров и редакторов СМИ). А экономия места, обычная для Твиттера, иногда приводит к тому, что тексты вообще становятся какими-то нечеловеческими — сплошные сокращения, отмена окончаний, хештеги через каждое слово. Даже с этой стороны предложенный способ поможет экономить место не за счёт ясности и естественности.


  1. Akdmeh
    07.08.2015 11:19
    +1

    Думаю, что-то типа «Вот свежая фотка с #Москвы{Москва}» (при этом показывать только первый вариант, а в самой ссылке сделать поиск по вариант в скобках) могло бы решить проблему очень быстро и удобно, но зачем твиттеру этим заморачиваться? В англоязычных все работает, и хорошо.


    1. vmb
      07.08.2015 11:32

      Да, это могло бы стать хорошим стандартом вообще для всякого использования хештегов. А количество вспомогательных символов можно не учитывать, как не учитывается размер ссылок в твитах.

      Англоязычным бы тоже пригодилось, хотя бы для унификации единственного и множественного числа существительных, а то и для приведения однокоренных существительных/прилагательных/наречий/глаголов к единому гнезду. А можно даже ещё шире: для поисковой унификации синонимических цепочек (как, например, это делается в Лурке: ссылки на одну и ту же статью даются под видом разных синонимов в зависимости от контекста).


    1. agee
      08.08.2015 10:23

      Как будем присекать эксплутацию такого подхода? Например: «Вот я со своим #мужем{ParisHilton}»


      1. vmb
        08.08.2015 10:46

        Так она и сейчас возможна.

        Вот я со своим мужем. #ParisHilton

        Тысячи их.


        1. agee
          08.08.2015 11:04
          +1

          Вот негодяи :) Но в Вашем примере тег виден явно.
          Ну тут, как минимум, чтобы полной подмены не было, можно прийти к конструкции наподобие "#муж{ем}". Было бы желание.
          С Москвой не так просто, конечно. Допустим, "Я в #Москв{а|е}" или "Я в #Москв[а]{е}".

          С другой стороны, для среднестатистиечкого пользователя Твиттера это может быть слишком мудрено. Но кто знает? :)


          1. vmb
            08.08.2015 12:17

            Ну, так ими же спамят для поиска, потом уже поздно, когда видно. Вы можете задать поиск по хештегу #сын или #кот и попасть на десятки твитов типа:



            Да, такой код, наверное, только программистам будет в удовольствие)


  1. vmb
    07.08.2015 11:38
    +3

    Вот, кстати, ещё повод пожалеть о разделении Хабра. Было бы интересно послушать читателей разделов Facebook, Twitter, Социальные сети и сообщества — но не будешь же делать туда кросспост, заругают за умножение сущностей.


    1. Lsh
      07.08.2015 23:43
      +3

      Пора склеивать обратно! Только минусы одни от этого!


  1. dtestyk
    07.08.2015 15:49
    +2

    Современные твиты настолько мало удобочитаемы, что напоминают высказывания какого-то футуристического(прологоподобного) языка программирования: @me #friend @­someuser. Осталось только добавить переменные: $somevariable. Назвал его bundle(связка), так как порядок тегов не влияет на значение высказывания.


    1. agee
      08.08.2015 11:13
      +2

      А что…
      Прописать у себя в конфиге аккаунта:

      [aliases]
      i, I = @me
      wife = @superbaby1997
      <3 = love
      

      И пишешь потом
      I <3 wife в #Москв{а|е}


      1. dtestyk
        08.08.2015 11:53
        +1

        а может быть даже и так:
        #alias i I @me

        ну еще можно добавить какой-нибудь служебный тег, типа:
        #alias #config wife @superbaby1997

        тогда ретвитом можно будет распространять списки синонимов:
        #config #alias #Москва #Москвы #Москве #Москву #Москвой #Moskov


  1. Shchvova
    07.08.2015 20:33
    +1

    У вас очень скудное знание английского судя по картинке. Вот навскидку слова происходящие от «Running»
    Baserunning
    Forerunning
    Gunrunning
    Outrunning
    Overrunning
    Rerunning
    + каждое из этих слов может быть как минимум в трех формах.


    1. vmb
      07.08.2015 21:20
      +1

      Картинка не моя, этот распространённый мем.

      И вы путаете словоизменение и словообразование. Перечисленное вами — это не формы слова run, это однокоренные ему слова. А так-то правда, английский один из самых богатых языков, в том числе по количеству синонимов и однокоренных слов.


      1. Zenitchik
        07.08.2015 23:01

        На картинке тоже приведены однокоренные. Замена префикса и суффикса — это не изменение, а словообразование. В основу слова не входит только окончание.


        1. vmb
          07.08.2015 23:18
          +1

          Да, на картинке есть несколько форм с префиксами, но тут дело осложняется тем, что в русском языке виды глагола, а иногда и времена можно образовывать только при помощи префиксов (вы не сможете образовать совершенный вид или синтетическое будущее время от глагола «бежать» без префикса). Суффиксы же тоже принимают участие в образовании форм — есть суффиксы причастий как форм глагола, например.


          1. deniskreshikhin
            08.08.2015 14:28
            +2

            Тем не менее, табличка предвзятая, почему-то нет runs (бежит) и runnings (бега).


            1. vmb
              08.08.2015 16:06

              Тут не поспоришь. Но это ведь не столько научная табличка, сколько художественный образ, а в них важно донести основную идею.


              1. deniskreshikhin
                08.08.2015 19:20
                +3

                Хех, ну там на самом деле такое разноообразие получается за счёт русской орфографии, мы записываем древнерусские местоимения и, я, е (он, она, оно) и русские на конце слова как часть этого слова:

                бежавши + и = бежавший
                бежавши + я = бежавшая
                бежавши + е = бежавшее

                бежавши + его = бежавшего
                бежавши + ему = бежавшему
                бежавши + ей = бежавшей

                бежавши + им = бежавшим

                и т.д.

                Поэтому получается, что до черта вариантов. А на самом деле вариант один, просто прибавляем местоимения, которые в английском пишутся отдельно.


                1. vmb
                  08.08.2015 19:34
                  -1

                  Ну, языки ведь типологически сравнивают в синхронии, в современном состоянии, а не в диахронии, не в форме их исторических этапов. С точки зрения сегодняшнего дня и сегодняшних носителей это уже просто окончания.


                  1. deniskreshikhin
                    08.08.2015 20:01
                    -1

                    Эм, это структурная типология рассматривается вне диахронии, а типологическое сравнение в общем учитывает и то и другое.
                    В любом случае производя разрозненное сравнение можно прийти к прямо противоположному выводу.
                    Сравните:
                    to be быть
                    was был
                    were был
                    am есть
                    is есть
                    are есть


                    1. vmb
                      08.08.2015 20:15
                      -1

                      Ну, это ведь сравнение исключения с нормой.


                      1. deniskreshikhin
                        08.08.2015 21:11
                        +1

                        «быть» это тоже глагол-исключение в русском: был, есть, буду)))


                        1. vmb
                          08.08.2015 21:55

                          Ну, пусть будет так. Я уже не понимаю, о чём мы спорим, честно говоря. Зря я вообще эту картинку прилепил. Она вообще сбоку припёка, но привлекла к себе почти всё внимание и вызвала какое-то особенное возмущение. Я вообще не об этом писал…

                          Уберу её, пожалуй, вообще.


                          1. deniskreshikhin
                            09.08.2015 12:16
                            +1

                            Вы как-то болезненно реагируете)

                            Это просто стёб, которым тут весь хабр занимается. Понятное дело что русский более замороченный язык чем английский, так что картинка была в тему.

                            Честно говоря если бы не картинка, я бы даже и в тему не зашёл бы)


                            1. vmb
                              09.08.2015 17:59
                              +1

                              Получилось, что картинка «для привлечения внимания» скушала всё внимание)


          1. Zenitchik
            08.08.2015 18:13
            +1

            Формы глагола — это опять-таки новые слова, а не изменение слова. Глагол в настоящем времени спрягается, а в прошедшем — изменяется по родам.


  1. vmb
    25.08.2015 13:46

    (P.S. Проверка через виртуальную машину подтверждает реализацию insertText в MS Edge версии 11.00.10240.16397 от 7.22.2015 (по версии файла), она же 20.10240.16384.0 (по информации в настройках): document.queryCommandSupported('insertText') возвращает true).