[Intum Dev](https://intum.dev.md) / [Technologia](https://intum.dev/technologia.md)

# [Znaki diakrytyczne i niełacińskie w URL-ach — jak generować ładne slugi](https://intum.dev/technologia/znaki-diakrytyczne-i-nielacinskie-w-url-ach-jak-generowac-ladne-slugi.md)

## Problem

Przy generowaniu URL-i (slugów) z tytułów zawierających znaki spoza ASCII — polskie diakrytyki (ą, ę, ł), cyrylicę (Київ), znaki CJK (東京) — powstaje pytanie: jak zapewnić czytelny, bezpieczny URL?

## Dwa podejścia

### 1. Transliteracja do ASCII (rekomendowane)

Zamiana znaków na ich najbliższe odpowiedniki ASCII:

- `Żółta gęś łąki` → `zolta-ges-laki`
- `Київ столиця` → `kyiv-stolytsya`
- `Produkty świąteczne` → `produkty-swiateczne`

**Zalety:**
- Bezproblemowe kopiowanie/wklejanie linków
- Czytelne w e-mailach, czatach, logach
- Kompatybilne ze wszystkimi systemami i API
- Dobre dla SEO

### 2. UTF-8 w URL-ach

Trzymanie oryginalnych znaków w URL-u (jak Wikipedia):

- W pasku przeglądarki wygląda ładnie: `example.com/artykuły/żółta-gęś`
- W kodzie źródłowym wygląda brzydko (percent-encoding): `example.com/%C5%BC%C3%B3%C5%82ta-%67%C4%99%C5%9B`

**Problemy z UTF-8 w URL-ach:**
- Przy kopiowaniu z paska przeglądarki czasem wkleja się percent-encoded wersja
- W e-mailach i czatach linki mogą się źle łamać
- Starsze systemy i API mogą nie obsłużyć poprawnie
- Trudniejsze debugowanie

## Rozwiązanie per alfabet

### Łaciński z diakrytykami (polski, czeski, niemiecki, francuski)

Działa od razu w Rails — `String#parameterize` automatycznie transliteruje:

```ruby
"Żółta gęś łąki".parameterize
# => "zolta-ges-laki"

"Příliš žluťoučký kůň".parameterize
# => "prilis-zlutoucky-kun"
```

### Cyrylica (ukraiński, rosyjski)

Wymaga dodatkowej konfiguracji. Dwa sposoby:

**Sposób 1 — Initializer z mapowaniem:**

```ruby
# config/initializers/transliterate.rb
ActiveSupport::Inflector.transliterate_mapping = {
  "А" => "A", "Б" => "B", "В" => "V", "Г" => "H",
  "Д" => "D", "Е" => "E", "Є" => "Ye", "Ж" => "Zh",
  "З" => "Z", "И" => "Y", "І" => "I", "Ї" => "Yi",
  "К" => "K", "Л" => "L", "М" => "M", "Н" => "N",
  "О" => "O", "П" => "P", "Р" => "R", "С" => "S",
  "Т" => "T", "У" => "U", "Ф" => "F", "Х" => "Kh",
  "Ц" => "Ts", "Ч" => "Ch", "Ш" => "Sh", "Щ" => "Shch",
  "Ь" => "", "Ю" => "Yu", "Я" => "Ya",
  # + małe litery analogicznie
}
```

**Sposób 2 — Gem babosa (rekomendowane):**

```ruby
"Київ столиця".to_slug.transliterate(:ukrainian).to_s.parameterize
# => "kyiv-stolytsya"
```

### Chiński / Japoński (CJK)

Nie da się zamapować 1:1 — znaki kanji mają wymowę zależną od kontekstu. Opcje:

- **Gem `chinese_pinyin`** — chiński hanzi → pinyin: `北京` → `bei-jing`
- **Gem `romaji`** — japońskie kana → romaji: `東京` → `toukyou`
- **Gem `babosa`** — uniwersalny, obsługuje wiele alfabetów
- **Fallback na token** — jeśli `parameterize` zwróci pusty string, generuj losowy identyfikator

```ruby
def generate_slug(text)
  slug = text.to_s.parameterize
  slug.present? ? slug : Utils.generate_token
end
```

## Rekomendacja

Dla aplikacji SaaS z wieloma locale (pl, en, fr, cs, sk, de, es, uk):

1. **Łaciński** — `parameterize` (działa od razu)
2. **Cyrylica** — gem `babosa` lub initializer z mapowaniem
3. **CJK** — fallback na token
4. **Transliteracja do ASCII** jest lepsza niż UTF-8 w URL-ach — linki są bezpieczne w każdym kontekście

## Porównanie gemów Ruby

| Gem | GitHub Stars | Powstał | Ostatni push | Pobrania | Opis |
|-----|-------------|---------|-------------|----------|------|
| **friendly_id** | ~6 200 | 2008 | 2026 | 54M | Pełna integracja z ActiveRecord — auto-slug, historia, scoped slugs |
| **stringex** | ~980 | 2008 | 2023 | 40M | Rozszerzenia String + wbudowany Unidecoder |
| **babosa** | ~530 | 2010 | 2023 | 157M | Najlepsza transliteracja per-locale, lekki, bez AR |
| **chinese_pinyin** | ~430 | 2010 | 2021 | 1M | Hanzi → pinyin (nieaktywny) |
| **romaji** | ~100 | 2012 | 2025 | 7M | Kana ↔ romaji |

**babosa** ma najwięcej pobrań (157M) bo jest zależnością `friendly_id`. Oba gemy stworzył ten sam autor.

### Który gem wybrać?

- **Tylko transliteracja** (bez integracji z AR) → **babosa**
- **Auto-slug w modelach** (historia, unikalne URL-e) → **friendly_id** (używa babosa pod spodem)
- **Chiński** → **chinese_pinyin** + fallback
- **Japoński** → **romaji** + fallback