Skip to main content

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)]
ProfileGrößeÜber
Simple (default)grosseuber
Dingroesseueber

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' — raise Polyslug\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.