Фильтр slugify
, используемый в настоящее время в Django, переводит (примерно) следующий код Perl:
use Unicode::Normalize;
sub slugify($) {
my ($input) = @_;
$input = NFKD($input); # Normalize (decompose) the Unicode string
$input =~ tr/\000-\177//cd; # Strip non-ASCII characters (>127)
$input =~ s/[^\w\s-]//g; # Remove all characters that are not word characters (includes _), spaces, or hyphens
$input =~ s/^\s+|\s+$//g; # Trim whitespace from both ends
$input = lc($input);
$input =~ s/[-\s]+/-/g; # Replace all occurrences of spaces and hyphens with a single hyphen
return $input;
}
Поскольку вы также хотите поменять символы с акцентом на символы без акцента, лучше всего сделать ставку на unidecode
(определено в Text::Unidecode
) перед удалением символов не-ASCII (, как указано по файлону ).
В этом случае функция может выглядеть следующим образом:
use Unicode::Normalize;
use Text::Unidecode;
sub slugify_unidecode($) {
my ($input) = @_;
$input = NFC($input); # Normalize (recompose) the Unicode string
$input = unidecode($input); # Convert non-ASCII characters to closest equivalents
$input =~ s/[^\w\s-]//g; # Remove all characters that are not word characters (includes _), spaces, or hyphens
$input =~ s/^\s+|\s+$//g; # Trim whitespace from both ends
$input = lc($input);
$input =~ s/[-\s]+/-/g; # Replace all occurrences of spaces and hyphens with a single hyphen
return $input;
}
Первый работает хорошо для строк, которые в основном являются ASCII, но не дотягивают до тех пор, пока вся строка не состоит из символов ASCII, так как все они удаляются, оставляя вас с пустой строкой.
Пример вывода:
string | slugify | slugify_unidecode
-------------------------------------------------
hello world hello world hello world
北亰 bei-jing
liberté liberta liberte
Обратите внимание на то, что implementation lu превращается в тупик с реализацией, вдохновленной Django. Обратите внимание также на разницу, которую делает нормализация NFC - liberté становится 'liberta' с NFKD после удаления второй части разложенного символа, но становится 'libert' после удаления вновь собранного 'é' с NFC.