Skip to main content

Lekka odmiana polskich rzeczowników przez przypadki (dane SGJP, indeks marisa-trie, mmap).

Project description

polish-inflection

PyPI Python versions CI License: BSD-2-Clause

Lekka, dane-only odmiana polskich rzeczowników przez przypadki. Dane pochodzą ze Słownika gramatycznego języka polskiego (SGJP), wyszukiwanie oparte jest o kompaktowy indeks marisa-trie (mmap, ~1 µs/lookup). Instalacja bez kompilatora (gotowe binarne wheele), zero Django, jedna zależność runtime.


🇵🇱 Wersja polska

Cel

Chcesz z wyrazu wydział dostać wydziału (dopełniacz), wydziałowi (celownik), wydziałów (dopełniacz liczby mnogiej) — programowo, bez trzymania form ręcznie w tabeli. polish-inflection odmienia rzeczowniki przez 7 przypadków × 2 liczby w obie strony:

  • generacja: lemat + przypadek + liczba → forma (odmien);
  • analiza (kierunek zwrotny): forma → [analizy] (podaj).

Dlaczego — luka w ekosystemie

Nie istniało lekkie, dane-only, przyjazne wdrożeniu rozwiązanie do odmiany polskich rzeczowników przez przypadki:

  • gettext / formy mnogie rozwiązują inny problem — wybór wariantu wg liczby (nplurals), a nie odmianę przez przypadki. gettext nie zna pojęcia przypadka i nie potrafi odmienić rzeczownika.
  • Morfeusz 2 to kanoniczny analizator i generator morfologii polskiej, ale natywny silnik C++ + bindingi SWIG + kompilacja słownika rzędu dziesiątek MB. Armata na muchę i ból wdrożeniowy w kontenerze. Tu nie ma silnika, SWIG-a ani kompilacji słownika — tylko dane i pip install.
  • pymorphy2 / pymorphy3 obsługują tylko rosyjski i ukraiński — nie polski.
  • inflection / inflect / pyinflect — tylko angielski.

Kluczowa idea: bierzemy dane SGJP (trójki forma / lemat / tag), a odrzucamy silnik. Zbiór form jest zamknięty i w pełni wyliczony — nic nie generujemy w locie, tylko indeksujemy i wyszukujemy.

Instalacja

pip install polish-inflection

pip install polish-inflection ciąga sam kod (wheel ~35 KB) plus dwie zależności: marisa-trie (kompilowane rozszerzenie C++ z gotowymi binarnymi wheelami) oraz polish-inflection-data — osobny pakiet z indeksami SGJP (~50 MB), wersjonowany wg edycji słownika. Dzięki temu wydania kodu są malutkie i nie re-publikują danych; nowa edycja SGJP → wydanie tylko pakietu danych. pip install nie potrzebuje kompilatora. Czytnik używa mmap: import nie ładuje słownika do RAM, a pojedynczy lookup stronicuje tylko O(długość słowa) węzłów (~1 µs, mierzony narzut RSS ~1,5 MB zamiast ~23 MB pełnego wczytania).

Przykłady — odmien

from polish_inflection import odmien, DOPEŁNIACZ, MNOGA

odmien("wydział", DOPEŁNIACZ)          # -> "wydziału"
odmien("wydział", DOPEŁNIACZ, MNOGA)   # -> "wydziałów"

odmien ma czytelny alias odmien_rzeczownik (symetria z odmien_przymiotnik / odmien_fraze) — to ta sama funkcja.

Zachowanie przy słowie spoza słownika jest sterowane parametrem default:

from polish_inflection import (
    odmien, odmien_lub_none, odmien_lub_wyraz, BrakOdmiany, DOPEŁNIACZ,
)

odmien("wydział", DOPEŁNIACZ)                 # "wydziału"
odmien("qwerty", DOPEŁNIACZ)                  # raise BrakOdmiany
odmien_lub_none("qwerty", DOPEŁNIACZ)         # None
odmien_lub_wyraz("qwerty", DOPEŁNIACZ)        # "qwerty"  (passthrough wejścia)
odmien("qwerty", DOPEŁNIACZ, default="—")     # "—"       (dowolny fallback)

Oboczności (kilka poprawnych form w jednym slocie) zwraca odmien_warianty:

