Функция генерации красивого ЧПУ

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

Задумался как-то о генерации ЧПУ, правильного с моей точки зрения. Ранее я в своих скриптах вбивал его просто вручную, не доверяя это важное дело автоматизации. Однако мне, наконец, надоело это делать.

Итак, что я считаю хорошим ЧПУ. Ни в коем случае это не кириллица (иногда в ЧПУ просто пихают title страницы) и не словосочетание, полученное с помощью транслитерации. Я считаю эти подходы ересью.

Для меня хороший ЧеловекоПонятный Урл – это слово или фраза на английском, разделённая минусами. Конечно же, при этом все специальные символы должны быть удалены, и все буквы переведены в нижний регистр.

Решение проблемы

Чтобы получить ЧПУ, нужно просто прогнать заголовок страницы через определённую функцию. Функцию напишем ниже.

Для решения воспользуемся API от Google Translate и соорудим функцию перевода. На самом деле я не писал её самостоятельно. Я ведь умею пользоваться Гуглом :)

Читаем статью и получаем готовую функцию для перевода текста с одного языка на другой. Кстати функция архиполезная. Можно использовать и в других местах, и для других языков.

Затем нам нужно обработать результат: удалить слэши, кавычки, амперсанд, прочие специальные символы; удалить пробелы, полученные в результате чистки; заменить оставшиеся пробелы минусами, и, наконец, удалить лишние минусы. Последним штрихом будет перевод строки в нижний регистр.

Внимание, код

Привожу полученную мной функцию.

function CreateURL($text, $fromLang, $toLang)
{
    // составляем URL для запроса к API
    $url = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=' . urlencode($text) . '&langpair=' . urlencode($fromLang . '|' . $toLang);

    // обращаемся к API и декодируем результат
    $translate = file_get_contents($url);
    $json = json_decode($translate, true);

    // если ответ не успешный, то возвращаем false
    if ($json['responseStatus'] != 200)
        return false;

    $result = $json['responseData']['translatedText']; // получаем перевод
    $result = stripslashes($result); // удаляем слэши
    $result = str_replace('#39;', '', $result); // удаляем одинарные кавычки
    $result = str_replace('"', '', $result); // удаляем двойные кавычки
    $result = str_replace('&', '', $result); // удаляем амперсанд
    $result = preg_replace('/([?!:^~|@№$–=+*&%.,;\[\]<>()_—«»#\/]+)/', '', $result); // удаляем недоспустимые символы
    $result = trim($result); // удаляем пробелы по бокам
    $result = preg_replace('/ +/', '-', $result); // пробелы заменяем на минусы
    $result = preg_replace('/-+/', '-', $result); // удаляем лишние минусы
    $result = preg_replace('/([-]*)(.+)([-]*)/', '\\2', $result); // удаляем лишние минусы

    return strtolower($result);
}

Вначале в этой функции использовался cURL, как и в исходной функции перевода. Однако благодаря справедливому замечанию Тормоза код заметно упрощён.

Конечно функция не идеальна и иногда придётся поправить урл руками, но часть времени в любом случае сэкономлена. Enjoy.

Просьба ко всем. При нахождении косяков, пишите комментарии. Спасибо заранее.

Демонстрация работы функции

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

Сам довольно долго игрался демкой. Результат очень понравился. Переводит Гугл отлично, и лишние символы не проходят.

Очень важное обновление

Благодаря Sergey M. и Kama обновлены регулярки. А товарищ Pensioner даже озаботился созданием плагина для WordPress. В конце концов, общими усилиями, получился вот такой плагин Rus-to-Eng. Вот так можно засветиться в идеологах плагина и получить ссылку в официальном репозитории ВордПресса :)

Конечная функция выглядит так:

function CreateURL($title)
{
    // составляем URL для запроса к API
    $url = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=' . urlencode($title) . '&langpair=ru%7Cen';

    // обращаемся к API и декодируем результат
    $translate = file_get_contents($url);
    $json = json_decode($translate, true);

    // если ответ не успешный, то возвращаем false
    if ($json['responseStatus'] != 200)
        return false;

    $result = $json['responseData']['translatedText']; // получаем перевод
    $result = htmlspecialchars_decode($result); // декодируем строку после перевода
    $result = stripslashes($result); // удаляем слэши
    $result = preg_replace('~\W~', '-', $result); // заменяем все специфические символы на минусы
    $result = preg_replace('~-+~', '-', $result); // удаляем лишние минусы
    $result = trim($result, '-'); // удаляем пробелы и минусы по бокам

    return strtolower($result);
}

Демка тоже естественно обновлена.

  • Или с помощью

Получить в подарок мини-книги и 21-дневный тренинг по личностному росту.

Подписаться на рассылку «Инструменты Интернет для онлайн бизнеса»

Поделиться ссылкой:


Комментарии:

26.10.2010 22:22:40

Интересно, возьму на заметку.
А насчет велосипедов - если бы их не изобретали, ничего нового открыть бы не удалось =)

26.10.2010 22:29:46

Teimos, я тоже так считаю. Но среди программистов столько злых людей, которые считают себя пупами Земли :) Они обычно и ворчат по поводу велосипедов и нестандартных решений.

27.10.2010 02:06:33

Отличный велосипед! Годный. Серьезно.

А почему тут не file_get_contents, а cURL? У него синтаксис какой-то непонятный, на память хрен воспроизведёшь.

А ещё не по теме, придирка к оформлению блога :) У тебя стиль трёх кнопочек слева расходится, нет единства. Если птичка и f контурами, то значок RSS тоже красивей было бы сделать простым контурным.

27.10.2010 05:55:05

Тормоз, спасибо. Рад, что тебе понравился.

Почему cURL? Просто потому что функцию перевода я свистнул. Конечно намного проще file_get_contents использовать. Поправил и в тексте и в демке. Работает идентично. Спасибо за подсказку. И как я сам не додумался.

Птичку и f я нашёл в Сети, а RSS рисовать надо было. Но, естественно, стало лень. Потому и отличается :)

27.10.2010 07:01:56

Классная идея. Я обычно то же самое, но вручную делал. Надо попробовать применить функцию.

27.10.2010 07:35:03

Dimox, спасибо. Я вручную задолбался, да и клиентам надо красивые урлы автоматически генерить. Они вручную вбивать уж совсем ленятся.

Тормоз, что ж ты со мной, чертяка, делаешь. Стало стыдно – нарисовал иконку для RSS.

27.10.2010 08:02:44
#7 NnpctO

У всех все работает, но у меня постоянно 404 ошибку показывает! При этом сам блог работает нормально, а вот когда кликою по архиву или по рубрике открывается пустой блог с 404 ошибкой. Но меню справа и верхнее меню на месте только, почему не работает НЕ пойму?! Уже все перерыл, а Wordpress свой так и не могу распинать. Rustolat плагин установил и один фиг не пашет!

27.10.2010 08:12:00

NnpctO, это вы про свой Вордпресс ругаетесь или на местном движке заметили буйство ошибки 404?

Rustolat транслитерирует заголовок, что по сути полная хрень.

27.10.2010 08:49:30
#9 NnpctO

Never Lex,
Да против вордпреса я ничего не имею. У меня вместо страниц появляется 404 ошибка. Но когда пишу ссылку на русском открывается нужный материал. к примеру http://mysite.ru/категория вот именно так открывается а не как category. вот в чем проблема.

27.10.2010 08:58:33

NnpctO, а я имею. Не нравится он мне. Немного я с ним помучался и написал свой движочек. Поэтому и не могу подсказать ничего по всеми любимому WP.

27.10.2010 09:37:24

Во, так лучше! Но недостаточно хорошо :) Стиль птицы и буквы гладкий, а значок RSS у тебя с какими-то зазубринками и крупноватый.

27.10.2010 09:41:09

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

27.10.2010 09:41:58

И функцию я бы ещё упростил значительно. В данном случае лучше одна продуманная preg_replace, чем вся эта куча в обработке.

27.10.2010 09:45:21

Тормоз, согласен. Только ни времени ни умения не хватает. Но когда будет время, надо будет подумать.

27.10.2010 10:16:14

А нафиг тебе в 21 веке у HTML такой доктайп страшный? :)

27.10.2010 10:19:19

Тормоз, какой смысл верстать на пятёрке, если сейчас браузеры и половины функций не поддерживают? Жду с нетерпением полной (или почти полной) поддержки пятёрки, но пока обхожусь Стриктом 4.01.

27.10.2010 10:21:56

Не знаю... На своїй кмс пишу саме транслітерацію.... Як на мене, це зручніше) Але скрипт дуже цікавий і класний))))

27.10.2010 10:42:59

Один короткий доктайп уже стоит перехода :) А функции подтянутся постепенно, что плохого, если ты дашь больше возможностей владельцам современных браузеров? Это ведь не скажется негативно на доисторических пользователях.

27.10.2010 10:45:39

Это не велосипед, это лучше. Обращение к переводчику гугла вместо транлитерации - это изящно. Можно перенести такое и в ВП.

27.10.2010 12:39:15

Ура, я прикрутил функцию к WordPress! Так все просто получилось. Never Lex, большое тебе спасибо ;0)

27.10.2010 13:00:01

Тоже прикрутить, что ли? :)

27.10.2010 13:47:21

Dimox, ух ты. Круто! Теперь моя функция живёт в ВордПрессе :) Не думал, что настолько понравится.

Тормоз, конечно прикручивай :)

27.10.2010 13:50:55

Лееень...

27.10.2010 15:26:20

Спасибо,действительно замечательно

27.10.2010 19:40:30

Я бы еще добил, если не получилось перевести (всякое быват), то возвращать не false, а стандартный транслит. А то так можно вовсе без какого-либо ЧПУ остаться, если вдруг перевод не вернется или еще какие глюки поймать. Думаю, ЧПУ в транслите, не так критично в большинстве случаев.

К тому же, блокиратор может сработать, если много запросов за короткий период.

27.10.2010 19:45:11

Kama, в принципе согласен. Хотя можно банально записать в базу mktime(), а потом отредактировать. Всё равно вы проверяете что получилось обычно.

Вообще эту функцию можно повесить на AJAX. Чтобы при заполнении заголовка, заполнялось и поле с ЧПУ. Или кнопочку надо было нажать, например. Тогда автор будет сразу видеть что к чему.

27.10.2010 21:18:07

А есть реальные результаты от такого url? Например, видно, что Google заметил эти слова и подсвечивает их в выдаче?

27.10.2010 22:17:29

Оу, забираю, спасибо! :)

ps: и спасибо димоксу за линк в твитте

28.10.2010 05:25:55

Neolot, Google знает английский и, например, подсвечивает site в урле, если ищется сайт. Можно на примере этого блога потестировать. Но конечно любит больше английские слова. Влияния на ранжирование скорее всего нет. Об этом спросите наших дорогих SEOшников :)

Sonikelf, действительно, спасибо Dimox’у.

28.10.2010 09:00:20
#30 darko

Учи регулярки. Такое кол-во preg_replace просто убивает. Одной слабо было сделать?

28.10.2010 09:11:06

darko, конечно совершенствоваться нужно. НО во-первых, функция работает, что главное. А во-вторых, всем понятна каждая строка. Или ты предлагаешь постить на блоге код, понятный только тебе?

28.10.2010 11:58:33

Ну, на самом деле, он прав:

$result = preg_replace(’/ +/’, ’-’, $result); // пробелы заменяем на минусы
$result = preg_replace(’/-+/’, ’-’, $result)

используешь прямо как str_replace, надо (уборка последовательностей дефисов):

$result = preg_replace(’~(-| )+~’, ’-’, $result);

Вот это [-]* - тоже лишнее, достаточно и -*

Плюс вместо удаления недопустимых символов лучше воспользоваться удалением не допустимых (проверок-то меньше выходит и читаемее):

$result = preg_replace(’~[^A-Za-z0-9_-]~’, ’’, $result);

А раз так, получается, что и убирание спецсимволов стоит отработать. Например, так (ведь Гугл нам возвращает некоторые символы в html-entities):

