slugany
A multi-language slugify library with zero runtime dependencies. MIT-licensed, fully typed, and audited for idempotency — a clean alternative to python-slugify with no GPL baggage.
381 tests · 100% coverage ·
mypy --strictclean ·ruffclean · 13,800+ randomized idempotency checks passed
Why slugany?
python-slugify is the de facto standard, but it drags in text-unidecode (GPL) and over 1,000 lines of code. slugany was built from scratch to be:
| python-slugify | slugany | |
|---|---|---|
| Runtime deps | text-unidecode (GPL) |
Zero |
| License | GPL | MIT |
| Languages | Limited | Built-in: es, pt, de, fr, it |
| Caching | No | lru_cache built-in (512) |
| Typing | Partial | Fully typed, py.typed marker |
| Core size | ~1,000+ lines | ~550 lines |
| Idempotency | Not guaranteed | Guaranteed & tested |
| CLI | Separate package | Built-in |
Features
- Zero runtime deps — only the Python standard library
- Multi-language transliteration — Spanish, Portuguese, German, French, Italian
- 8 case styles — kebab, snake, camel, pascal, dot, train, filename, url
- Smart punctuation — normalizes curly quotes, em-dashes, NBSP, zero-width chars, bullets
- HTML entity decoding —
&→&before processing - Emoji handling — strip, keep, or convert to text
- Confusable detection — Cyrillic homoglyphs → Latin equivalents
- Stopwords removal — filter out common words per language
- Custom replacements — pre- and post-pipeline string substitution
- CSS-safe slugs — prefix digit-leading slugs with
s- - Max length with word boundaries — truncate without breaking words
- Unicode preservation —
allow_unicode=Truekeeps non-ASCII chars - Fallback for empty slugs — never get an empty string
- Built-in
lru_cache— results cached automatically (maxsize=512) - CLI with stdin support — pipe text directly:
echo "text" | slugany - Idempotent —
slugify(slugify(x)) == slugify(x), guaranteed and tested - Fully typed — type hints on every public API,
py.typedmarker (PEP 561) - ~550 lines core — auditable, no bloat
Installation
pip install slugany
Requires Python 3.11+. No runtime dependencies.
Quickstart
from slugany import slugify
slugify("¡Hola Mundo!") # "hola-mundo"
slugify("Café résumé naïve") # "cafe-resume-naive"
slugify("Ñandú coração") # "nandu-coracao"
slugify("Über Straße", lang="de") # "ueber-strasse"
slugify("Hello 🎉 World") # "hello-world"
CLI
# Basic usage
slugany "Hello World"
# hello-world
# Pipe from stdin
echo "Café" | slugany
# cafe
# Case styles
slugany "hello world" --style camel
# helloWorld
slugany "hello world" --style train
# Hello-World
# Truncation with word boundary
slugany "hello-world-foo-bar" --max-length 10 --word-boundary
# hello-world
# Batch mode (one slug per line)
slugany --batch < input.txt
# CSS-safe slugs
slugany "123 main st" --css-safe
# s-123-main-st
Case Styles
slugify("hello world", style="kebab") # "hello-world"
slugify("hello world", style="snake") # "hello_world"
slugify("hello world", style="camel") # "helloWorld"
slugify("hello world", style="pascal") # "HelloWorld"
slugify("hello world", style="dot") # "hello.world"
slugify("hello world", style="train") # "Hello-World"
slugify("hello world", style="filename") # "Hello-World"
slugify("hello world", style="url") # "hello-world"
Languages
Built-in transliteration tables for five languages:
slugify("España", lang="es") # "espana"
slugify("Coração", lang="pt") # "coracao"
slugify("Über Straße", lang="de") # "ueber-strasse"
slugify("Cœur", lang="fr") # "coeur"
slugify("Caffè", lang="it") # "caffe"
Advanced
from slugany import slugify, slugify_batch, is_slug
# Stopwords — remove common words
slugify("the quick brown fox", stopwords=["the", "fox"]) # "quick-brown"
# Custom replacements — substitute before and after transliteration
slugify("hello world", replacements={"hello": "hi"}) # "hi-world"
slugify("Straße", replacements={"ß": "ss"}) # "strass"
# Emoji handling
slugify("Hello 🎉 World", emoji_mode="strip") # "hello-world"
slugify("Hello 🎉 World", emoji_mode="keep") # "hello-world"
# CSS-safe — prefix digit-leading slugs for CSS class names
slugify("123 main st", css_safe=True) # "s-123-main-st"
# Fallback — never get an empty string
slugify("!!!", fallback="untitled") # "untitled"
# Unicode preservation — keep non-ASCII characters
slugify("Ñandú", allow_unicode=True) # "ñandú"
# Max length with word boundary — truncate without breaking words
slugify("hello world foo bar", max_length=15, word_boundary=True) # "hello-world"
# Batch processing
slugify_batch(["Hello World", "Café Résumé"]) # ["hello-world", "cafe-resume"]
# Validation
is_slug("hello-world") # True
is_slug("hello world") # False
is_slug("hello_world", separator="_") # True
is_slug("hello-wörld", allow_unicode=True) # True
# Cache inspection
from slugany import slugify
slugify.cache_info() # CacheInfo(hits=0, misses=1, maxsize=512, currsize=1)
slugify.cache_clear() # Clear the cache
Migration from python-slugify
slugany is designed as a drop-in replacement. The main difference is that all arguments are keyword-only:
# python-slugify
from slugify import slugify
slugify("Hello World", "_")
slugify("Hello World", separator="_", stopwords=["the"])
# slugany
from slugany import slugify
slugify("Hello World", separator="_")
slugify("Hello World", separator="_", stopwords=["the"])
See the migration guide for full details.
Documentation
Full documentation at mathiaspaulenko.github.io/slugany
- Basic usage
- Styles & presets
- Languages
- CLI reference
- API reference
- Contracts & guarantees
- Performance
Contributing
Contributions are welcome! See CONTRIBUTING.md for development setup, PR process, and code style guidelines.
Please read our Code of Conduct before participating.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file slugany-0.2.0.tar.gz.
File metadata
- Download URL: slugany-0.2.0.tar.gz
- Upload date:
- Size: 37.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fe2dbb80d72363ea08fdf92c7638ebc904bcf1d029e9517405781b0fc11d0be
|
|
| MD5 |
3d920b1d5d821e2b9c71184a009c229d
|
|
| BLAKE2b-256 |
5c1e0d7add03e1269e2a6d6de18d58f73518eb1f3dc696a89236d0bd2a89ceb5
|
Provenance
The following attestation bundles were made for slugany-0.2.0.tar.gz:
Publisher:
release.yml on MathiasPaulenko/slugany
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
slugany-0.2.0.tar.gz -
Subject digest:
1fe2dbb80d72363ea08fdf92c7638ebc904bcf1d029e9517405781b0fc11d0be - Sigstore transparency entry: 2522634042
- Sigstore integration time:
-
Permalink:
MathiasPaulenko/slugany@470b8c93d96438ad5583be545f7cd252577f4eea -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/MathiasPaulenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@470b8c93d96438ad5583be545f7cd252577f4eea -
Trigger Event:
push
-
Statement type:
File details
Details for the file slugany-0.2.0-py3-none-any.whl.
File metadata
- Download URL: slugany-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbc9da70bf1bbab59d8d28b5a794c62e6f9bac431ba0e3ac323c23491e4c8612
|
|
| MD5 |
7eab657f100a25bcf2405b2e6f5fe545
|
|
| BLAKE2b-256 |
8ee538926016b9516f1dd03af539af35338367f2e2e5d74a292f5c94e60450c3
|
Provenance
The following attestation bundles were made for slugany-0.2.0-py3-none-any.whl:
Publisher:
release.yml on MathiasPaulenko/slugany
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
slugany-0.2.0-py3-none-any.whl -
Subject digest:
bbc9da70bf1bbab59d8d28b5a794c62e6f9bac431ba0e3ac323c23491e4c8612 - Sigstore transparency entry: 2522634154
- Sigstore integration time:
-
Permalink:
MathiasPaulenko/slugany@470b8c93d96438ad5583be545f7cd252577f4eea -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/MathiasPaulenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@470b8c93d96438ad5583be545f7cd252577f4eea -
Trigger Event:
push
-
Statement type: