Milestone 1.
This commit is contained in:
45
app/utils/slugs.py
Normal file
45
app/utils/slugs.py
Normal file
@@ -0,0 +1,45 @@
|
||||
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
|
Reference in New Issue
Block a user