$result = htmlspecialchars_decode($result);
//а потом стрипслэшес, трим
//и наша функция

И тогда наша функция убирания лишних символов отлично справится с уборкой всего лишнего.

Флаг u еще можно ставить, хотя здесь нам нужны только английские, так что UTF не UTF - без разницы.

В итоге:
$result = htmlspecialchars_decode($result);
$result = stripslashes($result);
$result = trim($result);
$result = preg_replace(’~[^A-Za-z0-9_-]~’, ’’, $result);
$result = preg_replace(’~(-| )+~’, ’-’, $result);

И ни одного комментария (который, кстати, в таком частом использовании захламляет код) - и так все понятно. «Одной», соответственно, тут сделать нельзя, а вытягивать в строку - буэ.

28.10.2010 12:02:17

Сергей М., спасибо огромное! Обязательно учту.

28.10.2010 12:07:53

Забыл еще две вещи - иногда спецсимволы стоят вплотную, поэтому менять недопустимое надо на дефисы, и еще смотреть, чтобы дефисов не было в начале и в конце (тримать, получается, уже бестолку). И, конечно, спускаем регистр первой буквы (можно было бы strtolower, конечно).

$result = htmlspecialchars_decode($result);
$result = stripslashes($result);
$result = preg_replace(’~[^A-Za-z0-9_-]~’, ’-’, $result);
$result = preg_replace(’~(-| )+~’, ’-’, $result);
$result = preg_replace(’~^-?(.+?)-?$~’, ’$1’, $result);
$result = lcfirst($result);

Кстати, может, у ПРОМТа или чего-то подобного есть API? function-generating-handsome-friendly-url все-таки выглядит по идиотски с точки зрения синтаксиса (хотя я думаю, будут примеры и похуже).

28.10.2010 12:09:19

В общем, нужна вставка кода в комментарии, так чтобы кавычки не менялись :)

28.10.2010 12:11:46

ну и, конечно же, третий коммент (на счастье)

в твоем примере в удалении недопустимых символов $-= будет значить "диапазон между $ и =", дефис надо было экранировать.

28.10.2010 12:14:15

тьфу, ну и как всегда торопливость не дает голове покоя: в моем примере
$result = preg_replace(’~(-| )+~’, ’-’, $result); - уже лишнее, достаточно будет

$result = preg_replace(’~-+~’, ’-’, $result);

пробелы уже и так там, в стране Минусятии.

28.10.2010 12:16:23

Сергей М., нет, в той регулярке тире, а не дефис.

Вставку кода в комментариях надо будет сделать, но пока и так неплохо получается :)

Спасибо за правки. Как будет время, обязательно посмотрю всё.

28.10.2010 13:44:07

Искариот молодец какой, не поленился, не то что я.
Я регулярки неплохо знаю, но всё равно иногда жутко лень придумывать выражения :) А ведь зря, эти навыки постоянно нужны.

А ещё есть хорошая табличка - http://nrd.pnpi.spb.ru/UseSoft/Journals/Soft&Script/Soft&Script28/marketer-regular.html

Такую бы ещё поаккуратней.

P.S. Парсер - лох :)

28.10.2010 13:47:19

Тормоз, Искариот молодчага! И тебе за ссылку спасибоу!

28.10.2010 13:48:35

Вот ещё неплохой материал - http://valex.net.ru/regexp.html

28.10.2010 13:50:14

Тормоз, да материала завались. Главное - практика.

28.10.2010 16:33:45

Из того, что написано понял 23, что недостаточно для самостоятельного прикручивания к вордпрессу. Ктото может выложить готовое решение?

28.10.2010 16:36:03

Это, по идее, плагином бы оформить. Возможно, кстати, попозже закажу разработку плагина на свои деньги под TextPattern. Вордпрессеры пусть сами подтягиваются :)

28.10.2010 16:57:55
#45 darko

