Как Stack Overflow генерирует свои SEO-дружественные URL-адреса?

219

Что такое хорошее полное регулярное выражение или какой-то другой процесс, который займет название:

Как изменить заголовок, чтобы быть частью URL-адреса, такого как переполнение стека?

и превратите его в

how-do-you-change-a-title-to-be-part-of-the-url-like-stack-overflow

который используется в SEO-дружественных URL-адресах в Stack Overflow?

Я использую среду разработки Ruby on Rails, но если есть некоторые другие решения для платформы (.NET, PHP, Django), я тоже хотел бы их увидеть.

Я уверен, что я (или другой читатель) столкнется с одной и той же проблемой на другой платформе.

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

  • 0
    А как насчет забавных персонажей? Что ты будешь делать с этими? Умляуты? Пунктуация? Это необходимо учитывать. По сути, я бы использовал подход белого списка, в отличие от описанных выше подходов черного списка: опишите, какие символы вы разрешите, какие символы вы будете преобразовывать (во что?), А затем измените остальные на что-то значащее ("") , Я сомневаюсь, что вы можете сделать это в одном регулярном выражении ... Почему бы просто не перебрать персонажей?
  • 1
    Должны быть перенесены в мета ; поскольку вопрос и ответ конкретно касаются реализации SO, и принятый ответ от @JeffAtwood.
Показать ещё 2 комментария
Теги:
seo
language-agnostic
friendly-url
slug

20 ответов

280
Лучший ответ

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

Это вторая версия, развернутая для 5-кратной производительности (и да, я сравнивал ее). Я решил, что буду оптимизировать его, потому что эту функцию можно назвать сотни раз на странице.

/// <summary>
/// Produces optional, URL-friendly version of a title, "like-this-one". 
/// hand-tuned for speed, reflects performance refactoring contributed
/// by John Gietzen (user otac0n) 
/// </summary>
public static string URLFriendly(string title)
{
    if (title == null) return "";

    const int maxlen = 80;
    int len = title.Length;
    bool prevdash = false;
    var sb = new StringBuilder(len);
    char c;

    for (int i = 0; i < len; i++)
    {
        c = title[i];
        if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
        {
            sb.Append(c);
            prevdash = false;
        }
        else if (c >= 'A' && c <= 'Z')
        {
            // tricky way to convert to lowercase
            sb.Append((char)(c | 32));
            prevdash = false;
        }
        else if (c == ' ' || c == ',' || c == '.' || c == '/' || 
            c == '\\' || c == '-' || c == '_' || c == '=')
        {
            if (!prevdash && sb.Length > 0)
            {
                sb.Append('-');
                prevdash = true;
            }
        }
        else if ((int)c >= 128)
        {
            int prevlen = sb.Length;
            sb.Append(RemapInternationalCharToAscii(c));
            if (prevlen != sb.Length) prevdash = false;
        }
        if (i == maxlen) break;
    }

    if (prevdash)
        return sb.ToString().Substring(0, sb.Length - 1);
    else
        return sb.ToString();
}

Чтобы увидеть предыдущую версию кода, которая была заменена (но функционально эквивалентна и быстрее на 5 раз), просмотрите историю изменений этого сообщения (щелкните ссылку даты).

Кроме того, исходный код метода RemapInternationalCharToAscii можно найти здесь.

  • 24
    Было бы неплохо с версией, которая не просто сбрасывает акцентированные символы, такие как åäö, но вместо этого переводит их в aao ... ^^
  • 22
    @oskar заглушка этой функции RemapInternationalCharToAscii() есть meta.stackexchange.com/questions/7435/…
Показать ещё 9 комментариев
28

Вот моя версия кода Джеффа. Я внес следующие изменения:

  • Дефиши были добавлены таким образом, чтобы их можно было добавить, а затем нужно удалить, поскольку это был последний символ в строке. То есть, мы никогда не хотим "my-slug-". Это означает дополнительное распределение строк, чтобы удалить его в этом случае. Ive работал вокруг этого путем задержки-дефиса. Если вы сравните мой код с Jeffs, логику этого легко проследить.
  • Его подход основан исключительно на поиске и пропустил много символов, которые я нашел в примерах, исследуя Stack Overflow. Чтобы противостоять этому, я сначала формирую проход нормализации (сопоставление AKA, упомянутое в вопросе переполнения Meta Stack Non US-ASCII-символы, сброшенные с полного (профиля) URL), а затем игнорировать любые символы за пределами допустимых диапазонов. Это работает большую часть времени...
  • ... Ибо, когда мне не пришлось добавлять таблицу поиска. Как упоминалось выше, некоторые символы не отображают низкое значение ASCII при нормализации. Вместо того, чтобы бросать эти Ive получил список исключений вручную, который, несомненно, полный дыр, но это лучше, чем ничего. Код нормализации был вдохновлен замечательным сообщением Джона Ханнаса в вопросе Как удалить акценты в строке?.
  • Преобразование case теперь также необязательно.

    public static class Slug
    {
        public static string Create(bool toLower, params string[] values)
        {
            return Create(toLower, String.Join("-", values));
        }
    
        /// <summary>
        /// Creates a slug.
        /// References:
        /// http://www.unicode.org/reports/tr15/tr15-34.html
        /// https://meta.stackexchange.com/questions/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696
        /// https://stackoverflow.com/questions/25259/how-do-you-include-a-webpage-title-as-part-of-a-webpage-url/25486#25486
        /// /questions/29054/how-can-i-remove-accents-on-a-stringhow-can-i-remove-accents-on-a-string
        /// </summary>
        /// <param name="toLower"></param>
        /// <param name="normalised"></param>
        /// <returns></returns>
        public static string Create(bool toLower, string value)
        {
            if (value == null)
                return "";
    
            var normalised = value.Normalize(NormalizationForm.FormKD);
    
            const int maxlen = 80;
            int len = normalised.Length;
            bool prevDash = false;
            var sb = new StringBuilder(len);
            char c;
    
            for (int i = 0; i < len; i++)
            {
                c = normalised[i];
                if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
                {
                    if (prevDash)
                    {
                        sb.Append('-');
                        prevDash = false;
                    }
                    sb.Append(c);
                }
                else if (c >= 'A' && c <= 'Z')
                {
                    if (prevDash)
                    {
                        sb.Append('-');
                        prevDash = false;
                    }
                    // Tricky way to convert to lowercase
                    if (toLower)
                        sb.Append((char)(c | 32));
                    else
                        sb.Append(c);
                }
                else if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-' || c == '_' || c == '=')
                {
                    if (!prevDash && sb.Length > 0)
                    {
                        prevDash = true;
                    }
                }
                else
                {
                    string swap = ConvertEdgeCases(c, toLower);
    
                    if (swap != null)
                    {
                        if (prevDash)
                        {
                            sb.Append('-');
                            prevDash = false;
                        }
                        sb.Append(swap);
                    }
                }
    
                if (sb.Length == maxlen)
                    break;
            }
            return sb.ToString();
        }
    
        static string ConvertEdgeCases(char c, bool toLower)
        {
            string swap = null;
            switch (c)
            {
                case 'ı':
                    swap = "i";
                    break;
                case 'ł':
                    swap = "l";
                    break;
                case 'Ł':
                    swap = toLower ? "l" : "L";
                    break;
                case 'đ':
                    swap = "d";
                    break;
                case 'ß':
                    swap = "ss";
                    break;
                case 'ø':
                    swap = "o";
                    break;
                case 'Þ':
                    swap = "th";
                    break;
            }
            return swap;
        }
    }
    

Для более подробной информации, модульные тесты и объяснение причин Facebook URL немного умнее, чем Stack Overflows, у меня есть расширенная версия этого в моем блоге.

  • 4
    +1 Это отлично, Дэн. Я также добавил комментарий в ваш блог о возможном изменении if (i == maxlen) break; быть, if (sb.Length == maxlen) break; вместо этого, так что, если вы передадите строку с большим количеством пробелов / недопустимых символов, вы все равно сможете получить фрагмент нужной длины, в то время как код в его нынешнем состоянии может в конечном итоге его сильно усечь (например, рассмотрим случай, когда вы начинаете с 80 пространства ...). Примерно 10 000 000 итераций в сравнении с кодом Джеффа показали, что скорость примерно одинакова.
  • 1
    Спасибо, ответил на мой блог и исправил код там и выше. Также спасибо за сравнительный анализ кода. Для интересующихся это было наравне с Джеффом.
Показать ещё 4 комментария
16

Вам нужно настроить собственный маршрут, чтобы указать URL на контроллер, который будет обрабатывать его. Поскольку вы используете Ruby on Rails, здесь используется введение при использовании своего механизма маршрутизации.

В Ruby вам понадобится регулярное выражение, которое вы уже знаете, и здесь используется регулярное выражение:

def permalink_for(str)
    str.gsub(/[^\w\/]|[!\(\)\.]+/, ' ').strip.downcase.gsub(/\ +/, '-')
end
11

Вы также можете использовать эту функцию JavaScript для генерации пула в виде формы (эта на основе/скопирована из Django):

function makeSlug(urlString, filter) {
    // Changes, e.g., "Petty theft" to "petty_theft".
    // Remove all these words from the string before URLifying

    if(filter) {
        removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from",
        "is", "in", "into", "like", "of", "off", "on", "onto", "per",
        "since", "than", "the", "this", "that", "to", "up", "via", "het", "de", "een", "en",
        "with"];
    }
    else {
        removelist = [];
    }
    s = urlString;
    r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi');
    s = s.replace(r, '');
    s = s.replace(/[^-\w\s]/g, ''); // Remove unneeded characters
    s = s.replace(/^\s+|\s+$/g, ''); // Trim leading/trailing spaces
    s = s.replace(/[-\s]+/g, '-'); // Convert spaces to hyphens
    s = s.toLowerCase(); // Convert to lowercase
    return s; // Trim to first num_chars characters
}
9

Для хорошей меры, здесь работает PHP-функция в WordPress... Я бы подумал, что WordPress является одной из наиболее популярных платформ, использующих причудливые ссылки.

    function sanitize_title_with_dashes($title) {
            $title = strip_tags($title);
            // Preserve escaped octets.
            $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
            // Remove percent signs that are not part of an octet.
            $title = str_replace('%', '', $title);
            // Restore octets.
            $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);
            $title = remove_accents($title);
            if (seems_utf8($title)) {
                    if (function_exists('mb_strtolower')) {
                            $title = mb_strtolower($title, 'UTF-8');
                    }
                    $title = utf8_uri_encode($title, 200);
            }
            $title = strtolower($title);
            $title = preg_replace('/&.+?;/', '', $title); // kill entities
            $title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
            $title = preg_replace('/\s+/', '-', $title);
            $title = preg_replace('|-+|', '-', $title);
            $title = trim($title, '-');
            return $title;
    }

Эта функция, а также некоторые вспомогательные функции могут быть найдены в файле wp-includes/formatting.php.

  • 6
    Это не полный ответ. Вам не хватает функций, таких как: remove_accents , seems_utf8 ...
  • 0
    чтобы завершить ответ git clone git://core.git.wordpress.org/ How-To Geek, вы все равно можете сделать git clone git://core.git.wordpress.org/ и найти файл wp-includes/formatting.php в
5

Если вы используете край Rails, вы можете положиться на Inflector.parametrize - вот пример из документации:

  class Person
    def to_param
      "#{id}-#{name.parameterize}"
    end
  end

  @person = Person.find(1)
  # => #<Person id: 1, name: "Donald E. Knuth">

  <%= link_to(@person.name, person_path(@person)) %>
  # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>

