A title like "Crème Brûlée Café", "Привет мир" or "重庆火锅" has to become a URL slug somehow, and there are only three real choices: transliterate it into plain ASCII letters, keep the original characters and let them be percent-encoded, or write a slug in another language by hand. This guide compares them, checks what Google's documentation says, and walks through Latin accents, German umlauts, Cyrillic, Greek and Chinese pinyin with real output from our URL slug generator, plus where popular slugify functions silently lose letters.

Three ways to handle non-ASCII titles

ApproachExample outputStrengthsWeaknesses
Transliterate to ASCIIcreme-brulee-cafe, privet-mir, chong-qing-huo-guoWorks in every browser, app, CMS and server; easy to typeThe romanized form must be readable for your audience; tone marks and some distinctions are lost
Keep Unicodecrème-brûlée-café, москва-гид, 重庆火锅Matches the words your audience reads and searchesBecomes long percent-encoded text wherever links are encoded; harder to type on other keyboards
Write or translate by handchongqing-hot-potFull control over the wordsManual work for every page; easy to become inconsistent

Most international sites pick one approach per language and use it everywhere. The worst outcome is a mix, where some pages in the same section are romanized and others are not.

What Google says about non-English URLs

Google's URL structure guidelines say to use words in your audience's language in the URL, "and, if applicable, transliterated words". Its examples include a German path, /lebensmittel/pfefferminz, and a Japanese one, /ペパーミント. So both romanized and native-script URLs are acceptable to Google.

The same page adds two technical rules that matter here:

  • Percent-encode non-ASCII characters in links. When you link to your pages, characters outside ASCII should be percent-encoded in the href attribute. Google's example is gem%C3%BCse rather than gemüse.
  • URLs are case sensitive. Google treats /APPLE and /apple as different URLs, which is one more reason to lowercase consistently in every script that has case.

What percent-encoding does to a slug

A URL can only carry a limited set of ASCII characters, so every other character is sent as its UTF-8 bytes, each written as % plus two hexadecimal digits. Running the browser's own encodeURI and encodeURIComponent functions gives:

Readable formEncoded form
https://example.com/café-москваhttps://example.com/caf%C3%A9-%D0%BC%D0%BE%D1%81%D0%BA%D0%B2%D0%B0
重庆火锅%E9%87%8D%E5%BA%86%E7%81%AB%E9%94%85

Each accented Latin or Cyrillic letter takes two bytes (six characters encoded), and each Chinese character takes three bytes (nine characters). A four-character Chinese slug becomes 36 characters of encoded text. If your audience shares links in apps that show the encoded form, transliteration reads better. Paste any string into the URL encoder/decoder to see its bytes, or to decode a link back into readable text.

Latin accents and special letters

Most accented Latin letters are a base letter plus a mark, so removing the mark is safe: é → e, ñ → n, ç → c. A few letters have no mark to remove and need an explicit mapping: ß → ss, æ → ae, ø → o, ł → l, đ → d. Our engine handles both; the German umlaut option is covered in the next section. Here is real output next to the common one-line regex text.toLowerCase().replace(/[^a-z0-9]+/g, '-'):

TitleOur defaultQuick regex
Crème Brûlée Cafécreme-brulee-cafecr-me-br-l-e-caf-
Straße über Ärger öffnenstrasse-uber-arger-offnenstra-e-ber-rger-ffnen
Æbleskiver på Øresundaebleskiver-pa-oresund-bleskiver-p-resund
Łódź Kraków Gdańsklodz-krakow-gdansk-d-krak-w-gda-sk
Đà Nẵng Phở Bòda-nang-pho-bo-n-ng-ph-b-

The quick regex is fine for plain English but breaks words apart as soon as a title contains a single accent.

German, Danish and other language conventions

Some languages have their own fallback when special letters are unavailable. In German, ä, ö and ü are traditionally written ae, oe and ue, so mueller is closer to "Müller" than muller. WordPress follows the same idea: its remove_accents() function applies ä → ae, ö → oe, ü → ue and ß → ss only for German locales (de_DE, de_CH, de_AT), and å → aa, ø → oe for Danish. Without a locale, WordPress maps ß to a single s.

Our tool uses one general mapping for every language, plus a German umlauts switch. With it on, "Müller Größe Übersicht" becomes mueller-groesse-uebersicht instead of muller-grosse-ubersicht, and "Straße über Ärger öffnen" becomes strasse-ueber-aerger-oeffnen instead of strasse-uber-arger-offnen.

Danish å becomes a and ø becomes o in our mapping, as in aebleskiver-pa-oresund. If you need the Danish aa and oe forms, edit those slugs by hand.

Stop words are language-specific too. The stop word editor has Spanish, French and German presets that add to the English list: with the Spanish preset, "El Mejor Café de la Ciudad" becomes mejor-cafe-ciudad instead of el-mejor-cafe-de-la-ciudad; the French and German presets give meilleures-crepes-paris and tipps-garten.

Cyrillic and Greek

Cyrillic and Greek letters have no accent to strip, so they need a full letter table. Our engine uses one fixed, English-readable table for each script:

TitleSlug
Привет мирprivet-mir
Щука и Ёжshchuka-i-yozh
Юрий Хрущёвyuriy-khrushchyov
Καλημέρα κόσμεkalimera-kosme

Several romanization systems exist for both scripts, and ours is one generic scheme, not a national standard. A name may therefore differ from its passport or map spelling. Edit brand, person and city slugs by hand, and spot-check Ukrainian, Serbian or Bulgarian text.

Chinese: pinyin slugs

Hanyu Pinyin is the international standard for romanizing Chinese (ISO 7098). Chinese characters have no letters to strip, so a plain slugify function either drops them or needs a pinyin dictionary. Our tools have an optional Chinese → pinyin setting that loads a dictionary only when your text contains Chinese:

TitlePinyin modePinyin offKeep Unicode
重庆火锅chong-qing-huo-guo(empty)重庆火锅
银行开户指南yin-hang-kai-hu-zhi-nan(empty)银行开户指南
绿茶的好处lv-cha-de-hao-chu(empty)绿茶的好处
北京 Travel Guide 2026bei-jing-travel-guide-2026travel-guide-2026北京-travel-guide-2026

Details worth knowing:

  • No tone marks. Tones are dropped because they are not ASCII.
  • ü becomes v, as in lv-cha. This is the convention of pinyin keyboards; some sites write lu or lyu instead.
  • Polyphonic characters are read in context. 重 is chong in 重庆 and 行 is hang in 银行, because the dictionary looks at whole words. Rare names can still be misread.
  • One hyphen per syllable. For word-level slugs such as chongqing-hotpot, write them by hand.
  • Japanese and Korean are different. Japanese kanji get Chinese readings and kana are dropped: 東京 ラーメン becomes dong-jing. Korean Hangul is dropped. For those languages, use Keep Unicode or write the slug yourself.

Different platforms make different trade-offs, and some lose letters without any warning. Django's slugify() normalizes text with NFKD and then drops everything that is not ASCII. That removes accents correctly, but letters that do not decompose disappear completely. These are the results of Django's algorithm next to ours:

TitleDjango defaultDjango allow_unicode=TrueOur default
Crème Brûlée Cafécreme-brulee-cafecrème-brûlée-cafécreme-brulee-cafe
Straße über Ärger öffnenstrae-uber-arger-offnenstraße-über-ärger-öffnenstrasse-uber-arger-offnen
Æbleskiver på Øresundbleskiver-pa-resundæbleskiver-på-øresundaebleskiver-pa-oresund
Привет мир(empty)привет-мирprivet-mir
重庆火锅(empty)重庆火锅chong-qing-huo-guo (pinyin mode)

WordPress's sanitize_title_with_dashes() "does not replace special accented characters" and percent-encodes them instead; when a post is saved, remove_accents() runs first with the locale rules above, so the result depends on the site language.

Code: transliterate accents in JavaScript and Python

If you need the Latin part of this in your own code, normalize to NFKD, remove the combining marks, and map the few letters that do not decompose first. These functions produce the same slugs as our engine for every Latin example in this guide, such as strasse-uber-arger-offnen and aebleskiver-pa-oresund:

const SPECIAL = { ß: 'ss', æ: 'ae', Æ: 'AE', ø: 'o', Ø: 'O', ł: 'l', Ł: 'L', đ: 'd', Đ: 'D', ı: 'i' };

function asciiSlug(text) {
  return text
    .replace(/[ßæÆøØłŁđĐı]/g, (c) => SPECIAL[c])
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

asciiSlug('Łódź Kraków Gdańsk'); // 'lodz-krakow-gdansk'
import re
import unicodedata

SPECIAL = str.maketrans({"ß": "ss", "æ": "ae", "Æ": "AE", "ø": "o", "Ø": "O",
                         "ł": "l", "Ł": "L", "đ": "d", "Đ": "D", "ı": "i"})

def ascii_slug(text: str) -> str:
    text = unicodedata.normalize("NFKD", text.translate(SPECIAL))
    text = "".join(c for c in text if not unicodedata.combining(c)).lower()
    return re.sub(r"[^a-z0-9]+", "-", text).strip("-")

ascii_slug("Straße über Ärger öffnen")  # 'strasse-uber-arger-offnen'

They do not handle Cyrillic, Greek or Chinese. For those scripts, use a full transliteration table or library, or our tools. Our guide to slugify regex and code snippets has complete functions for five languages.

Warning

Common mistakes

  • Using an ASCII-only regex on international titles. As the table above shows, [^a-z0-9]+ turns "Crème" into cr-me.
  • Dropping letters silently. NFKD plus "ignore non-ASCII" loses ß, æ, ø, ł and đ, and empties Cyrillic and Chinese titles. Test your slug function with real titles from every language you publish.
  • Publishing empty slugs. Our tools flag every title whose slug comes out empty; in your own code, add a fallback.
  • Mixing schemes. Switching from muller to mueller or from Unicode to pinyin halfway through a site creates inconsistent URLs, and fixing it later means redirects.
  • Double encoding. Encoding an already encoded slug turns %C3%A9 into %25C3%25A9. Store readable slugs and encode once, when you build the link.

Multilingual slug checklist

[ ] One approach per language section: transliterate, keep Unicode, or translate
[ ] Latin accents removed (é -> e) and special letters mapped (ß -> ss, æ -> ae, ł -> l)
[ ] German content: decide ä -> a or ä -> ae, then use it on every page
[ ] Cyrillic and Greek romanized with one table; names checked by hand
[ ] Chinese: pinyin mode on, output scanned for misread names
[ ] Japanese and Korean: Keep Unicode or a hand-written slug
[ ] No empty slugs; no silently dropped letters
[ ] Non-ASCII links percent-encoded once in href
[ ] Language stop words removed where they do not change the meaning
[ ] Slugs lowercase and unique in their folder

Try it in the tools

The URL slug generator has Smart transliteration, German umlauts, Chinese → pinyin and Keep Unicode switches, plus Spanish, French and German stop word presets. The bulk CSV slug generator applies them to a whole spreadsheet. To keep the romanized slugs short, follow our URL slug length guide. To strip accents from names or plain text without turning it into a slug, use the remove accents tool. For pinyin versus Chinese-character URLs in depth, read our Chinese URL slug guide.

FAQ

Frequently asked questions

Should I transliterate URLs or keep native characters?

Both work for Google, which recommends words in your audience's language and accepts transliterated words. Transliterate when links are often shared or typed on other keyboards; keep native characters when your audience reads that script and your platform handles UTF-8 consistently.

Should ü become u or ue in a URL slug?

For German content, ue is the traditional spelling when umlauts are unavailable, so mueller is closer to Müller. For other languages, u is the usual choice. Pick one rule per site and keep it.

How do I make a slug from a Chinese title?

Turn on Chinese to pinyin in our tools: 重庆火锅 becomes chong-qing-huo-guo, without tone marks. You can also keep the Chinese characters with Keep Unicode, or write an English slug by hand.

Why does my slug come out empty?

The title contains only characters the function cannot romanize, such as Chinese with pinyin off, Japanese kana or Korean. Turn on pinyin for Chinese, switch to Keep Unicode, or write the slug by hand.