Never Lex, понятность кода в ущерб производительности? Конечно в данном случае на производительность можно и «насрать», т.к. делается это один раз и «горлышко бутылки» - явно не регэкспы. Но всё-таки такой «понятный» стиль у аффтора может перейти и во все остальные участки кода.
Вообще, регэкспы достаточно понятная вещь, если за плечами есть матчасть. Да и огромные регулярки никто не запрещает писать многострочно с комментариями О.о

28.10.2010 17:54:28

допилил этот код до готового плагина под WP
http://1-sites.info/page/plagin-perevoda-russkogo-urla-v-anglijskij-rustoeng
осталось собственно отшлифовать сами регулярки

28.10.2010 18:08:14

Pensioner,
взял на пробу, посмотрим чо натворил

28.10.2010 20:16:28

Добавил комментарий по исправлениям к уже появившемуся плагину ВП - должен работать теперь :) (хотя аякс, скорее всего, будет притормаживать - в 3 версии он часто санитизирует, при вводе тайтла, но вроде бы ограничений на стуки к API транслейта нет)

@Тормоз: ну почему нет, на почти все языки программирования и финтифлюшки есть читшиты http://www.addedbytes.com/cheat-sheets/regular-expressions-cheat-sheet/ (был еще один, не помню где находил, он, кажется, еще удобнее)

28.10.2010 20:53:58

Сергей М., а как совместно рустулат и рустуенг использовать, чтобы они за титул не конкурировали?

28.10.2010 21:02:31
#50 polonskiy

Интересная реализация. Но ИМХО транслит - лучше, т.к. некоторые слова могут не перевестись и получится каша из транслита и перевода. А это не красиво как для пользователя, так и для поисковика. Пример:
наш заголовок: «мой дембельский альбом»
результат: «my-dembelsky-album»
И в результате у Вас может получиться 30% ссылок на блоге - переведены, 20% - транслит, 50% - мешанина.
Если вбить в Гугл запрос «золото», он подсветит в урлах и «gold», и «zoloto». Яндекс же выделит только «zoloto».
Итого: транслит рулит :)

28.10.2010 21:08:03

Vodka! Putin! Perestrojka! :)
особо порадовал перевод «вот такие вот дела»="these-are-the-things-that-39-s"
А пропорции больше будут зависеть от тематики и словарного запаса блогера, хотя гугл транслейт безукоризненным тоже не назовешь.

29.10.2010 01:03:11

polonskiy, согласен, транслит не хуже.

Насчет плагина для WP от Пенсионера, вешать функцию эту на sanitize_title думаю, более чем неправильно, она используется в WP сплошь и рядом и не только в админке, и не только для титлов!

И еще, хочу сказать, что это: preg_replace(’~[^A-Za-z0-9_-]~’, ’-’, $result); работает в 2 раза медленнее (не поленился проверил, но в данном случае конечно это не важно), чем перечисление всех удаляемых символов (как сделал автор) и фраза Сергея М, - «проверок-то меньше выходит» мне сразу не понравилась, чего нельзя сказать об остальных замечаниях :).

Вот мой вариант «чистки» (я в php новичек, практика):
$result = stripslashes( htmlspecialchars_decode($result) );
$result = preg_replace( array(’!W!’,’!-+!’), ’-’, $result );
$result = strtolower( trim($result, ’-’) );

Автору спасибо за пост, как не крути, прикольная идея! :)

29.10.2010 01:09:16

Чет, обратная косая черта, перед W куда-то убежала, тут:
preg_replace( array(’!W!’,’!-+!’), ’-’, $result );

29.10.2010 09:12:19

polonskiy, каждому своё. Меня лично тошнит от транслита :) А ссылки в любом случае нужно проверять и по необходимости править руками. Функция просто упрощает этот процесс до минимума.

Pensioner, спасибо за продолжение идеи!

Kama, и вам спасибо!

29.10.2010 10:42:15

«Проверок меньше» относилось к читаемости (хотя, конечно, странно, что 5 не работает дольше, чем 20 без отрицаний). W здесь, конечно же лучше - что-то сразу не сообразил - пусть дефис сам заменяется на дефис, и все логичней :)

А вот вбивать по два оператора в строку - дурной тон. Тут, конечно, кода мало, но в больших проектах может выйти боком.