from polish_inflection import odmien_warianty, MIEJSCOWNIK
odmien_warianty("pokój", MIEJSCOWNIK)   # -> lista wszystkich poprawnych form

Przykłady — podaj (kierunek zwrotny)

Polszczyzna ma synkretyzm (jedna forma = wiele przypadków) i homografię (jedna forma = wiele lematów), więc podaj zwraca listę analiz:

from polish_inflection import podaj

podaj("jednostki")
# [Analiza(lemat='jednostka', przypadek='gen', liczba='sg', rodzaj='f'),
#  Analiza(lemat='jednostka', przypadek='nom', liczba='pl', rodzaj='f'),
#  Analiza(lemat='jednostka', przypadek='acc', liczba='pl', rodzaj='f'),
#  Analiza(lemat='jednostka', przypadek='voc', liczba='pl', rodzaj='f')]

podaj("jednostki", liczba="pl")   # zawężenie do liczby mnogiej

Analiza to lekki NamedTuple: (lemat, przypadek, liczba, rodzaj).

Pytania przypadkowe (pytania)

Warstwa czytająca się jak zdanie: funkcje nazwane pytaniami przypadków. Zakładają wyraz w mianowniku, zgadują liczbę i zwracają żądany przypadek:

from polish_inflection import kogo_czego, komu_czemu, podstawowa_forma

kogo_czego("wydział")     # "wydziału"    (dopełniacz l.poj.)
kogo_czego("wydziały")    # "wydziałów"   (liczba zgadnięta z mianownika)
komu_czemu("jednostka")   # "jednostce"

# operacja odwrotna: dowolna forma -> forma podstawowa (lemat)
podstawowa_forma("wydziałów")   # "wydział"

Kanoniczne: kogo_czego, komu_czemu, kogo_co, z_kim_z_czym, o_kim_o_czym (+ aliasy komu/czemu/z_kim/z_czym/o_kim/o_czym w polish_inflection.pytania). Domyślnie przy braku formy zwracają wejściowy wyraz (passthrough — pod UI). Pełny opis: docs/api.md.

Zgoda liczebnikowa — poprawna forma rzeczownika przy liczbie (odmiana_liczebnikowa):

from polish_inflection import odmiana_liczebnikowa, NARZĘDNIK

odmiana_liczebnikowa("wydział", 1)             # "wydział"
odmiana_liczebnikowa("wydział", 2)             # "wydziały"
odmiana_liczebnikowa("wydział", 5)             # "wydziałów"
odmiana_liczebnikowa("wydział", 5, NARZĘDNIK)  # "wydziałami"
f"{5} {odmiana_liczebnikowa('wydział', 5)}"    # "5 wydziałów"

Zwraca sam rzeczownik (liczebnik słownie nie jest generowany). W przeciwieństwie do gettext/ngettext nie musisz wpisywać form ręcznie i działa w każdym przypadku, nie tylko w mianowniku.

Odmiana przymiotnika (odmien_przymiotnik)

Przymiotniki odmieniamy regułą, nie indeksem — ich deklinacja jest regularna, więc nie zajmują miejsca w słowniku (zero przyrostu danych). Zwalidowane przeciw pełnemu SGJP: l.poj. 99,9%.

from polish_inflection import odmien_przymiotnik, DOPEŁNIACZ, MIEJSCOWNIK, MĘSKI, ŻEŃSKI, NIJAKI

odmien_przymiotnik("lubelski", DOPEŁNIACZ, MĘSKI)     # "lubelskiego"
odmien_przymiotnik("medyczny", DOPEŁNIACZ, ŻEŃSKI)    # "medycznej"
odmien_przymiotnik("stosowany", MIEJSCOWNIK, NIJAKI)  # "stosowanym"

lemat to mianownik l.poj. rodzaju męskiego (forma słownikowa); rodzaj to rodzaj słowa określanego (MĘSKI/ŻEŃSKI/NIJAKI, jak zwraca podaj).

Kierunek zwrotny dla przymiotnika daje podaj_przymiotnik (forma → [Analiza]):

from polish_inflection import podaj_przymiotnik

podaj_przymiotnik("wołowa")   # [Analiza('wołowy','nom','sg','f'), Analiza('wołowy','voc','sg','f')]
podaj_przymiotnik("Michała")  # []  — forma rzeczownika, nie przymiotnik

