Metadata-Version: 2.4
Name: pyunislavic
Version: 0.1.0
Summary: A Python implementation for transliterating and normalizing text from many Slavic languages and Interslavic into unified writing systems.
Author: Zijie ZHANG
License-Expression: MIT
License-File: LICENSE
Keywords: cyrillic,cyrillization,interslavic,latin,normalization,orthography,romanization,slavic,transliteration
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# PyUniSlavic

A Python implementation for transliterating and normalizing text from many
Slavic languages and Interslavic into unified writing systems.

PyUniSlavic converts text written with a broad range of Latin and Cyrillic
letters used across Slavic orthographies into a selected common Latin,
ASCII-oriented, or Cyrillic representation. It preserves the source words,
grammar, and meaning: it is a writing-system converter, not a machine
translator.

The package is an independent, focused Python port of part of the
transliteration logic in
[`@interslavic/utils`](https://github.com/medzuslovjansky/js-utils). The
upstream algorithm was created for Interslavic transliteration. PyUniSlavic
also uses its permissive input normalization as a practical common writing
layer for text in multiple Slavic languages.

This broader use follows from the design of
[Interslavic orthography](https://interslavic.fun/learn/orthography/) itself.
Its standard Latin and Cyrillic alphabets are cross-Slavic compromises rather
than adaptations of any one national orthography, and they were designed to be
intuitively understandable and easy to transliterate between. Its optional
etymological letters represent shared historical phonemes whose developments
in modern Slavic languages are largely predictable. The official orthography
guide therefore explicitly identifies transcription from any Slavic language
into Interslavic orthography as one possible use of this etymological layer.
It illustrates the principle with `pęť`: one shared etymological form that
relates several divergent national spellings of the same inherited word.

The
[upstream implementation](https://github.com/medzuslovjansky/js-utils/blob/main/src/transliterate/transliterate.ts)
turns that cross-Slavic design into a reusable pipeline: its loose preprocessor
maps many Latin and Cyrillic letter variants, historical characters, and
alternative spellings into a common internal representation; the flavor stage
then preserves or merges selected distinctions before the result is rendered
in the requested output orthography. PyUniSlavic reuses this orthographic
pipeline without changing the source vocabulary or grammar. In this way, a
mechanism created for Interslavic transliteration can also provide practical
cross-Slavic script unification and same-script normalization. This remains
orthographic normalization rather than translation, and it does not claim
exact language-specific phonological conversion for every Slavic language.

PyUniSlavic is not an official Interslavic implementation and is not affiliated
with or endorsed by the Interslavic project.

## Features

- Transliterates between common Latin and Cyrillic representations.
- Normalizes Latin text to a shared Latin orthography.
- Normalizes Cyrillic text to a shared Cyrillic orthography.
- Accepts many modern, historical, and alternative Latin and Cyrillic letters
  recognized by the upstream loose-input normalizer.
- Provides etymological, standard, and simplified output profiles.
- Preserves punctuation, numbers, spacing, and basic capitalization patterns.
- Uses only the Python standard library.
- Does not translate, lemmatize, or alter the grammar of the source language.

## Installation

```bash
pip install pyunislavic
```

## Quick start

```python
from pyunislavic import transliterate

result = transliterate(
    "Меджусловјанскы језык",
    type=1,
    flav=2,
)

print(result)
# Medžuslovjansky jezyk
```

The public function is:

```python
transliterate(i_source: str, type: int, flav: int = 2) -> str
```

## Supported output types

| `type` | Name | Output |
| ---: | --- | --- |
| `1` | Latin | Common Latin orthography with diacritics |
| `2` | ASCII-oriented | Digraph-based Latin output such as `sz`, `cz`, and `zs` |
| `5` | Standard Cyrillic | Common Cyrillic orthography using letters such as `ј`, `љ`, and `њ` |

`type=2` follows the behavior and naming of the upstream `ASCII` mode. With
`flav=2`, some supported soft-consonant letters can remain non-ASCII, so this
mode should be understood as ASCII-oriented rather than an unconditional
7-bit ASCII guarantee.

## Supported flavor profiles

| `flav` | Name | Behavior |
| ---: | --- | --- |
| `2` | Etymological | Preserves the greatest number of distinctions supported by this port |
| `3` | Standard | Merges several historical vowels and soft-consonant distinctions |
| `4` | Slovianto | Applies additional simplification, including `ě → e` and `y → i` |

Every supported output type can be combined with every supported flavor, for a
total of nine combinations:

```text
type = 1, 2, or 5
flav = 2, 3, or 4
```

Unsupported values raise `ValueError`.

Enums are also available:

```python
from enum import IntEnum


class TransliterationType(IntEnum):
    LATIN = 1
    ASCII = 2
    STANDARD_CYRILLIC = 5


class FlavorizationType(IntEnum):
    ETYMOLOGICAL = 2
    STANDARD = 3
    SLOVIANTO = 4
```

They can be imported and passed directly to `transliterate`:

```python
from pyunislavic import (
    FlavorizationType,
    TransliterationType,
    transliterate,
)

result = transliterate(
    "Medžuslovjansky jezyk",
    type=TransliterationType.STANDARD_CYRILLIC,
    flav=FlavorizationType.STANDARD,
)

print(result)
# Меджусловјанскы језык
```

## Same-script normalization

The source and output scripts do not have to be different. Latin input can be
normalized to the selected common Latin profile, and Cyrillic input can be
normalized to the selected common Cyrillic profile.

### Latin input

```python
transliterate(
    "Nawet przy założeniu",
    type=1,
    flav=2,
)
# 'Navet przy založeniu'
```

### Cyrillic input

```python
transliterate(
    "Улаштовуватимуть водопровідний вузол",
    type=5,
    flav=2,
)
# 'Улаштовуватимуть водопровидниј вузол'
```

These examples retain Polish and Ukrainian vocabulary and grammar. Only their
orthographic representation is normalized.

## Extended real-language examples

The following examples were produced by the current implementation from text
in several Slavic languages. Language labels use ISO 639-3 codes. They show
orthographic conversion and normalization as implemented by the upstream-derived
pipeline; they are not translations or claims about official national spelling.

### Latin etymological output (`type=1`, `flav=2`)

#### Belarusian (`bel`)

```text
INPUT
Той жа Юры Чыж рабіў свой бізнэс па «паняцьцях», дзякуючы блізкасьці да «цела» меў прывілеі, атрымліваў вольны доступ у прыбытковыя сфэры, куды пускалі толькі абраных, напрыклад, у гандаль нафтапрадуктамі.

OUTPUT
Toj ža Jury Čiž rabiv svoj biznes pa «panjacjcjah», dźakujuči blizkaści da «cela» mev pryvilei, atrymlivav voĺny dostup u prybytkovyja sfery, kudy puskali toĺki abranyh, napryklad, u gandaĺ naftapraduktami.
```

#### Russian (`rus`)

```text
INPUT
В боях за Дубно 14 февраля 1944 года молодой фотокорреспондент находился в боевых порядках 29-го гвардейского кавалерийского полка, оказавшегося в эпицентре сражения за город.

OUTPUT
V bojah za Dubno 14 fevralja 1944 goda molodoj fotokorrespondent nahodilśa v boevyh poŕadkah 29-go gvardejskogo kavalerijskogo polka, okazavšegośa v epicentre sraženija za gorod.
```

#### Ukrainian (`ukr`)

```text
INPUT
"Улаштовуватимуть водопровідний вузол на водоводі діаметром 900 мм на розі вул. Зеленогірської та вул. Петра Радченка", — йдеться у повідомленні.

OUTPUT
"Ulaštovuvatimut́ vodoprovidnij vuzol na vodovodi diametrom 900 mm na rozi vul. Zelenogirśkoji ta vul. Petra Radčenka", — jdet́śa u povidomlenni.
```

#### Rusyn (`rue`)

```text
INPUT
Ключі організовано за дихотомічною системою і представлено у вигляді основного ключа до родин і 10 ключів до родів і видів кожної родини (кротові, мідицеві, вовчкові, мишівкові, тушканові, сліпакові, мишачі, хом'якові, норицеві).

OUTPUT
Ključi organizovano za dihotomičnoju sistemoju i predstavleno u vigljadi osnovnogo ključa do rodin i 10 ključiv do rodiv i vidiv kožnoji rodini (krotovi, midicevi, vovčkovi, mišivkovi, tuškanovi, slipakovi, mišači, hom'jakovi, noricevi).
```

#### Serbian (`srp`)

```text
INPUT
Бугари су имали 7.000 погинулих 6.000 заробљених војника и 130 топова а Грци су имали 8.700 погинулих.

OUTPUT
Bugari su imali 7.000 poginulih 6.000 zarobljenih vojnika i 130 topova a Grci su imali 8.700 poginulih.
```

### Cyrillic etymological output (`type=5`, `flav=2`)

#### Slovak (`slk`)

```text
INPUT
Pridať do obľúbenýchProdukt si uložíte medzi vami zvolené obľúbené položky a môžete si ho tak rýchlo nájsť Drevený vozík na kolieskach obsahuje 24 farebných drevených kociek v rôznych tvaroch.

OUTPUT
Придать до обльубеныхпродукт си уложите медзи вами зволене обльубене положкы а можете си хо так рыхло најсть Древены возик на колиесках обсахује 24 фаребных древеных коциек в розных тварох.
```

#### Polish (`pol`)

```text
INPUT
Pytałem o Olsztyn, a oni wypowiadali się o mieście w samych superlatywach, twierdząc m.in., że szczególnie latem jest tu pięknie.

OUTPUT
Пыталем о Олштын, а они выповиадали сиѧ о миесьцие в самых суперлатывах, твиердзѫц м.ин., же шчеголние латем јест ту пиѧкние.
```

#### Czech (`ces`)

```text
INPUT
Trojice slonů afrických v Zoo Dvůr Králové nad Labem na Trutnovsku si dnes pochutnala na vánoční nadílce, v níž bylo i několik vánočních stromků.

OUTPUT
Тројице слону африцкых в Зоо Двур Кралове над Лабем на Трутновску си днес похутнала на ваночни надилце, в ниж было и нѣколик ваночних стромку.
```

### Latin standard output (`type=1`, `flav=3`)

#### Polish (`pol`)

```text
INPUT
Pytałem o Olsztyn, a oni wypowiadali się o mieście w samych superlatywach, twierdząc m.in., że szczególnie latem jest tu pięknie.

OUTPUT
Pytalem o Olštyn, a oni vypoviadali sie o miescie v samyh superlatyvah, tvierdzuc m.in., že ščegolnie latem jest tu pieknie.
```

### ASCII-oriented standard output (`type=2`, `flav=3`)

#### Czech (`ces`)

```text
INPUT
Trojice slonů afrických v Zoo Dvůr Králové nad Labem na Trutnovsku si dnes pochutnala na vánoční nadílce, v níž bylo i několik vánočních stromků.

OUTPUT
Trojice slonu africkyh v Zoo Dvur Kralove nad Labem na Trutnovsku si dnes pohutnala na vanoczni nadilce, v nizs bylo i njekolik vanocznih stromku.
```

### Cyrillic standard output (`type=5`, `flav=3`)

#### Belarusian (`bel`)

```text
INPUT
Той жа Юры Чыж рабіў свой бізнэс па «паняцьцях», дзякуючы блізкасьці да «цела» меў прывілеі, атрымліваў вольны доступ у прыбытковыя сфэры, куды пускалі толькі абраных, напрыклад, у гандаль нафтапрадуктамі.

OUTPUT
Тој жа Јуры Чиж рабив свој бизнес па «пањацјцјах», дзакујучи близкасци да «цела» мев прывилеи, атрымливав волны доступ у прыбытковыја сферы, куды пускали толки абраных, напрыклад, у гандал нафтапрадуктами.
```

#### Russian (`rus`)

```text
INPUT
В боях за Дубно 14 февраля 1944 года молодой фотокорреспондент находился в боевых порядках 29-го гвардейского кавалерийского полка, оказавшегося в эпицентре сражения за город.

OUTPUT
В бојах за Дубно 14 февраља 1944 года молодој фотокорреспондент находилса в боевых порадках 29-го гвардејского кавалеријского полка, оказавшегоса в епицентре сраженија за город.
```

#### Ukrainian (`ukr`)

```text
INPUT
"Улаштовуватимуть водопровідний вузол на водоводі діаметром 900 мм на розі вул. Зеленогірської та вул. Петра Радченка", — йдеться у повідомленні.

OUTPUT
"Улаштовуватимут водопровидниј вузол на водоводи диаметром 900 мм на рози вул. Зеленогирскоји та вул. Петра Радченка", — јдетса у повидомленни.
```

#### Rusyn (`rue`)

```text
INPUT
Ключі організовано за дихотомічною системою і представлено у вигляді основного ключа до родин і 10 ключів до родів і видів кожної родини (кротові, мідицеві, вовчкові, мишівкові, тушканові, сліпакові, мишачі, хом'якові, норицеві).

OUTPUT
Кључи организовано за дихотомичноју системоју и представлено у вигљади основного кључа до родин и 10 кључив до родив и видив кожноји родини (кротови, мидицеви, вовчкови, мишивкови, тушканови, слипакови, мишачи, хом'јакови, норицеви).
```

#### Slovak (`slk`)

```text
INPUT
Pridať do obľúbenýchProdukt si uložíte medzi vami zvolené obľúbené položky a môžete si ho tak rýchlo nájsť Drevený vozík na kolieskach obsahuje 24 farebných drevených kociek v rôznych tvaroch.

OUTPUT
Придат до облубеныхпродукт си уложите медзи вами зволене облубене положкы а можете си хо так рыхло најст Древены возик на колиесках обсахује 24 фаребных древеных коциек в розных тварох.
```

## Choosing a flavor

Use `flav=2` when retaining soft consonants and etymological distinctions is
more important than minimizing the character inventory.

Use `flav=3` for a more neutral and compact common orthography. For example,
some soft-sign distinctions retained by `flav=2` are removed by `flav=3`.

Use `flav=4` when maximum simplification is preferred and the additional loss
of orthographic distinctions is acceptable.

## Input processing

This focused port always uses the upstream loose-input preprocessing path. It:

1. normalizes input to Unicode NFC;
2. processes continuous sequences of Unicode letters and combining marks;
3. converts recognized Latin and Cyrillic variants to an internal common form;
4. applies the selected flavor profile;
5. renders the selected output type; and
6. restores lowercase, title-style, or uppercase word casing.

Punctuation, whitespace, digits, and other non-letter characters are preserved.

## Scope and limitations

PyUniSlavic provides a common orthographic representation, not a normative
spelling standard for every living Slavic language.

The loose-input rules were designed primarily to accept varied Interslavic
input. Their broad character coverage is useful for other Slavic languages,
but the normalizer is not a complete language-specific phonological analyzer.
For example:

- Polish combinations such as `rz` or `dzi` are not always interpreted as a
  single language-specific sound.
- Some Ukrainian distinctions, including certain uses of `і/и` and `г/ґ`, can
  be merged by normalization.
- Etymological and standard profiles intentionally merge different sets of
  letters, so conversion is not guaranteed to be reversible.
- Mixed or camel-case capitalization is reduced to lowercase, title-style, or
  uppercase at the word level, matching the upstream behavior.

Applications that require exact source-language phonology should add a
language-specific input adapter before calling `transliterate`.

## Compatibility testing

The initial Python port was checked against the upstream TypeScript behavior
for all nine supported parameter combinations. The parity suite included 144
targeted cases and 4,500 deterministic randomized Unicode cases, with identical
outputs for the tested inputs.

## Upstream project and attribution

The core transliteration and loose-normalization rules are adapted from:

- [`medzuslovjansky/js-utils`](https://github.com/medzuslovjansky/js-utils)
- [`src/transliterate/transliterate.ts`](https://github.com/medzuslovjansky/js-utils/blob/main/src/transliterate/transliterate.ts)
- [Interslavic](https://interslavic.fun/)

The upstream project describes `@interslavic/utils` as a collection of
JavaScript utilities for the Interslavic language, including transliteration.

The upstream source is distributed under the MIT License:

> Copyright (c) 2021 Medžuslovjansky (Меджусловјанскы)

Any distribution of this derived port must include the upstream copyright and
MIT permission notice. See the
[`@interslavic/utils` license](https://github.com/medzuslovjansky/js-utils/blob/main/LICENSE)
for the complete text.

## Project status

PyUniSlavic currently implements only the nine combinations documented above.
Other upstream output modes and flavor profiles are intentionally outside the
scope of this focused port.
