Прививка от реальности: розовые очки для браузера

  • Tutorial
Почему вокруг так много матерятся? Одно дело, когда на ногу падает молоток, или когда надо срочно сообщить коллеге, что он не успевает сделать макет сайта. Но в Интернете-то у автора всегда должно быть достаточно времени, чтобы подобрать красивую фразу и показать себя грамотным интеллигентным человеком с большим словарным запасом. К сожалению, редок тот случай, когда обсценная лексика действительно уместна — навскидку, один на сотню.

Некоторые владельцы форумов, чатов и блогов борются с обилием мата организационными мерами (устанавливая правила) или техническими (используя парсеры), но самый большой недостаток существующих антимат-систем — это многочисленные ложные срабатывания, порождающие удивительные неологизмы вроде заштричлен, застрапенис и скигей (кто не догадался — в оригинале было слово «скипидар»). Также скрипты (а зачастую — и сами авторы текстов) иногда заменяют буквы из середины бранных слов на звёздочки (***) или символы "#$%^", из-за чего у меня возникает подозрение, что у этих людей вместо половых органов чёрные квадратики.

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

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


Часть 1. Лингвистика. Русский мат по полочкам


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

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

Уникальной особенностью русского мата является то, что изменение одной морфемы (например, приставки) может кардинально изменить семантику слова. Более того, даже слова с одной и той же приставкой (или без неё) могут нести разную смысловую нагрузку в зависимости от суффиксов, стоящих после корня. Окончание никак не влияет на семантику, а служит лишь для обозначения отношения слова к другим членам предложения.

Всего русский язык насчитывает 70 приставок, 20 из которых — заимствованные из греческого и латинского языков (экс-, пост-, гипер-, и т.д.), а 50 — исконно русские. Заимствованные приставки с матерными словами не употребляются, либо употребляются, но настолько редко, что этим можно пренебречь. Заметим также, что каждый из рассматриваемых корней употребляется не со всеми существующими русскими приставками, что значительно сокращает количество существующих комбинаций приставка-корень. Аналогично отсеиваются многие суффиксы.

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

Часть 2. Регулярные выражения


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

Введение в регулярные выражения

Регулярные выражения в JavaScript работают через специальный объект RegExp. Краткая запись регулярного выражения имеет следующий синтаксис:
/выражение/флаги

В самом простом случае между слешами может стоять жёстко заданный набор символов. Например:
/Колбаса/

Такая конструкция найдёт все вхождения подстроки «Колбаса» в тексте. Важно: регистр имеет значение, то есть, строку «колбаса» такое выражение уже не найдёт. Чтобы не учитывать регистр символов, нужно использовать флаг i:
/колбаса/i

Такая конструкция уже соответствует всем вхождениям строк «Колбаса», «колбаса», «КОЛБАСА» и «КоЛбАсА».

Мы, конечно же, хотим, чтобы все предложения в обработанном тексте начинались с заглавной буквы, а слова внутри предложения — со строчной. Поэтому мы последовательно применим к тексту оба регулярных выражения, заменив их на одно из синонимичных и столь же эмоционально окрашенных выражений. Для примера, возьмём слово «Страх».
text = text.replace(/Страх/g, randomWord(["Ужас", "Кошмар"]));
text = text.replace(/страх/i, randomWord(["ужас", "кошмар"]));

Метод строкового объекта replace() принимает два аргумента:
  1. Регулярное выражение или искомая подстрока — шаблон, по которому будет производиться поиск в документе;
  2. Строка (или функция, возвращающая строку), которой будут заменены все вхождения шаблона


Рассмотрим более сложный и более близкий к предмету обсуждения случай, когда слово имеет несколько словоформ, которые описывают одно и то же состояние/явление/объект.
Пишем:
text = text.replace(/(О|А|При)фигеть/g, "С ума сойти");
text = text.replace(/(о|а|при)фигеть/i, "с ума сойти");

Конструкция вида (A|B)C соотвествует подстрокам AC и BC. С помощью вышеприведённых строчек кода мы находим все вхождения подстрок «Офигеть», «Афигеть», «Прифигеть», а затем — делаем то же самое, но без учёта регистра.

