Przejdź do treści

Przyjazny URL z polskich znaków

  • Teksty i String
  • poziom: średni
  • aktualizacja: 16 sierpnia 2026

Slug to krótka, bezpieczna część adresu, np. zolc-i-lodz zamiast tytułu „Żółć i Łódź!”. Jeśli wyczyścisz tytuł samym replaceAll("[^a-z]", "-"), z „łódź” zostanie --d- — polskie litery po prostu wypadną. Klasa Normalizer rozkłada większość znaków diakrytycznych na literę i osobny akcent, który wystarczy pominąć. Wyjątkiem jest ł: tej litery Normalizer nie rusza, więc kod obsługuje ją jawnie, a przy okazji scala wiele separatorów i tworzy unikalne warianty -2, -3.

JAVA PrzyjaznyUrl.java 90 linii Pobierz .java
import java.text.Normalizer;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * Tworzenie przyjaznych adresów URL z polskich tytułów — skryptyJAVA.pl
 * Zapisz jako: PrzyjaznyUrl.java
 */
public final class PrzyjaznyUrl {

    private static final Locale POLSKI = Locale.forLanguageTag("pl-PL");

    private PrzyjaznyUrl() {
        // Klasa zawiera wyłącznie metody statyczne.
    }

    /**
     * Zamienia tekst na mały, ASCII-owy slug, np. „Żółć i Łódź!” na „zolc-i-lodz”.
     * Dla null, pustego tekstu lub samych symboli zwraca pusty napis.
     */
    public static String utworzSlug(String tekst) {
        if (tekst == null || tekst.isBlank()) {
            return "";
        }

        // Normalizer nie rozkłada litery ł, więc zamieniamy ją osobno.
        String maleLitery = tekst.trim().toLowerCase(POLSKI).replace('ł', 'l');
        String rozlozony = Normalizer.normalize(maleLitery, Normalizer.Form.NFD);
        StringBuilder wynik = new StringBuilder(rozlozony.length());

        for (int i = 0; i < rozlozony.length();) {
            int znak = rozlozony.codePointAt(i);
            i += Character.charCount(znak);

            if ((znak >= 'a' && znak <= 'z') || (znak >= '0' && znak <= '9')) {
                wynik.appendCodePoint(znak);
            } else if (Character.getType(znak) == Character.NON_SPACING_MARK) {
                // Akcent po rozłożeniu, np. kropka z „ż” — pomijamy go.
                continue;
            } else if (!wynik.isEmpty() && wynik.charAt(wynik.length() - 1) != '-') {
                // Spacje, myślniki, emoji i interpunkcja stają się jednym separatorem.
                wynik.append('-');
            }
        }

        if (!wynik.isEmpty() && wynik.charAt(wynik.length() - 1) == '-') {
            wynik.setLength(wynik.length() - 1);
        }
        return wynik.toString();
    }

    /**
     * Tworzy slug i od razu rezerwuje go w zbiorze; przy kolizji dopisuje -2, -3 itd.
     * Pusty wynik zastępuje bezpieczną nazwą „element”.
     */
    public static String utworzUnikalnySlug(String tekst, Set<String> zajeteSlugi) {
        String baza = utworzSlug(tekst);
        if (baza.isEmpty()) {
            baza = "element";
        }

        String kandydat = baza;
        int numer = 2;
        while (!zajeteSlugi.add(kandydat)) {
            kandydat = baza + "-" + numer++;
        }
        return kandydat;
    }

    // ── PRZYKŁADY ───────────────────────────────────────────
    public static void main(String[] args) {
        List<String> tytuly = List.of(
                "Żółć i Łódź!",
                "  Java 17 — praktyczny kurs  ",
                "Ceny: 50% taniej!!!",
                "Zażółć gęślą jaźń",
                "☕");

        for (String tytul : tytuly) {
            System.out.printf("%-30s -> %s%n", tytul, utworzSlug(tytul));
        }

        Set<String> zajete = new HashSet<>();
        System.out.println(utworzUnikalnySlug("Kawa i Java", zajete));
        System.out.println(utworzUnikalnySlug("Kawa i Java!", zajete));
        System.out.println(utworzUnikalnySlug("Kawa   i   Java", zajete));
    }
}

Jak użyć tego skryptu

  1. Skopiuj kod i zapisz go w pliku PrzyjaznyUrl.java — dokładnie tak, jak nazywa się klasa publiczna — a następnie uruchom poleceniem java PrzyjaznyUrl.java. Przykłady pokażą zamianę polskich tytułów oraz obsługę powtórzeń.
  2. W swoim projekcie wywołaj PrzyjaznyUrl.utworzSlug(tytul). Metoda zwraca wyłącznie małe litery ASCII, cyfry i pojedyncze myślniki, więc wynik można bezpiecznie umieścić w ścieżce URL.
  3. Jeśli slug ma być unikalny w bieżącej kolekcji, przekaż mutowalny Set<String> do utworzUnikalnySlug. Metoda sama doda wybrany wynik do zbioru.
  4. W aplikacji z bazą danych zastąp zbiór sprawdzeniem kolumny slug i załóż na niej indeks UNIQUE; w razie równoczesnej kolizji ponów próbę z kolejnym numerem. Pamiętaj też o długości — metoda niczego nie obcina, więc tytuł na 300 znaków da slug na 300 znaków. Przytnij go do szerokości kolumny (zwykle 100–200 znaków), najlepiej na ostatnim myślniku przed limitem, żeby nie urwać słowa w połowie.

Warto wiedzieć

  • Normalizer w trybie NFD rozkłada m.in. ą, ć, ó i ż, ale nie literę ł. Jawne replace('ł', 'l') nie jest ozdobnikiem — bez niego „Łódź” zmieniłaby się w „odz”.
  • Kod celowo pozostawia tylko alfabet łaciński ASCII i cyfry. Tytuł zapisany wyłącznie cyrylicą, alfabetem greckim albo emoji da pusty wynik; metoda unikalna zastąpi go wtedy nazwą element. Gorzej z tekstem mieszanym: litery łacińskie, których NFD nie rozkłada — ß, đ, æ czy ligatura fi — zamieniają się w separator i po cichu rozrywają słowo, więc „Straße” da stra-e, a nie strasse. Jeśli spodziewasz się takich tytułów, dopisz własne reguły transliteracji przed wywołaniem metody.
  • Slug nie powinien być jedynym identyfikatorem rekordu. Tytuł prędzej czy później się zmieni, dlatego w bazie trzymaj stałe ID, a stary slug przekieruj kodem HTTP 301 na nowy adres — inaczej stracisz wszystkie linki, które ktoś zdążył zapisać.
  • Przekazany zbiór jest modyfikowany przez utworzUnikalnySlug i nie zapewnia bezpieczeństwa między wątkami. W aplikacji WWW ostateczną ochroną przed duplikatem powinno być ograniczenie UNIQUE w bazie.
#polskie znaki #unicode #regex #slug #seo

Podobne skrypty