Функция генерации красивого ЧПУ
Очень часто бывает, что разработчик имеет свой взгляд на вещи. И это часто ведёт к прогрессу, так как появляются новые подходы и велосипеды. Представляю вам один из них.
Задумался как-то о генерации ЧПУ, правильного с моей точки зрения. Ранее я в своих скриптах вбивал его просто вручную, не доверяя это важное дело автоматизации. Однако мне, наконец, надоело это делать.
Итак, что я считаю хорошим ЧПУ. Ни в коем случае это не кириллица (иногда в ЧПУ просто пихают title страницы) и не словосочетание, полученное с помощью транслитерации. Я считаю эти подходы ересью.
Для меня хороший ЧеловекоПонятный Урл – это слово или фраза на английском, разделённая минусами. Конечно же, при этом все специальные символы должны быть удалены, и все буквы переведены в нижний регистр.
Решение проблемы
Чтобы получить ЧПУ, нужно просто прогнать заголовок страницы через определённую функцию. Функцию напишем ниже.
Для решения воспользуемся API от Google Translate и соорудим функцию перевода. На самом деле я не писал её самостоятельно. Я ведь умею пользоваться Гуглом :)
Читаем статью и получаем готовую функцию для перевода текста с одного языка на другой. Кстати функция архиполезная. Можно использовать и в других местах, и для других языков.
Затем нам нужно обработать результат: удалить слэши, кавычки, амперсанд, прочие специальные символы; удалить пробелы, полученные в результате чистки; заменить оставшиеся пробелы минусами, и, наконец, удалить лишние минусы. Последним штрихом будет перевод строки в нижний регистр.
Внимание, код
Привожу полученную мной функцию.
{
// составляем 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. Вот так можно засветиться в идеологах плагина и получить ссылку в официальном репозитории ВордПресса :)
Конечная функция выглядит так:
{
// составляем 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);
}
Демка тоже естественно обновлена.
Поделиться ссылкой:
Комментарии:
Отличный велосипед! Годный. Серьезно.
А почему тут не file_get_contents, а cURL? У него синтаксис какой-то непонятный, на память хрен воспроизведёшь.
А ещё не по теме, придирка к оформлению блога :) У тебя стиль трёх кнопочек слева расходится, нет единства. Если птичка и f контурами, то значок RSS тоже красивей было бы сделать простым контурным.
Классная идея. Я обычно то же самое, но вручную делал. Надо попробовать применить функцию.
У всех все работает, но у меня постоянно 404 ошибку показывает! При этом сам блог работает нормально, а вот когда кликою по архиву или по рубрике открывается пустой блог с 404 ошибкой. Но меню справа и верхнее меню на месте только, почему не работает НЕ пойму?! Уже все перерыл, а Wordpress свой так и не могу распинать. Rustolat плагин установил и один фиг не пашет!
Во, так лучше! Но недостаточно хорошо :) Стиль птицы и буквы гладкий, а значок RSS у тебя с какими-то зазубринками и крупноватый.
И функцию я бы ещё упростил значительно. В данном случае лучше одна продуманная preg_replace, чем вся эта куча в обработке.
Не знаю... На своїй кмс пишу саме транслітерацію.... Як на мене, це зручніше) Але скрипт дуже цікавий і класний))))
Один короткий доктайп уже стоит перехода :) А функции подтянутся постепенно, что плохого, если ты дашь больше возможностей владельцам современных браузеров? Это ведь не скажется негативно на доисторических пользователях.
Это не велосипед, это лучше. Обращение к переводчику гугла вместо транлитерации - это изящно. Можно перенести такое и в ВП.
Ура, я прикрутил функцию к WordPress! Так все просто получилось. Never Lex, большое тебе спасибо ;0)
Я бы еще добил, если не получилось перевести (всякое быват), то возвращать не false, а стандартный транслит. А то так можно вовсе без какого-либо ЧПУ остаться, если вдруг перевод не вернется или еще какие глюки поймать. Думаю, ЧПУ в транслите, не так критично в большинстве случаев.
К тому же, блокиратор может сработать, если много запросов за короткий период.
А есть реальные результаты от такого url? Например, видно, что Google заметил эти слова и подсвечивает их в выдаче?
Учи регулярки. Такое кол-во preg_replace просто убивает. Одной слабо было сделать?
Ну, на самом деле, он прав:
$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);
И ни одного комментария (который, кстати, в таком частом использовании захламляет код) - и так все понятно. «Одной», соответственно, тут сделать нельзя, а вытягивать в строку - буэ.
Забыл еще две вещи - иногда спецсимволы стоят вплотную, поэтому менять недопустимое надо на дефисы, и еще смотреть, чтобы дефисов не было в начале и в конце (тримать, получается, уже бестолку). И, конечно, спускаем регистр первой буквы (можно было бы 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 все-таки выглядит по идиотски с точки зрения синтаксиса (хотя я думаю, будут примеры и похуже).
В общем, нужна вставка кода в комментарии, так чтобы кавычки не менялись :)
ну и, конечно же, третий коммент (на счастье)
в твоем примере в удалении недопустимых символов $-= будет значить "диапазон между $ и =", дефис надо было экранировать.
тьфу, ну и как всегда торопливость не дает голове покоя: в моем примере
$result = preg_replace(’~(-| )+~’, ’-’, $result); - уже лишнее, достаточно будет
$result = preg_replace(’~-+~’, ’-’, $result);
пробелы уже и так там, в стране Минусятии.
Искариот молодец какой, не поленился, не то что я.
Я регулярки неплохо знаю, но всё равно иногда жутко лень придумывать выражения :) А ведь зря, эти навыки постоянно нужны.
А ещё есть хорошая табличка - http://nrd.pnpi.spb.ru/UseSoft/Journals/Soft&Script/Soft&Script28/marketer-regular.html
Такую бы ещё поаккуратней.
P.S. Парсер - лох :)
Из того, что написано понял 23, что недостаточно для самостоятельного прикручивания к вордпрессу. Ктото может выложить готовое решение?
Это, по идее, плагином бы оформить. Возможно, кстати, попозже закажу разработку плагина на свои деньги под TextPattern. Вордпрессеры пусть сами подтягиваются :)
Never Lex, понятность кода в ущерб производительности? Конечно в данном случае на производительность можно и «насрать», т.к. делается это один раз и «горлышко бутылки» - явно не регэкспы. Но всё-таки такой «понятный» стиль у аффтора может перейти и во все остальные участки кода.
Вообще, регэкспы достаточно понятная вещь, если за плечами есть матчасть. Да и огромные регулярки никто не запрещает писать многострочно с комментариями О.о
допилил этот код до готового плагина под WP
http://1-sites.info/page/plagin-perevoda-russkogo-urla-v-anglijskij-rustoeng
осталось собственно отшлифовать сами регулярки
Добавил комментарий по исправлениям к уже появившемуся плагину ВП - должен работать теперь :) (хотя аякс, скорее всего, будет притормаживать - в 3 версии он часто санитизирует, при вводе тайтла, но вроде бы ограничений на стуки к API транслейта нет)
@Тормоз: ну почему нет, на почти все языки программирования и финтифлюшки есть читшиты http://www.addedbytes.com/cheat-sheets/regular-expressions-cheat-sheet/ (был еще один, не помню где находил, он, кажется, еще удобнее)
Сергей М., а как совместно рустулат и рустуенг использовать, чтобы они за титул не конкурировали?
Интересная реализация. Но ИМХО транслит - лучше, т.к. некоторые слова могут не перевестись и получится каша из транслита и перевода. А это не красиво как для пользователя, так и для поисковика. Пример:
наш заголовок: «мой дембельский альбом»
результат: «my-dembelsky-album»
И в результате у Вас может получиться 30% ссылок на блоге - переведены, 20% - транслит, 50% - мешанина.
Если вбить в Гугл запрос «золото», он подсветит в урлах и «gold», и «zoloto». Яндекс же выделит только «zoloto».
Итого: транслит рулит :)
Vodka! Putin! Perestrojka! :)
особо порадовал перевод «вот такие вот дела»="these-are-the-things-that-39-s"
А пропорции больше будут зависеть от тематики и словарного запаса блогера, хотя гугл транслейт безукоризненным тоже не назовешь.
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, ’-’) );
Автору спасибо за пост, как не крути, прикольная идея! :)
Чет, обратная косая черта, перед W куда-то убежала, тут:
preg_replace( array(’!W!’,’!-+!’), ’-’, $result );
«Проверок меньше» относилось к читаемости (хотя, конечно, странно, что 5 не работает дольше, чем 20 без отрицаний). W здесь, конечно же лучше - что-то сразу не сообразил - пусть дефис сам заменяется на дефис, и все логичней :)
А вот вбивать по два оператора в строку - дурной тон. Тут, конечно, кода мало, но в больших проектах может выйти боком.
По поводу sanitize_title - функция sanitize_title действительно используется для разного (санитизация путей и пр.), а вот фильтр висит только там, где нам надо. На всякий случай мы можем использовать проверку, аналогичную проводимой в плагине Cyr-To-Lat. Кстати, с ним прекрасно уживается - все старые посты будут работать по старой схеме.
Плагин теперь живет в репозитории Wordpress http://wordpress.org/extend/plugins/rus-to-eng/
Первая бета Nanote включала как раз такой подход к генерации ссылок, потом из-за соображений сео/читаемости/часто некорректного перевода была заменена на обычную транслитерацию.
мне так понравилось играться с транслейтом, что сделал пробный автопереводчик контента для WP
http://1-sites.info/page/avtomaticheskij-perevod-kontenta-wordpress
но есть затык с чисткой контента
$string = preg_replace(’/[^a-z0-9\_-]+/miu’, ’’, $string); - так не проще будет?
А то же самое но с JS ? У меня глохнет на том, что $.ajax получает почему то пустое тело (data). Хотя если взять адрес (из файрбага напр что б вместе с текстом запроса) то отдаётся нормальный файл....
Я вот вроде все сделал как и написано и не пойму правильно ли я сделал, ведь сайт почти не изменил свои урлЫ. Может я упуская какую-нибудь мелочь из виду, незнаю. Буду еще стараться разобраться.
По-моему проще сделать сайт на CMS DLE, где ЧПУ уже встроено, чем изобретать велосипед.
Похоже, что навсегда. По официальной информации от Гугла API закрыли еще 26 мая этого года. Но оно еще работало до недавнего времени. И вот буквально пару дней назад ЧПУ переводиться перестало.
Здравствуйте. Интересный у вас блог, постоянно читаю. Извиняюсь если вопрос будет не по теме. Если делать чпу либо транслитом либо так как в вашем варианте просто перевод на английский, получается выборку из базы данных нужно делать уже по названию статьи, а не по её id, а это как то не совсем гуд, или я ошибаюсь?
Тоесть например в запросе для извлечения из бд данной статьи условие идет так: WHERE url = $url а не id = $id
в $url лежит: function-generating-handsome-friendly-url/
Понятно, спасибо за быстрый ответ. CMS у меня - свой собственный велосипед, как раз сейчас занимаюсь его изобретением )))
Интересно, возьму на заметку.
А насчет велосипедов - если бы их не изобретали, ничего нового открыть бы не удалось =)