По поводу sanitize_title - функция sanitize_title действительно используется для разного (санитизация путей и пр.), а вот фильтр висит только там, где нам надо. На всякий случай мы можем использовать проверку, аналогичную проводимой в плагине Cyr-To-Lat. Кстати, с ним прекрасно уживается - все старые посты будут работать по старой схеме.

Плагин теперь живет в репозитории Wordpress http://wordpress.org/extend/plugins/rus-to-eng/

29.10.2010 11:19:40

Первая бета Nanote включала как раз такой подход к генерации ссылок, потом из-за соображений сео/читаемости/часто некорректного перевода была заменена на обычную транслитерацию.

29.10.2010 11:26:13

мне так понравилось играться с транслейтом, что сделал пробный автопереводчик контента для WP
http://1-sites.info/page/avtomaticheskij-perevod-kontenta-wordpress
но есть затык с чисткой контента

12.11.2010 06:53:19

$string = preg_replace(’/[^a-z0-9\_-]+/miu’, ’’, $string); - так не проще будет?

26.11.2010 13:09:16
#59 Joshuan

А то же самое но с JS ? У меня глохнет на том, что $.ajax получает почему то пустое тело (data). Хотя если взять адрес (из файрбага напр что б вместе с текстом запроса) то отдаётся нормальный файл....

17.01.2011 15:30:30
#60 Lenaru

Я вот вроде все сделал как и написано и не пойму правильно ли я сделал, ведь сайт почти не изменил свои урлЫ. Может я упуская какую-нибудь мелочь из виду, незнаю. Буду еще стараться разобраться.

29.01.2011 12:41:22

Пробую написать «я вернулся», а мне пишет такое: i-39-m-back

31.03.2011 11:47:06
#62 Дмитрий

По-моему проще сделать сайт на CMS DLE, где ЧПУ уже встроено, чем изобретать велосипед.

31.03.2011 12:11:42

Дмитрий, дело не в CMS, а иногда транслит переводится не правильно и правильнее перевести на инглишь..

01.09.2011 08:42:41

Печаль. Переводчик перестал работать =(

01.09.2011 08:46:40

Dimox, навсегда? поменялось API или временный глюк?

01.09.2011 09:04:00

Похоже, что навсегда. По официальной информации от Гугла API закрыли еще 26 мая этого года. Но оно еще работало до недавнего времени. И вот буквально пару дней назад ЧПУ переводиться перестало.

09.09.2011 09:10:44
#67 Иван

Здравствуйте. Интересный у вас блог, постоянно читаю. Извиняюсь если вопрос будет не по теме. Если делать чпу либо транслитом либо так как в вашем варианте просто перевод на английский, получается выборку из базы данных нужно делать уже по названию статьи, а не по её id, а это как то не совсем гуд, или я ошибаюсь?
Тоесть например в запросе для извлечения из бд данной статьи условие идет так: WHERE url = $url а не id = $id
в $url лежит: function-generating-handsome-friendly-url/

09.09.2011 09:16:50

Иван, всё зависит от вашей CMS. Какая по сути разница, транслит или перевод? Всё равно по айдишнику без лишних запросов достать не получится.

Я, например, в своих скриптах достаю все страницы по полю ’name’. Оно естественно должно быть уникальным.

09.09.2011 11:08:04
#69 Иван

Понятно, спасибо за быстрый ответ. CMS у меня - свой собственный велосипед, как раз сейчас занимаюсь его изобретением )))

19.09.2011 14:42:03
#70 Алеусандр

странно, не работает функция в примере

20.09.2011 15:05:56

Вот за это я люблю WORDPRESS!!! ))))

20.09.2011 16:01:33
#72 NnpctO

Never Lex,
Теперь все ок.

Оставьте комментарий [форматирование]

Пожалуйста, воздержитесь от спама и идиотских высказываний. Жёсткая модерация. Ссылки закрыты атрибутом nofollow, а значит не несут пользы для продвижения!
Ссылки на всё кроме личных блогов и тематических блогов, сходных по тематике с данным, вырезаются.



Мой RSS фид