45 lines
1.3 KiB
Python
45 lines
1.3 KiB
Python
import re
|
|
import unicodedata
|
|
|
|
|
|
def slugify(title: str, max_length: int = 80) -> str:
|
|
"""
|
|
Convert a title to a filesystem-friendly slug:
|
|
- normalize unicode to NFKD and strip accents
|
|
- lowercase
|
|
- replace non alphanum with hyphens
|
|
- collapse duplicate hyphens
|
|
- trim hyphens at edges
|
|
- limit length (keeping whole segments if possible)
|
|
"""
|
|
if not title:
|
|
return "untitled"
|
|
|
|
# Normalize accents
|
|
normalized = unicodedata.normalize("NFKD", title)
|
|
ascii_str = normalized.encode("ascii", "ignore").decode("ascii")
|
|
|
|
# Lowercase and replace non-alphanum with hyphen
|
|
slug = re.sub(r"[^a-z0-9]+", "-", ascii_str.lower()).strip("-")
|
|
|
|
if not slug:
|
|
slug = "untitled"
|
|
|
|
# Collapse multiple hyphens
|
|
slug = re.sub(r"-{2,}", "-", slug)
|
|
|
|
# Respect max length, try not to cut segments
|
|
if len(slug) > max_length:
|
|
parts = slug.split("-")
|
|
cut = []
|
|
total = 0
|
|
for p in parts:
|
|
# +1 for the hyphen when joining (except first)
|
|
add = len(p) + (1 if cut else 0)
|
|
if total + add > max_length:
|
|
break
|
|
cut.append(p)
|
|
total += add
|
|
slug = "-".join(cut) if cut else slug[:max_length].rstrip("-")
|
|
|
|
return slug |