Также, если вам нужно обрабатывать более экзотические символы, такие как акценты (éphémère) в предыдущей версии Rails, вы можете использовать смесь PermalinkFu и DiacriticsFu:

DiacriticsFu::escape("éphémère")
=> "ephemere"

DiacriticsFu::escape("räksmörgås")
=> "raksmorgas"
5

Я не знаком с Ruby on Rails, но следующий (непроверенный) PHP-код. Вы можете, вероятно, перевести это очень быстро в Ruby on Rails, если найдете его полезным.

$sURL = "This is a title to convert to URL-format. It has 1 number in it!";
// To lower-case
$sURL = strtolower($sURL);

// Replace all non-word characters with spaces
$sURL = preg_replace("/\W+/", " ", $sURL);

// Remove trailing spaces (so we won't end with a separator)
$sURL = trim($sURL);

// Replace spaces with separators (hyphens)
$sURL = str_replace(" ", "-", $sURL);

echo $sURL;
// outputs: this-is-a-title-to-convert-to-url-format-it-has-1-number-in-it

Надеюсь, это поможет.

4

Реализация T-SQL, адаптированная из dbo.UrlEncode:

CREATE FUNCTION dbo.Slug(@string varchar(1024))
RETURNS varchar(3072)
AS
BEGIN
    DECLARE @count int, @c char(1), @i int, @slug varchar(3072)

    SET @string = replace(lower(ltrim(rtrim(@string))),' ','-')

    SET @count = Len(@string)
    SET @i = 1
    SET @slug = ''

    WHILE (@i <= @count)
    BEGIN
        SET @c = substring(@string, @i, 1)

        IF @c LIKE '[a-z0-9--]'
            SET @slug = @slug + @c

        SET @i = @i +1
    END

    RETURN @slug
END
4

Я не очень люблю Ruby или Rails, но в Perl это то, что я сделал бы:

my $title = "How do you change a title to be part of the url like Stackoverflow?";

my $url = lc $title;   # Change to lower case and copy to URL.
$url =~ s/^\s+//g;     # Remove leading spaces.
$url =~ s/\s+$//g;     # Remove trailing spaces.
$url =~ s/\s+/\-/g;    # Change one or more spaces to single hyphen.
$url =~ s/[^\w\-]//g;  # Remove any non-word characters.

print "$title\n$url\n";

Я просто сделал быстрый тест и, похоже, сработал. Надеюсь, это относительно легко перевести на Ruby.

3

Я знаю, что это очень старый вопрос, но поскольку большинство браузеров теперь поддерживают URL-адреса юникода, я нашел отличное решение в XRegex, которое преобразует все, кроме букв (на всех языках '-'.)

Это можно сделать на нескольких языках программирования.

Образец \\p{^L}+, и вам просто нужно использовать его, чтобы заменить все не буквы на "-".

Рабочий пример в node.js с xregex.

var text = 'This ! can @ have # several $ letters % from different languages such as עברית or Español';

var slugRegEx = XRegExp('((?!\\d)\\p{^L})+', 'g');

var slug = XRegExp.replace(text, slugRegEx, '-').toLowerCase();

console.log(slug) ==> "this-can-have-several-letters-from-different-languages-such-as-עברית-or-español"
3

Как насчет смешных персонажей? Что вы собираетесь с ними делать? Умляуты? Пунктуация? Это необходимо учитывать. В принципе, я бы использовал подход "белого списка", в отличие от подходов "черного списка": описать, какие символы вы разрешите, какие символы вы будете конвертировать (на что?), А затем изменить остальные на что-то значимое (""), Я сомневаюсь, что вы можете сделать это в одном регулярном выражении... Почему бы просто не пропустить символы?

3

