Transliteration and Unicode
ASCII transliteration
By default non-ASCII source text is folded to a URL-safe ASCII slug. Pick the profile that matches your audience:
#[Polyslug(source: 'title', transliterate: TransliterationProfile::Din)]
| Profile | Größe → | Über → |
|---|---|---|
Simple (default) | grosse | uber |
Din | groesse | ueber |
Simple drops the umlaut; Din expands it the German-standard way. Neither is more
correct in general — pick the one your readers expect to see in a URL, and keep it
consistent, because changing the profile changes future slugs (existing ones stay put
and keep resolving through their history).
Native Unicode slugs
For non-Latin markets, transliteration destroys the slug's whole purpose: a Chinese
headline folded to ASCII is not readable by anyone. Set unicode: 'native' to keep
Unicode letters and numbers instead:
#[Polyslug(source: 'headline', unicode: 'native')]
class Article extends Model implements Sluggable
{
use HasPolyslug;
}
Everything that is not a letter or a number is collapsed into the separator, so the grammar of a slug is unchanged — only its character set widens. Chinese, Cyrillic, Greek and accented Latin all survive.
Slugs are lower-cased at generation, mb-aware, so the case-insensitive unique index behaves identically on PostgreSQL, SQLite and MySQL — the three disagree on how to lower-case non-ASCII letters, and folding at write time is what removes that disagreement from the equation. Input is assumed to be NFC-normalized.
Titles with nothing to slugify
A title made only of emoji or of characters the profile strips leaves an empty slug.
emptyFallback decides what happens:
'id-only'(default) — store an empty slug, so the URL is just_{encodedId}and the save never fails.'throw'— raisePolyslug\Exceptions\CouldNotGenerateSlug.
The default is deliberate: a user saving a CJK-only or emoji-only title should get a
working (if plain) URL, not a 500. Choose 'throw' only where an empty slug is a data
problem you want surfaced at write time.
Note that with unicode: 'native' a CJK title is not empty at all — it becomes a CJK
slug, and the fallback never triggers.
Length
maxLength trims a slug to at most that many characters and never leaves it ending
mid-separator. It applies after slugification, so it counts the characters that actually
appear in the URL.