Rozpoznawanie jest leksykalne: kandydaci na bazę są filtrowani zbiorem prawdziwych baz deklinacyjnych z SGJP (mały indeks przymiotniki.marisa, 0,5 MB), więc formy które tylko wyglądają jak przymiotnik (np. rzeczownik Michałamichały) dają []. podaj (rzeczowniki) i podaj_przymiotnik (przymiotniki) są rozdzielonepodaj nic nie robi z przymiotnikami.

Odmiana nazw wielowyrazowych (odmien_fraze)

Odmiana wielowyrazowych nazw własnych instytucji — odmienia rzeczownik-głowę i uzgadniające się przymiotniki (także w liczbie mnogiej), a dopełniaczowe dopełnienie zamraża:

from polish_inflection import odmien_fraze, DOPEŁNIACZ, MIEJSCOWNIK, MNOGA

odmien_fraze("Uniwersytet Lubelski", DOPEŁNIACZ)            # "Uniwersytetu Lubelskiego"
odmien_fraze("Uniwersytet Lubelski", DOPEŁNIACZ, MNOGA)     # "Uniwersytetów Lubelskich"
odmien_fraze("Akademia Medyczna", MIEJSCOWNIK)              # "Akademii Medycznej"
odmien_fraze("Instytut Technologii Stosowanej", DOPEŁNIACZ) # "Instytutu Technologii Stosowanej"
odmien_fraze("Uniwersytet im. Marii Curie", DOPEŁNIACZ)     # "Uniwersytetu im. Marii Curie"

Parser jest heurystyczny (pod nazwy własne): wykrywa głowę, uzgadnia przymiotniki, zamraża ogon od pierwszego dopełnienia zależnego lub markera im.. Nieusuwalne dwuznaczności (np. „Instytut Polski" = przymiotnik czy dopełniacz „Polski"?) domyślnie zamraża; w warstwie aplikacji warto mieć override na wyjątki.

Wielkość liter rozstrzyga nazwę własną. Fraza pisana w całości małą literą jest traktowana jak zwykłe rzeczowniki (nie nazwy własne) — nie sięgamy do gazeteera nazw miejscowych i nie kapitalizujemy wyniku:

odmien_fraze("dupa wołowa", DOPEŁNIACZ)   # "dupy wołowej"  (nie "dupa Wołowa"!)
odmien_fraze("sala gimnastyczna", DOPEŁNIACZ)  # "sali gimnastycznej"

Homografy rodzajowe