Предполагая, что ваш класс модели имеет атрибут title, вы можете просто переопределить метод to_param в модели, например:

def to_param
  title.downcase.gsub(/ /, '-')
end

Этот эпизод Railscast содержит все подробности. Вы также можете убедиться, что заголовок содержит только допустимые символы:

validates_format_of :title, :with => /^[a-z0-9-]+$/,
                    :message => 'can only contain letters, numbers and hyphens'
2

Здесь моя (медленная, но забавная для записи) версия кода Джеффа:

public static string URLFriendly(string title)
{
    char? prevRead = null,
        prevWritten = null;

    var seq = 
        from c in title
        let norm = RemapInternationalCharToAscii(char.ToLowerInvariant(c).ToString())[0]
        let keep = char.IsLetterOrDigit(norm)
        where prevRead.HasValue || keep
        let replaced = keep ? norm
            :  prevWritten != '-' ? '-'
            :  (char?)null
        where replaced != null
        let s = replaced + (prevRead == null ? ""
            : norm == '#' && "cf".Contains(prevRead.Value) ? "sharp"
            : norm == '+' ? "plus"
            : "")
        let _ = prevRead = norm
        from written in s
        let __ = prevWritten = written
        select written;

    const int maxlen = 80;  
    return string.Concat(seq.Take(maxlen)).TrimEnd('-');
}

public static string RemapInternationalCharToAscii(string text)
{
    var seq = text.Normalize(NormalizationForm.FormD)
        .Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark);

    return string.Concat(seq).Normalize(NormalizationForm.FormC);
}

Моя тестовая строка:

" I love C#, F#, C++, and... Crème brûlée!!! They see me codin'... they hatin'... tryin' to catch me codin' dirty... "

2

https://stackoverflow.com/questions/25259/how-does-stack-overflow-generate-its-seo-friendly-urls отлично, но современный браузер (исключая IE, как обычно) теперь прекрасно обрабатывает кодировку utf8:

Изображение 4755

Итак, я обновил предлагаемое решение:

public static string ToFriendlyUrl(string title, bool useUTF8Encoding = false)
{
    ...

        else if (c >= 128)
        {
            int prevlen = sb.Length;
            if (useUTF8Encoding )
            {
                sb.Append(HttpUtility.UrlEncode(c.ToString(CultureInfo.InvariantCulture),Encoding.UTF8));
            }
            else
            {
                sb.Append(RemapInternationalCharToAscii(c));
            }
    ...
}

Полный код на Pastebin

Изменить: Здесь код для метода RemapInternationalCharToAscii (который отсутствует в pastebin).

  • 0
    Согласно Википедии , Mozilla 1.4, Netscape 7.1, Opera 7.11 были одними из первых приложений, поддерживающих IDNA. Для Internet Explorer 6 доступен подключаемый модуль браузера для поддержки IDN. Internet Explorer 7.0 и API-интерфейсы URL в Windows Vista обеспечивают встроенную поддержку IDN. Похоже, удаление символов UTF-8 - пустая трата времени. Да здравствует UTF-8 !!!
2

Вы можете использовать следующий вспомогательный метод. Он может преобразовывать символы Unicode.

public static string ConvertTextToSlug(string s)
{
    StringBuilder sb = new StringBuilder();

    bool wasHyphen = true;

    foreach (char c in s)
    {
        if (char.IsLetterOrDigit(c))
        {
            sb.Append(char.ToLower(c));
            wasHyphen = false;
        }
        else
            if (char.IsWhiteSpace(c) && !wasHyphen)
            {
                sb.Append('-');
                wasHyphen = true;
            }
    }

    // Avoid trailing hyphens
    if (wasHyphen && sb.Length > 0)
        sb.Length--;

    return sb.ToString().Replace("--","-");
}
2

Существует небольшой плагин Ruby on Rails, называемый PermalinkFu, который делает это. метод escape делает преобразование в строку, подходящую для URL. Посмотрите на код; этот метод довольно прост.

Чтобы удалить символ ASCII, он использует iconv lib для перевода на 'ascii//ignore//translit' from ' UTF-8'. Затем пробелы превращаются в тире, все сбито и т.д.

  • 0
    Хотя это работает отлично, я почему-то чувствую, что это не очень эффективно.
2

Код Брайана, в Ruby:

title.downcase.strip.gsub(/\ /, '-').gsub(/[^\w\-]/, '')

downcase превращает строку в нижний регистр, strip удаляет начальные и конечные пробелы, первый вызов gsub глобально заменяет пробелы с тире, а второй удаляет все, что не является буквой или тире.

1

Теперь все браузеры прекрасно обрабатывают кодировку utf8, поэтому вы можете использовать WebUtility.UrlEncode Метод, похожий на HttpUtility.UrlEncode, используемый @giamin, но его работа вне веб-приложения.

1

Мне понравилось, как это делается без использования регулярных выражений, поэтому я портировал его на PHP. Я просто добавил функцию под названием is_between для проверки символов:

function is_between($val, $min, $max)
{
    $val = (int) $val; $min = (int) $min; $max = (int) $max;

    return ($val >= $min && $val <= $max);
}

function international_char_to_ascii($char)
{
    if (mb_strpos('àåáâäãåa', $char) !== false)
    {
        return 'a';
    }

    if (mb_strpos('èéêëe', $char) !== false)
    {
        return 'e';
    }

    if (mb_strpos('ìíîïi', $char) !== false)
    {
        return 'i';
    }

    if (mb_strpos('òóôõö', $char) !== false)
    {
        return 'o';
    }

    if (mb_strpos('ùúûüuu', $char) !== false)
    {
        return 'u';
    }

    if (mb_strpos('çccc', $char) !== false)
    {
        return 'c';
    }

    if (mb_strpos('zzž', $char) !== false)
    {
        return 'z';
    }

    if (mb_strpos('ssšs', $char) !== false)
    {
        return 's';
    }

    if (mb_strpos('ñn', $char) !== false)
    {
        return 'n';
    }

    if (mb_strpos('ýÿ', $char) !== false)
    {
        return 'y';
    }

    if (mb_strpos('gg', $char) !== false)
    {
        return 'g';
    }

    if (mb_strpos('r', $char) !== false)
    {
        return 'r';
    }

    if (mb_strpos('l', $char) !== false)
    {
        return 'l';
    }

    if (mb_strpos('d', $char) !== false)
    {
        return 'd';
    }

    if (mb_strpos('ß', $char) !== false)
    {
        return 'ss';
    }

    if (mb_strpos('Þ', $char) !== false)
    {
        return 'th';
    }

    if (mb_strpos('h', $char) !== false)
    {
        return 'h';
    }

    if (mb_strpos('j', $char) !== false)
    {
        return 'j';
    }
    return '';
}

function url_friendly_title($url_title)
{
    if (empty($url_title))
    {
        return '';
    }

    $url_title = mb_strtolower($url_title);

    $url_title_max_length   = 80;
    $url_title_length       = mb_strlen($url_title);
    $url_title_friendly     = '';
    $url_title_dash_added   = false;
    $url_title_char = '';

    for ($i = 0; $i < $url_title_length; $i++)
    {
        $url_title_char     = mb_substr($url_title, $i, 1);

        if (strlen($url_title_char) == 2)
        {
            $url_title_ascii    = ord($url_title_char[0]) * 256 + ord($url_title_char[1]) . "\r\n";
        }
        else
        {
            $url_title_ascii    = ord($url_title_char);
        }

        if (is_between($url_title_ascii, 97, 122) || is_between($url_title_ascii, 48, 57))
        {
            $url_title_friendly .= $url_title_char;

            $url_title_dash_added = false;
        }
        elseif(is_between($url_title_ascii, 65, 90))
        {
            $url_title_friendly .= chr(($url_title_ascii | 32));

            $url_title_dash_added = false;
        }
        elseif($url_title_ascii == 32 || $url_title_ascii == 44 || $url_title_ascii == 46 || $url_title_ascii == 47 || $url_title_ascii == 92 || $url_title_ascii == 45 || $url_title_ascii == 47 || $url_title_ascii == 95 || $url_title_ascii == 61)
        {
            if (!$url_title_dash_added && mb_strlen($url_title_friendly) > 0)
            {
                $url_title_friendly .= chr(45);

                $url_title_dash_added = true;
            }
        }
        else if ($url_title_ascii >= 128)
        {
            $url_title_previous_length = mb_strlen($url_title_friendly);

            $url_title_friendly .= international_char_to_ascii($url_title_char);

            if ($url_title_previous_length != mb_strlen($url_title_friendly))
            {
                $url_title_dash_added = false;
            }
        }

        if ($i == $url_title_max_length)
        {
            break;
        }
    }

    if ($url_title_dash_added)
    {
        return mb_substr($url_title_friendly, 0, -1);
    }
    else
    {
        return $url_title_friendly;
    }
}
0

Нет, нет, нет. Вы все так неправы. За исключением материалов diacritics-fu, вы добираетесь туда, но как насчет азиатских персонажей (стыдно за разработчиков Ruby за то, что они не рассматривают их nihonjin братья).

Firefox и Safari отображают символы, отличные от ASCII, в URL, и, откровенно говоря, они выглядят великолепно. Приятно поддерживать ссылки, такие как http://somewhere.com/news/read/お 前 た ち は ア ホ じ ゃ な い か い.

Итак, вот какой-то PHP-код, который это сделает, но я просто написал его и не испытывал стресс.

<?php
    function slug($str)
    {
        $args = func_get_args();
        array_filter($args);  //remove blanks
        $slug = mb_strtolower(implode('-', $args));

        $real_slug = '';
        $hyphen = '';
        foreach(SU::mb_str_split($slug) as $c)
        {
            if (strlen($c) > 1 && mb_strlen($c)===1)
            {
                $real_slug .= $hyphen . $c;
                $hyphen = '';
            }
            else
            {
                switch($c)
                {
                    case '&':
                        $hyphen = $real_slug ? '-and-' : '';
                        break;
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'e':
                    case 'f':
                    case 'g':
                    case 'h':
                    case 'i':
                    case 'j':
                    case 'k':
                    case 'l':
                    case 'm':
                    case 'n':
                    case 'o':
                    case 'p':
                    case 'q':
                    case 'r':
                    case 's':
                    case 't':
                    case 'u':
                    case 'v':
                    case 'w':
                    case 'x':
                    case 'y':
                    case 'z':

                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'E':
                    case 'F':
                    case 'G':
                    case 'H':
                    case 'I':
                    case 'J':
                    case 'K':
                    case 'L':
                    case 'M':
                    case 'N':
                    case 'O':
                    case 'P':
                    case 'Q':
                    case 'R':
                    case 'S':
                    case 'T':
                    case 'U':
                    case 'V':
                    case 'W':
                    case 'X':
                    case 'Y':
                    case 'Z':

                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        $real_slug .= $hyphen . $c;
                        $hyphen = '';
                        break;

                    default:
                       $hyphen = $hyphen ? $hyphen : ($real_slug ? '-' : '');
                }
            }
        }
        return $real_slug;
    }

Пример:

$str = "~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04 コリン ~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04 トーマス ~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04 アーノルド ~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04";
echo slug($str);

Выходы:   コ リ ン -и- ト ー マ ス -и- ア ー ノ ル ド

"-and-" - это потому, что и меняются на "-and-".

  • 4
    Я действительно не знаю, что сказать об этой части информации.
  • 3
    Это действительно хороший пример того, когда НЕ нужно использовать оператор switch case.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню