dev.club — where best developers and top companies connect.

dev.club — where best developers and top companies connect.Invite only

Request invite

Python Slugify

Unicode-aware slug generation for Python, with explicit transliteration choices.

CI PyPI Maintained with Dojo

Quickstart

9+ releases ship the modern algorithm. Legacy behavior remains the default; use algorithm='modern' for the latest rules. See the migration guide before changing persisted URLs or keys.

Install python-slugify, import slugify. Other similarly named distributions are not this package.

python -m pip install --upgrade python-slugify
from slugify import slugify

# Use the modern algorithm for the latest rules.
assert slugify("C'est déjà l'été.", algorithm='modern') == 'c-est-deja-l-ete'
assert slugify('影師嗎', backend='text-unidecode', algorithm='modern') == 'ying-shi-ma'
assert slugify('影師嗎', allow_unicode=True, algorithm='modern') == '影師嗎'

# Calling slugify() without algorithm uses the legacy pipeline (the old default),
# kept unchanged for backward compatibility.
assert slugify("C'est déjà l'été.") == 'c-est-deja-l-ete'
slugify "Hello, world!"
# hello-world
printf 'Café' | python -m slugify --stdin
# cafe
slugify --regex-pattern '[^-a-z0-9_]+' '___This is a test___'
# ___this-is-a-test___

The examples above work with 9+ releases. Python 3.10 or newer is required.

Python support

Python Release family
2.7–3.5 below 5
3.6 5–6
3.7–3.9 7–8 (check each release's Requires-Python)
3.10+ 9.x

The configured release-9 test matrix covers CPython 3.10–3.14 and PyPy 3.11. Configuration is not proof of a successful run; see local verification. Older applications can stay on a pinned 8.x release rather than upgrading Python or regenerating slugs immediately.

Backends and installation

backend Runtime selection Installation
auto (default) Installed Unidecode first, otherwise text-unidecode Base install includes text-unidecode
text-unidecode Only text-unidecode Included in base install
unidecode Only Unidecode python -m pip install 'python-slugify[unidecode]'
anyascii Only AnyASCII python -m pip install 'python-slugify[anyascii]'

Explicit selection never silently falls back. A missing selected module raises ModuleNotFoundError. Backend imports are lazy; allow_unicode=True bypasses transliteration entirely. Extras add dependencies; they do not remove text-unidecode. The AnyASCII extra does not change the default backend. There is no dependency-free installation extra in this release.

Transliteration is not translation, language detection, or context-sensitive pronunciation. Backends produce different slugs: for example 影師嗎 becomes ying-shi-ma with text-unidecode/Unidecode, but yingshima with AnyASCII. Pin both this package and your explicitly chosen backend for stable persisted identifiers.

API options

The original positional parameters remain in their original order. New options are keyword-only.

slugify(
    text, entities=True, decimal=True, hexadecimal=True,
    max_length=0, word_boundary=False, separator='-', save_order=False,
    stopwords=(), regex_pattern=None, lowercase=True, replacements=(),
    allow_unicode=False, *, replacement_stage='both', backend='auto',
    algorithm='legacy',
)

smart_truncate(string, max_length=0, word_boundary=False, separator=' ', save_order=False) is also public and retains legacy behavior: str.strip(separator) strips a character set, empty separators raise ValueError, zero is unlimited, and negative limits retain slicing semantics. Modern slugify uses a separate private token-budget helper; it does not change this public function.

from slugify import slugify

assert slugify('a'b') == 'ab'  # unchanged default
assert slugify('a'b', algorithm='modern') == 'a-b'
assert slugify('xylophone x', separator='x', max_length=100, algorithm='modern') == 'xylophonexx'

Recipes and boundaries

from slugify import slugify, GERMAN

assert slugify('ÜBER', replacements=GERMAN, replacement_stage='pre') == 'ueber'
assert slugify('a', replacements=[('a', 'aa')]) == 'aaaa'  # legacy two passes
assert slugify('a', replacements=[('a', 'aa')], replacement_stage='pre') == 'aa'
assert slugify('one two three four', max_length=12, word_boundary=True) == 'one-two-four'
assert slugify('one two three four', max_length=12, word_boundary=True, save_order=True) == 'one-two'
assert slugify('a b c', separator='---', max_length=5, algorithm='modern') == 'a---b'
assert slugify('Baby’s shoes', replacements=[('’', '-')], replacement_stage='pre') == 'baby-s-shoes'

CYRILLIC, GERMAN, GREEK, and their combined PRE_TRANSLATIONS are optional substitution lists, not automatically applied locale rules. allow_unicode=True can normalize compatibility jamo, and does not preserve emoji by default. Disable all three entity flags if encoded markup should not be decoded. Empty input, whitespace, or entirely filtered content can yield an empty string; callers choose an appropriate fallback.

Slugs are not guaranteed unique, filesystem-safe on every OS, XML-name-safe, or safe as an entire URL. Choose escaping, reserved-name handling, path validation, and transactional uniqueness for your destination. Custom regexes and post replacements can intentionally introduce punctuation; do not treat this package as a security sanitizer.

Command line

slugify --help and python -m slugify --help describe all options, including --algorithm (default legacy), --backend and --replacement-stage. Use -- before text when supplying multi-valued options:

slugify --stopwords the in a hurry -- the quick brown fox jumps over the lazy dog in a hurry
slugify --replacement-stage pre --replacements 'a->aa' -- a

Development and local release checks

python -m pip install -r dev.requirements.txt
python -m pip install -e '.[unidecode,anyascii]'
python -m pytest
python -m mypy
python tools/check_dist.py
# Full declared interpreter/backend matrix (requires those interpreters):
tox

The artifact check builds and installs both wheel and source archive in temporary environments outside the checkout. It never uploads or tags. Publishing requires a separate maintainer decision; there is no setup.py publish shortcut. See migration and release notes, review and draft replies, and the historical review. Please consult the contribution wiki before proposing changes.

Licensing

python-slugify's own code is MIT licensed. Dependency licenses are separate:

The installed package set and the backend used at runtime are different questions. Selecting AnyASCII does not remove text-unidecode from a normal installation. This is factual dependency guidance, not legal advice or a blanket assurance about your application's obligations. Evaluate the actual versions, distribution method, and applicable license terms.

Sponsors

Neekware Inc. — creator of Dojo Workspace, your AI workspace for building, learning, and getting things done.

🚀 Created with Dojo ⛩️

Join libs.tech

...and unlock some superpowers

GitHub

We won't share your data with anyone else.