Niektóre słowa to dwa wyrazy o tej samej pisowni — np. profesor: męski (odmienny: profesora) i żeński nieodmienny (profesor, jak „pani profesor"). Domyślnie odmien wybiera formę odmienioną; możesz też wymusić rodzaj:

from polish_inflection import odmien, DOPEŁNIACZ, MĘSKI, ŻEŃSKI

odmien("profesor", DOPEŁNIACZ)                 # "profesora"  (domyślnie odmieniona)
odmien("profesor", DOPEŁNIACZ, rodzaj=ŻEŃSKI)  # "profesor"   (żeński nieodmienny)
odmien("profesor", DOPEŁNIACZ, rodzaj=MĘSKI)   # "profesora"

Stałe

Przypadki: MIANOWNIK, DOPEŁNIACZ, CELOWNIK, BIERNIK, NARZĘDNIK, MIEJSCOWNIK, WOŁACZ. Liczby: POJEDYNCZA, MNOGA (domyślnie POJEDYNCZA). Rodzaje: MĘSKI, ŻEŃSKI, NIJAKI. Sentinele default: TEN_SAM_WYRAZ (passthrough) i RAISES (wyjątek).

Źródło danych i atrybucja

Dane fleksyjne pochodzą ze Słownika gramatycznego języka polskiego (SGJP), w wersji przypiętej i zwendorowanej w repozytorium (patrz data/sgjp/). Zbudowane indeksy pokrywają 223 748 lematów / ~3,86 mln rekordów form i ważą łącznie ~49 MB (odmien.marisa ≈ 24 MB, podaj.marisa ≈ 25 MB, plus przymiotniki.marisa ≈ 0,5 MB baz przymiotników). Jadą w osobnym pakiecie polish-inflection-data (wersjonowanym wg edycji SGJP).

Copyright © 2007–2026 Marcin Woliński, Zbigniew Bronk, Włodzimierz Gruszczyński, Witold Kieraś, Zygmunt Saloni, Danuta Skowrońska, Robert Wołosz.

SGJP jest udostępniany na licencji 2-clause BSD. Strona licencyjna: https://morfeusz.sgjp.pl/doc/license/en. Pełny tekst i szczegóły atrybucji — patrz NOTICE.md oraz data/sgjp/LICENSE.sgjp.

Licencja

Dwie warstwy, jawnie rozdzielone:

  • Kod pakietu — BSD-2-Clause, © Michał Pasternak (plik LICENSE).
  • Dane SGJP — BSD-2-Clause, © autorzy wymienieni wyżej. Redystrybucja jest dozwolona pod warunkiem zachowania noty copyright, tekstu licencji i atrybucji (szczegóły w NOTICE.md).

Ograniczenia (v1)

  • Rdzeń danych: rzeczowniki (indeks SGJP). Przymiotniki i zgoda we frazach są regułowe (odmien_przymiotnik, odmien_fraze); brak czasowników.
  • Bez guessera słów spoza słownika i bez analizy biegnącego tekstu (segmentacji/tokenizacji) — świadome YAGNI.
  • Zakres: 7 przypadków × 2 liczby, oba kierunki (odmien / podaj).

🇬🇧 English version

Purpose

Turn wydział into wydziału (genitive), wydziałowi (dative), wydziałów (genitive plural) — programmatically, without keeping inflected forms by hand. polish-inflection declines Polish nouns across 7 cases × 2 numbers in both directions:

  • generation: lemma + case + number → form (odmien);
  • analysis (reverse): form → [analyses] (podaj).

Why — a gap in the ecosystem

There was no lightweight, data-only, deployment-friendly way to decline Polish nouns by case:

  • gettext / plural forms solve a different problem — picking a variant by number (nplurals), not declension by case. gettext has no concept of grammatical case and cannot decline a noun.
  • Morfeusz 2 is the canonical Polish morphological analyzer and generator, but ships a native C++ engine + SWIG bindings + a compiled dictionary of tens of MB. Overkill and a container-deployment headache. Here there is no engine, no SWIG and no dictionary compilation — just data and pip install.
  • pymorphy2 / pymorphy3 cover only Russian and Ukrainian — not Polish.
  • inflection / inflect / pyinflect — English only.

Core idea: take the SGJP data (form / lemma / tag triples), drop the engine. The set of forms is closed and fully enumerated — we generate nothing at runtime, we only index and look up.

Installation

pip install polish-inflection

pip install polish-inflection pulls only the code (~35 KB wheel) plus two dependencies: marisa-trie (a compiled C++ extension with prebuilt binary wheels) and polish-inflection-data — a separate package holding the SGJP indices (~50 MB), versioned by the dictionary edition. So code releases stay tiny and never re-publish the data; a new SGJP edition ships only the data package. pip install needs no compiler. The reader uses mmap: import does not load the dictionary into RAM, and a single lookup pages in only O(word length) nodes (~1 µs, measured RSS overhead ~1.5 MB instead of ~23 MB for a full load). The indices cover 223,748 lemmas / ~3.86M form records and weigh ~49 MB total (odmien.marisa ≈ 24 MB, podaj.marisa ≈ 25 MB, plus przymiotniki.marisa ≈ 0.5 MB of adjective bases).

Examples — odmien

from polish_inflection import odmien, DOPEŁNIACZ, MNOGA

odmien("wydział", DOPEŁNIACZ)          # -> "wydziału"
odmien("wydział", DOPEŁNIACZ, MNOGA)   # -> "wydziałów"

Out-of-dictionary behaviour is controlled by default:

from polish_inflection import (
    odmien, odmien_lub_none, odmien_lub_wyraz, BrakOdmiany, DOPEŁNIACZ,
)

odmien("wydział", DOPEŁNIACZ)                 # "wydziału"
odmien("qwerty", DOPEŁNIACZ)                  # raises BrakOdmiany
odmien_lub_none("qwerty", DOPEŁNIACZ)         # None
odmien_lub_wyraz("qwerty", DOPEŁNIACZ)        # "qwerty"  (passthrough)
odmien("qwerty", DOPEŁNIACZ, default="—")     # "—"       (any fallback)

odmien_warianty returns all valid forms in a slot (variants).

Examples — podaj (reverse direction)

Polish has syncretism (one form = many cases) and homography (one form = many lemmas), so podaj returns a list of analyses:

from polish_inflection import podaj

podaj("jednostki")
# [Analiza(lemat='jednostka', przypadek='gen', liczba='sg', rodzaj='f'),
#  Analiza(lemat='jednostka', przypadek='nom', liczba='pl', rodzaj='f'),
#  Analiza(lemat='jednostka', przypadek='acc', liczba='pl', rodzaj='f'),
#  Analiza(lemat='jednostka', przypadek='voc', liczba='pl', rodzaj='f')]

podaj("jednostki", liczba="pl")   # narrow to plural

Analiza is a lightweight NamedTuple: (lemat, przypadek, liczba, rodzaj). Constant names stay Polish (MIANOWNIK, DOPEŁNIACZ, …, POJEDYNCZA, MNOGA) and map to SGJP tags (nom, gen, …, sg, pl).

Case-question helpers (pytania)

An ergonomic layer whose functions are named after each case's Polish question. They assume the input word is nominative, infer its number, and return the requested case; podstawowa_forma does the reverse (any form → lemma):

from polish_inflection import kogo_czego, komu_czemu, podstawowa_forma

kogo_czego("wydział")     # "wydziału"    (genitive sg)
kogo_czego("wydziały")    # "wydziałów"   (number inferred from the nominative)
komu_czemu("jednostka")   # "jednostce"
podstawowa_forma("wydziałów")   # "wydział"   (base/dictionary form)

Canonical names: kogo_czego, komu_czemu, kogo_co, z_kim_z_czym, o_kim_o_czym (plus aliases in polish_inflection.pytania). On a miss they return the input word by default (UI-friendly passthrough). Full reference: docs/api.md.

Numeral agreement — the correct noun form for a count (odmiana_liczebnikowa):

from polish_inflection import odmiana_liczebnikowa, NARZĘDNIK

odmiana_liczebnikowa("wydział", 1)             # "wydział"
odmiana_liczebnikowa("wydział", 2)             # "wydziały"
odmiana_liczebnikowa("wydział", 5)             # "wydziałów"
odmiana_liczebnikowa("wydział", 5, NARZĘDNIK)  # "wydziałami"

Returns just the noun (the numeral word isn't generated). Unlike gettext/ngettext you don't hand-write the forms, and it works in every case, not only the nominative.

Data source and attribution

The inflectional data comes from the Grammatical Dictionary of Polish (SGJP), pinned and vendored in this repository (see data/sgjp/).

Copyright © 2007–2026 Marcin Woliński, Zbigniew Bronk, Włodzimierz Gruszczyński, Witold Kieraś, Zygmunt Saloni, Danuta Skowrońska, Robert Wołosz.

SGJP is distributed under the 2-clause BSD license. License page: https://morfeusz.sgjp.pl/doc/license/en. Full text and attribution details are in NOTICE.md and data/sgjp/LICENSE.sgjp.

License

Two clearly separated layers:

  • Package code — BSD-2-Clause, © Michał Pasternak (see LICENSE).
  • SGJP data — BSD-2-Clause, © the authors listed above. Redistribution is permitted provided the copyright notice, license text and attribution are retained (details in NOTICE.md).

Limitations (v1)

  • Data core: nouns (SGJP index). Adjectives and phrase agreement are rule-based (odmien_przymiotnik, odmien_fraze); no verbs.
  • No out-of-dictionary guesser and no running-text analysis (segmentation/tokenization) — deliberate YAGNI.
  • Scope: 7 cases × 2 numbers, both directions (odmien / podaj).

Support graciously provided by

IPLweb

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

polish_inflection-0.7.0-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file polish_inflection-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: polish_inflection-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for polish_inflection-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b95267635371701051bb134bfd56cf0e122d0690e2b5f2d300d45cbdee0ea268
MD5 bbd306989d71063d4210ff28b9e1c9a1
BLAKE2b-256 84cb16e9d6f90ec9a613d9a8bc4f910833414534552a1b937161229484781dfc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page