Конструкцию (A|B) можно использовать сколько угодно раз, на любом уровне вложенности и в любой части выражения. Рассмотрим это на ещё чуть более сложном примере с множественными суффиксами: распространённое наречие «хреново». Это слово имеет огромное количество суффиксных производных: «хреновасто», «хреновенько» и даже «хреновастенько». При составлении регулярного выражения для более грубого аналога мы должны учитывать чередование е/ё в корне и тот факт, что «ё» на письме часто заменяется буквой «е». Сейчас нам это не надо, так что, составим регулярное выражение, учитывающее все эти формы:
/Хренов(аст|)(еньк|)о/

и произведём соответствующую замену:
text = text.replace(/Хренов(аст|)(еньк|)о/g, randomWord(["Плохо", "Печально", "Ужасно", "Кошмарно", "Уныло"]));
text = text.replace(/Хренов(аст|)(еньк|)о/i, randomWord(["плохо", "печально", "ужасно", "кошмарно", "уныло"]));

Аналогично, но учитывая возможные окончания поступим с прилагательным «хреновый» — негативным оценочным суждением по отношению к качеству объекта речи:
text = text.replace(/Хренов(аст|)(еньк|)(ы(й|х|е|м)|о(е|го|й|му)|ая|ий)/g, "Низкого качества");
text = text.replace(/хренов(аст|)(еньк|)(ы(й|х|е|м)|о(е|го|й|му)|ая|ий)/i, "низкого качества");

Путём нехитрого сложения и умножения мы можем сосчитать, что приведённое в примере регулярное выражение
/хренов(аст|)(еньк|)(ы(й|х|е|м)|о(е|го|й|му)|ая|ий)/

может найти 40 возможных форм слова. А в случае с использованием матерного корня и, как следствие, чередованием е/ё — 80.

Если мы хотим найти и заменить слово «Балет» на «Опера» или «Мюзикл», но при этом не изменять слова «Кордебалет», «Арбалет» и другие, то мы должны сделать это так:
text = text.replace(/(\s|^)Балет/g, randomWord(["Опера", " Мюзикл"]));
text = text.replace(/(\s|^)балет/i, randomWord([" опера", "мюзикл"]));

спецсимвол ^ означает начало входных данных. Таким образом, мы находим слово «Балет», идущее после пробела или в самом начале обыскиваемой строки.
Да, сюрприз, конструкция \b, использующаяся для обозначения границы слова, не работает с кириллицей, хотя границу слова, написанного латинскими буквами, определяет на отлично.
Также полезным может оказаться спецсимвол $, обозначающий конец входных данных.

Часть 3. Расширение Google Chrome


Расширение будет состоять из трёх основных частей:
  1. обязательный файл манифеста manifest.json, в котором описаны основные параметры расширения;
  2. собственно, javascript-файл, который будет выполнять всю работу;
  3. Иконки размера 128х128, 48х48 и 16х16;

Манифест прост донельзя.
{
	"manifest_version": 2,
	"name": "У нас не матерятся",
	"version": "1.0",
	"icons": { "16": "icon32.png",
           "48": "icon128.png",
          "128": "icon128.png" },
	"description": "Заменяет нецензурную брань на синонимичные литературные выражения.",
	"content_scripts": 
	[
		{
			"matches": ["*://*/*"],
			"js": ["content_script.js"],
			"run_at": "document_end"
		}
	]
}

О синтаксисе лучше всего узнать всё, что надо, из первоисточника.

Скрипт запускается сразу после загрузки страницы (мы явно указали в манифесте «run_at»: «document_end»), его тело состоит из трёх функций:
  • walk(node) — функция, осуществляющая рекурсивный обход нодов HTML-документа. Если принятый ею нод содержит текст, она передаёт нод в функцию makeItCultural();
  • makeItCultural(textNode) — функция, осуществляющая замену подстрок по шаблонам согласно регулярным выражениям. В случае, если существует несколько вариантов замены, эти варианты передаются в виде массива в функцию randomWord();
  • randomWord(words) — функция, принимающая массив и возвращающая его случайный элемент;


На странице chrome://extensions/ ставим галочку «Developer mode», жмём кнопку «Load unpacked extension...» и выбираем папку с нашим расширением. После этого тестируем его, правим, после каждой правки не забываем нажать на ссылку Reload возле нашего расширения.

Чтобы публиковать расширения в Webstore, нужно один раз заплатить 5 долларов. Вообще, весь этот процесс отлично описан в другой хорошей статье на Хабре, так что, не вижу смысла повторяться.

Заключение


Получившееся в итоге расширение заставляет играть новыми красками ЖЖ Артемия Лебедева и интервью Сергея Шнурова, но ему пока не по зубам Большой Петровский Загиб (ссылку приводить не буду — погуглите сами, оно того стоит). Тема русского мата бесконечно глубока и по ней написан не один десяток академических работ. Однажды физики докажут (или опровергнут) теорию струн, инженеры построят персональные квантовые компьютеры, у каждого встречного в кармане будет гаджет, питающийся от холодного ядерного реактора, а русский мат так и не будет изучен до конца.

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

Готовое расширение можно установить из Chrome Webstore, исходники найти на GitHub.

Список источников


Javascript.ru — Регулярные выражения;
Chrome Web Store — Google Developers
Алексей Плуцер-Сарно. Большой словарь мата, Том I и Том II

P.S. Прошу не материться в комментариях. Не хочу становиться виновником массовых хабрарепрессий.
Share post

Similar posts

Comments 43

    –9
    Дооооо!
      +27
      А можно приделать кнопочку с обратным эффектом? Тогда все страницы в интернете заиграют новыми красками!
        +3
        Я думал об этом. Мне кажется, это должно быть намного проще.
          0
          Краски будут новые.

          Количество бит уменьшится.
          +10
          huify.ru/huifier/
          huify.ru/url_huifier/aHR0cDovL2hhYnJhaGFici5ydQ%3D%3D — и хабы становятся хабами-ху"№ами с событиями-х;№тиями.
            +3
            Это ерунда, вот каганов в свое время делал хороший юзерскрипт, который к сожалению больше не доступен на его сайте.

            Но в принципе логика там действительно довольно простая была.
              +2
              который к сожалению больше не доступен

              Значит просто необходимо вытащить его из коллективного бессознательного, и положить его здесь: pastebin.com/PHs12FYz
                +4
                Спасибо!
                А то на странице материализатора все ссылки нерабочие.

                Кому интересно, скрин результата применения этого скрипта к этой ветке комментариев:
                habrastorage.org/storage2/f70/a2c/15b/f70a2c15b41196245547fc755d036548.png

                Обратите внимание на комментарий oshibka404, насколько точно простой скрипт может угадывать настроения комментария. Что собственно подтверждает основную идею Каганова — чтобы материться много ума не надо.
            0
            Так "материализатор" уже давно есть.
              0
              www.proza.ru/2013/03/05/250 прекрасно описывает первоапрельскуюпредновогоднюю шутку.
                0
                +6
                Сделал фидл с вашим скриптом: http://jsfiddle.net/X6s9T/
                Работает интересно, но пробивает во многих местах :)
                Понажимайте кнопочку Run несколько раз.
                  0
                  Спасибо! Буду дорабатывать.
                    +13
                    Сношать-колотить, кошмар как шикарно!
                      0
                      У меня не всё заменило, причём даже очевидные вещи. Что не так?
                      jsfiddle.net/Esx7e/1/
                        0
                        Плюс, пропал пробел.
                        jsfiddle.net/tt7hc/
                          +3
                          Получившийся вариант «гомосексуалистаса» искренне порадовал.
                      0
                      На сайтах с Ajax навигацей не работает. Например с VK.
                        0
                        Да, есть такая проблема. Есть предложения, как это исправить?
                        Может быть, run_at можно поставить другой?
                          0
                          может, к событиям страницы привязать?
                          или к DOM change?
                          или тупо таймер?.. //хм, таймер не пойдет, там же randomWord
                            0
                            Да, лучше всего привязаться к событию DOMSubtreeModified
                        +5
                        >с нетерпением жду багрепортов


                        >и фичреквестов
                        Индикация применения розовых очков на странице.
                          +1
                          Ой, что-то явно пошло не так.
                          Прямо совсем-совсем не так. Исправляю.

                          Индикация — это хорошее предложение. Спасибо.
                            0
                            В качестве индикации лучше всего кнопочку возле строки адреса. Зачем? Не помешает возможность включения-выключения розовых очков.
                          +1
                          >Хреновастенько/печально
                          Как-то это непохоже на обогащение. Хреновастенько гораздо богаче звучит.
                            +3
                            «печальненько»
                              0
                              А ещё много слов заменяется словом «дурак». И му_ак, и уё_ище, и ху_плёт, и ещё многие, и все они просто дураки. Хотя вот му_дило, имхо, это вообще не то же самое, что дурак.
                                +1
                                Му? дак и дурак две большие разницы. Му? дак, сука, может быть умным и от этого ещё более опасным.
                              0
                              Вспомнил что в онлайн игре Warrock матные слова заменялись тоже (забавно выходило, например: «Suck my grenade»), однако с английским это все гораздо легче.
                                +3
                                /(\s|^)балет/i
                                Не поймает слова́, которым предшествует знак препинания без пробела, например, когда слово «балет» взято в кавычки, или в скобки, или идёт после дефиса в составном слове, не говоря уж о банально пропущенных пробелах после точек и запятых и более редких, но встречающихся конструкциях а-ля «опера&балет». Если \b не работает, то придётся перечислять все небуквенные символы, а не только пробельные.
                                  0
                                  Да, сюрприз, конструкция \b, использующаяся для обозначения границы слова, не работает с кириллицей, хотя границу слова, написанного латинскими буквами, определяет на отлично.

                                  Явно не хватает в JS модификатора u :(

                                  А вообще очень удивил подход :)
                                    +4
                                    <test> Превосходное расширение. Надоешу себе в браузер и буду спокойно серфить, а то надоели эти гомосексуалисты уже. У некоторых каждое второе слово блядь или член. </test>
                                      +1
                                      Интересное приложение. С многокоренными правда беда — слова можно не только приставками модифицировать, но и добавлением нового корня. Ловите еще для доработки:
                                      Добавили корень и регэксп не сработал: Промудох*ебл*дское п*здопро*бище — Промудохдураклядское вагинопро*бище, Зло*бучий — Зло*бучий, Семикрылый пятих*й — Семикрылый пятих*й

                                      Сработал не тот регэксп: Х*ебл*дство — Хдураклядство

                                      Только есть подозрение, что решать данную задачу с помощью регэкспов вообще нельзя — либо будет много пропускать, либо полезут энциклонги и большеэнцикло.
                                        0
                                        Вот именно в таких конструкциях проявляется глубина русского мата… Такое, имхо, грех заменять.
                                        0
                                        Интересное расширение. Хотя, честно признать, не особо понимаю подобного. Да, мат может быть неприятен, но это часть языка. Я не говорю, что одобряю повсеместное его использование, но и огораживать себя от подобного не вижу смысла.

                                        p.s. ожидал, что пост будет от товарища Mithgol'а )
                                          +5
                                          А есле пищут с нарочитыми ашипками? А если спе циаль но ставят пробелы в случайных местах? И, ли да.же знаки препинания? Зameняют некоторые буквы на латинницy, или исп0льзуют цифры? Что, если пытаются собрать б`/кву из нескольки>< символов? Да прсто пропскают илли дублируют буквы? И флудят флудят флудят флудят без мата.

                                          Инструменты модерации отменить всё равно не получится, а если уж они есть, то пусть люди и решают, что делать с нарушителями и какие тут правила. Я считаю, что на тематических форумах и в комментариях мат полностью недопустим.

                                          Хотя в авторских статьях можно на усмотрение автора оставить. Если автор не может написать обзор уязвимости или PHP фреймворка без грубостей — это хороший маркер непрофессионализма, полезно.
                                            0
                                            Я считаю, что на тематических форумах и в комментариях мат полностью недопустим.

                                            В принципе согласен, но иногда так и хочется выразить эмоции при виде поста или коммента явно вводящего людей в заблуждение. Причем в аксиоматической форме. И вот на эту аксиоматическую форму хочется ответить нецензурной.
                                            +1
                                            tema.livejournal.com/1404504.html
                                            Всего три слова неправильных осталось после обработки тёминого поста. Неплохо =)
                                              +1
                                              Сразу вспомнил эпизод масяни, в котором они с хрюнделем писали мат-фильтр для домашней странички.
                                                –1
                                                ++++++++[>+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++>++++++++++++>+++++++++++++>++++++++++++++>+++++++++++++++>++++++++++++++++
                                                  0
                                                  обоснуйте
                                                    0
                                                    Программа обрывается.
                                                    0
                                                    Заменяет «Олеговна» на «Оледерьма» :)

                                                    Only users with full accounts can post comments. Log in, please.