Как Stack Overflow генерирует оптимизированные для SEO URL-адреса?
-
09-06-2019 - |
Вопрос
Что такое хороший комплект регулярное выражение или какой-либо другой процесс, который будет иметь заголовок:
Как изменить заголовок, чтобы он стал частью URL-адреса, например Stack Overflow?
и превратить его в
how-do-you-change-a-title-to-be-part-of-the-url-like-stack-overflow
который используется в оптимизированных для SEO URL-адресах в Stack Overflow?
Среда разработки, которую я использую, Рубин на рельсах, но если есть какие-то другие решения для конкретной платформы (.NET, PHP, Джанго), мне бы тоже хотелось их увидеть.
Я уверен, что я (или другой читатель) столкнусь с той же проблемой на другой платформе в будущем.
Я использую пользовательские маршруты и в основном хочу знать, как изменить строку, чтобы все специальные символы были удалены, все были строчными буквами и заменены все пробелы.
Решение
Вот как мы это делаем.Обратите внимание, что граничных условий, вероятно, больше, чем вы думаете на первый взгляд.
Это вторая версия, развернутая для повышения производительности в 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
исходный код метода можно найти здесь.
Другие советы
Вот моя версия кода Джеффа.Я внес следующие изменения:
- Дефисы были добавлены таким образом, что их можно было добавить, а затем удалить, поскольку это был последний символ в строке.То есть нам никогда не нужен «мой-слизень-».Это означает дополнительное выделение строки для ее удаления в этом крайнем случае.Я обошёл эту проблему, расставив переносы с задержкой.Если вы сравните мой код с кодом Джеффа, логику этого легко понять.
- Его подход основан исключительно на поиске, и он упустил много символов, которые я нашел в примерах, исследуя Stack Overflow.Чтобы противостоять этому, я сначала выполняю проход нормализации (сопоставление AKA, упомянутое в вопросе о переполнении стека Meta). Символы, отличные от US-ASCII, удалены из полного URL-адреса (профиля).), а затем игнорировать любые символы за пределами допустимых диапазонов.В большинстве случаев это работает...
- ...В противном случае мне также пришлось добавить таблицу поиска.Как упоминалось выше, некоторые символы не отображаются в низкое значение ASCII при нормализации.Вместо того, чтобы отказаться от них, у меня есть ручной список исключений, который, несомненно, полон дыр, но это лучше, чем ничего.Код нормализации был вдохновлен замечательным постом Джона Ханны в вопросе Stack Overflow. Как убрать акценты в строке?.
Преобразование регистра теперь также необязательно.
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 /// https://stackoverflow.com/questions/3769457/how-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; } }
Более подробная информация о модульных тестах и объяснение того, почему Фейсбук's URL-адрес схема немного умнее, чем Stack Overflows, у меня есть расширенная версия этого в моем блоге.
Вы захотите настроить собственный маршрут, чтобы указать URL-адрес контроллеру, который будет его обрабатывать.Поскольку вы используете Ruby on Rails, вот введение в использовании их механизма маршрутизации.
В Ruby вам понадобится регулярное выражение, которое вы уже знаете, и вот его регулярное выражение:
def permalink_for(str)
str.gsub(/[^\w\/]|[!\(\)\.]+/, ' ').strip.downcase.gsub(/\ +/, '-')
end
Вы также можете использовать это JavaScript функция для генерации пулов в форме (она основана на/скопирована из Джанго):
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
}
Для наглядности вот функция 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.
Если вы используете Rails Edge, вы можете положиться на Инфлектор.параметризировать - вот пример из документации:
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>
Также, если вам нужно обрабатывать более экзотические символы, такие как акценты (эфемер) в предыдущей версии Rails, вы можете использовать смесь Постоянная ссылкаFu и Диакритические знакиFu:
DiacriticsFu::escape("éphémère")
=> "ephemere"
DiacriticsFu::escape("räksmörgås")
=> "raksmorgas"
Я не знаком с 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
Надеюсь, это поможет.
Я не особо разбираюсь в 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.
Реализация 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
Предполагая, что ваш класс модели имеет атрибут 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'
Я знаю, что это очень старый вопрос, но поскольку большинство браузеров сейчас поддержка URL-адресов в Юникоде Я нашел отличное решение в XRegex который преобразует все, кроме букв (на всех языках в «-»).
Это можно сделать на нескольких языках программирования.
Шаблон \\p{^L}+
и тогда вам просто нужно использовать его, чтобы заменить все небуквы на «-».
Рабочий пример в node.js с регулярное выражение модуль.
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"
Код Брайана на Ruby:
title.downcase.strip.gsub(/\ /, '-').gsub(/[^\w\-]/, '')
downcase
переводит строку в нижний регистр, strip
удаляет начальные и конечные пробелы, первый gsub
вызов глокально субзаменяет пробелы тире, а второй удаляет все, что не является буквой или тире.
Существует небольшой плагин Ruby on Rails под названием Постоянная ссылкаFu, это делает это.А метод побега выполняет преобразование в строку, подходящую для URL-адрес.Посмотрите на код;этот метод довольно прост.
Чтобы удалить не-ASCII символов, он использует библиотеку iconv для перевода в «ascii//ignore//translit» из «utf-8».Пробелы затем превращаются в тире, все понижается и т. д.
Вы можете использовать следующий вспомогательный метод.Он может конвертировать символы Юникода.
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("--","-");
}
Вот моя (медленная, но интересная) версия кода Джеффа:
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... "
А решение для переполнения стека это здорово, но современный браузер (за исключением IE, как обычно) теперь прекрасно обрабатывает кодировку utf8:
Поэтому я обновил предложенное решение:
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));
}
...
}
Редактировать: Вот код для RemapInternationalCharToAscii
метод (который отсутствует в Pastebin).
Мне понравилось, как это делается без использования обычные выражения, поэтому я портировал его на 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;
}
}
Теперь все браузеры прекрасно обрабатывают кодировку utf8, поэтому вы можете использовать WebUtility.UrlEncode Метод, он такой HttpUtility.UrlEncode используется @giamin, но работает вне веб-приложения.
Нет нет нет.Вы все очень сильно ошибаетесь.Если не считать диакритических знаков, вы это поняли, но как насчет азиатских символов (позор разработчикам Ruby, которые не учли их нихонджин братья).
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);
Выходы:コリン-и-トーマス-и-アーノルド
«-и-» означает, что & заменяется на «-и-».
Я портировал код на TypeScript.Его можно легко адаптировать к JavaScript.
Я добавляю .contains
метод к String
прототип, если вы ориентируетесь на новейшие браузеры или ES6, вы можете использовать .includes
вместо.
if (!String.prototype.contains) {
String.prototype.contains = function (check) {
return this.indexOf(check, 0) !== -1;
};
}
declare interface String {
contains(check: string): boolean;
}
export function MakeUrlFriendly(title: string) {
if (title == null || title == '')
return '';
const maxlen = 80;
let len = title.length;
let prevdash = false;
let result = '';
let c: string;
let cc: number;
let remapInternationalCharToAscii = function (c: string) {
let s = c.toLowerCase();
if ("àåáâäãåą".contains(s)) {
return "a";
}
else if ("èéêëę".contains(s)) {
return "e";
}
else if ("ìíîïı".contains(s)) {
return "i";
}
else if ("òóôõöøőð".contains(s)) {
return "o";
}
else if ("ùúûüŭů".contains(s)) {
return "u";
}
else if ("çćčĉ".contains(s)) {
return "c";
}
else if ("żźž".contains(s)) {
return "z";
}
else if ("śşšŝ".contains(s)) {
return "s";
}
else if ("ñń".contains(s)) {
return "n";
}
else if ("ýÿ".contains(s)) {
return "y";
}
else if ("ğĝ".contains(s)) {
return "g";
}
else if (c == 'ř') {
return "r";
}
else if (c == 'ł') {
return "l";
}
else if (c == 'đ') {
return "d";
}
else if (c == 'ß') {
return "ss";
}
else if (c == 'Þ') {
return "th";
}
else if (c == 'ĥ') {
return "h";
}
else if (c == 'ĵ') {
return "j";
}
else {
return "";
}
};
for (let i = 0; i < len; i++) {
c = title[i];
cc = c.charCodeAt(0);
if ((cc >= 97 /* a */ && cc <= 122 /* z */) || (cc >= 48 /* 0 */ && cc <= 57 /* 9 */)) {
result += c;
prevdash = false;
}
else if ((cc >= 65 && cc <= 90 /* A - Z */)) {
result += c.toLowerCase();
prevdash = false;
}
else if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-' || c == '_' || c == '=') {
if (!prevdash && result.length > 0) {
result += '-';
prevdash = true;
}
}
else if (cc >= 128) {
let prevlen = result.length;
result += remapInternationalCharToAscii(c);
if (prevlen != result.length) prevdash = false;
}
if (i == maxlen) break;
}
if (prevdash)
return result.substring(0, result.length - 1);
else
return result;
}