Odmiana rzeczowników przy liczebnikach w języku polskim

A to Ci nietypowe temat na tym blogu 😀 Ale spokojnie, będzie o programowaniu 😉 Ostatnio szlifuję pierwszą publiczną wersję pewnej aplikacji, nad którą pracuję hobbystycznie. Gryzła mnie tylko jedna rzecz: Odmiana słów przy liczbach. Temat powszechnie znany i irytujący. Jak ładnie i prosto zapisać w kodzie „1 komentarz”, „2 komentarze” ale „5 komentarzy”? Czy da się to jakoś zautomatyzować?

Podany przykład muszę zapisać w postaci kodu. Czy prosty warunek

if (n > 1)

wystarczy? No oczywiście nie, bo w języku polskim formy odmiany mamy aż 3. Język angielski (poza wyjątkami) jest tutaj znacznie prostszy. Jest liczba mnoga, albo jej nie ma — basta. W języku polskim tak łatwo nie jest. Jak czytam na stronie Rady Języka Polskiego „Składnia liczebników”:

(…) na jakiej zasadzie łączą się liczebniki główne z rzeczownikami w grupę podmiotu w zdaniu. Otóż zasada jest taka, że liczebniki 2, 3, 4 oraz liczebniki, których ostatnim członem jest 2, 3, 4 (czyli np. 22, 23, 24, 152, 153, 154 itd.) łączą się z rzeczownikami w mianowniku liczby mnogiej, np. trzy koty, dwadzieścia cztery koty, sto pięćdziesiąt dwa koty. Liczebniki od 5 do 21 i te, które są zakończone na 5-9 (np. 25, 36, 27, 58, 69), łączą się z rzeczownikiem w dopełniaczu liczby mnogiej, np. pięć kotów, siedemnaście kotów, sto siedemdziesiąt siedem kotów.

Jak wybrnąć?

Nie odmieniać

No pierwsza myśl, która przychodzi do głowy: nie odmieniać. Mogę napisać „komentarze: 10” albo „komentarze: 1” albo „komentarze: 3” i brzmi to całkiem zgrabnie. Tak zresztą musiałem zrobić w Disqusie (systemie komentarzy na tym blogu), gdyż autorzy nie przewidzieli w ogóle możliwości odmiany liczby mnogiej w więcej niż dwóch formach. Jest miejsce na przetłumaczenie słowa „comment” dla 0, 1 lub więcej komentarzy:

Odmiana liczby mnogiej przy liczebnikach

Działa! Ale mnie nie satysfakcjonuje. W Disqusie wyboru nie mam, ale we własnej aplikacji mogę zrobić to… dobrze 🙂

Odmieniać!

Można by analizować wypowiedź eksperta z RJP i ułożyć do tego odpowiedni warunek. Ale to nieco karkołomne. Na pewno ktoś musiał już to zrobić za mnie, prawda? Chwila myślenia… zajrzę do gettext!

Przeszukałem dokumentację dla „plural forms” i rzeczywiście znalazłem konkretne warunki dla języka polskiego! A warunek ten to:

Plural-Forms: nplurals=3; \
    plural=n==1 ? 0 : \
           n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;

Moja pierwsza reakcja: 😱 Ale po chwili analizowania wszystko stało się jasne. Kod opisuje dokładnie to samo co odpowiedź Rady Języka Polskiego. Postanowiłem zamienić to na małą funkcję do odmiany słów w aplikacji:

function polishPlural(singularNominativ, pluralNominativ, pluralGenitive, value) {
    if (value === 1) {
        return singularNominativ;
    } else if (value % 10 >= 2 && value % 10 <= 4 && (value % 100 < 10 || value % 100 >= 20)) {
        return pluralNominativ;
    } else {
        return pluralGenitive;
    }
}

Wykorzystuję to w taki sposób:

polishPlurals("komentarz", "komentarze", "komentarzy", 1); // komentarz
polishPlurals("komentarz", "komentarze", "komentarzy", 0); // komentarzy
polishPlurals("komentarz", "komentarze", "komentarzy", 3); // komentarze

Lub ewentualnie razem z bind:

const commentsLabel = polishPlurals.bind(null, 'komentarz', 'komentarze', 'komentarzy');
commentsLabel(1); // komentarz
commentsLabel(0); // komentarzy
commentsLabel(3); // komentarze

Stworzyłem moduł na npm!

Kod jest dostępny na GitHubie: mmiszy/polish-plurals

Paczkę npm zaś można pobrać stąd: polish-plurals. Paczka npm eksportuje zarówno plik .js (CommonJS) jak i .mjs (moduły ECMAScript).

Zachęcam do dodawania issues / komentowania / tworzenia PR-ów 😉

Co robić, jak żyć?

Weź śmiało powyższą paczkę i korzystaj. A jeśli masz bardzo rozbudowane tłumaczenia do przygotowania to może pomyśl o wykorzystaniu gotowych systemów, które dają już takie możliwości? Przykładowo, wspomniany gettext daje ogromne możliwości!