yanagi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7d9c51329617c5af3c378c075b472bc750348149a804068ebe4fc83b70b43d43
4
+ data.tar.gz: a87fbe968fb608139db4c7cf4d59637fcb52d7e056f52b56d4ac4304798d34d2
5
+ SHA512:
6
+ metadata.gz: 56b3ba1f958741308ad2ca52ac55a9965df0df4e101cd8f7af2e1725df4215f2f78081bc56608e7343593126eacd176e5f2b20515639802fd0123460217cf11f
7
+ data.tar.gz: 8b21ac53b568ab9a50fc71df6519cab2fcafbd945e4f40406d644b6632b1c99c10e779badbab953260bb16268e7c7eaad91641c4f1d6687764560047b0918408
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-08-29
9
+
10
+ First release. Yanagi (柳) turns the Japanese→Ukrainian transliteration policy
11
+ from prose into executable rules, so a violation fails a build instead of
12
+ reaching print.
13
+
14
+ ### Added
15
+
16
+ - **Mode A — generation.** `Yanagi.cyrillic` and `Yanagi.romaji` render kana
17
+ (or katakana, or a kanji + reading pair) into Ukrainian Cyrillic and Modified
18
+ Hepburn. Both derive from a single mora tokenizer, so they cannot disagree
19
+ about mora boundaries.
20
+ - **Mode B — audit.** `Yanagi::Audit` scans existing Ukrainian prose in three
21
+ tiers. Only tokens anchored to a known Japanese lexical item are ever
22
+ rewritten; pattern matches merely rank what a human reviews. Findings are
23
+ reported by default and applied only from an approved findings file.
24
+ - **`Yanagi::DocSync`.** Parses the authoritative policy document and asserts it
25
+ agrees with the rules data, so the doc and the engine cannot drift.
26
+ - **CLI** (`yanagi`): `romaji`, `cyrillic`, `audit`, `apply`, `doc-sync`,
27
+ `lexicon`, `verify-gold`.
28
+ - **Rules as data** in `data/`: the mora table with its forbidden variants,
29
+ combinatorial rules, exonyms, the lexicon, a native-Ukrainian allowlist, and
30
+ an exceptions table.
31
+
32
+ ### Policy encoded
33
+
34
+ - Mora table rejecting Polivanov: ші, чі, цу, фу, джі, дз, ґ.
35
+ - Palatalised syllables (yōon), including the soft sign before о: шьо, чьо, джьо.
36
+ - い after a vowel renders as «і», never «ї».
37
+ - ん renders as «м» before п and б, «н» elsewhere.
38
+ - Long vowels are not doubled.
39
+ - Sokuon っ doubles the following consonant before the plosives п and к, and is
40
+ not rendered before sibilants and affricates.
41
+ - Established Ukrainian exonyms take precedence over derivation.
42
+
43
+ ### Verification
44
+
45
+ - 377/377 hand-verified glossary pairs pass, with an empty exceptions table.
46
+ - Romaji output is byte-identical across 1496 dictionary readings, confirming
47
+ the engine was extracted from its predecessor without behaviour change.
48
+ - Zero runtime dependencies; stdlib only.
49
+
50
+ [0.1.0]: https://github.com/shogi-dojo/yanagi/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 shogi-dojo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Yanagi (柳)
2
+
3
+ Deterministic Japanese → Ukrainian transliteration and policy enforcement gem.
4
+
5
+ 柳 (*yanagi* = willow / верба) — a symbol of Ukraine and a tree equally at home in Japanese poetry.
6
+
7
+ ## Features
8
+ - **Zero runtime dependencies** — standard library only (`yaml`, `json`, `set`, `unicode_normalize`).
9
+ - **Mode A (Generation)**: Deterministic kana and romaji to Ukrainian Cyrillic rendering.
10
+ - **Mode B (Audit)**: Lexicon-anchored prose audit and policy violation detector:
11
+ - **Tier 1**: AUTOFIX-eligible (matches lexicon entry via forbidden Polivanov/anglicism substitution).
12
+ - **Tier 2**: High-confidence report for unknown Japanese terms (contains JP markers, unanchored in lexicon/allowlist).
13
+ - **Tier 3**: Polivanov markers check (off by default).
14
+ - **Policy Synchronization (`DocSync`)**: Asserts agreement between markdown policy documents (`shared/transliteration.md`) and executable YAML rules.
15
+
16
+ ## Corpus data
17
+
18
+ The gem ships the **policy rules** only: the mora table, combinatorial rules,
19
+ exonyms and the exceptions table.
20
+
21
+ Mode B additionally needs two files generated from a specific translation
22
+ project — a lexicon of known Japanese terms and an allowlist of native
23
+ Ukrainian vocabulary. These are **not distributed**, because they are one
24
+ project's editorial research rather than transliteration policy.
25
+
26
+ Mode A (`cyrillic`, `romaji`) needs neither and works out of the box. Without
27
+ them Mode B simply reports nothing, since it only ever rewrites tokens anchored
28
+ to a known lexical item.
29
+
30
+ To use Mode B, generate the files and point the gem at them:
31
+
32
+ ```bash
33
+ export YANAGI_DATA_DIR=/path/to/your/data
34
+ yanagi lexicon build --glossary /path/to/glossary.org
35
+ ```
36
+
37
+
38
+ ## Installation
39
+
40
+ Add this line to your application's Gemfile:
41
+
42
+ ```ruby
43
+ gem 'yanagi'
44
+ ```
45
+
46
+ Or reference via git repository:
47
+
48
+ ```ruby
49
+ gem 'yanagi', git: 'https://github.com/shogi-dojo/yanagi.git', tag: 'v0.1.0'
50
+ ```
51
+
52
+ ## Ruby API Usage
53
+
54
+ ### Mode A: Transliteration
55
+
56
+ ```ruby
57
+ require 'yanagi'
58
+
59
+ # Kana to Cyrillic
60
+ Yanagi.cyrillic("しゅうさい")
61
+ # => #<Yanagi::Result text="шюсай" source=:derived confidence=1.0>
62
+
63
+ Yanagi.cyrillic("シュウサイ")
64
+ # => #<Yanagi::Result text="шюсай" source=:derived confidence=1.0>
65
+
66
+ # Exonyms
67
+ Yanagi.cyrillic("とうきょう")
68
+ # => #<Yanagi::Result text="Токіо" source=:exonym confidence=1.0>
69
+
70
+ # Kana to Modified Hepburn Romaji
71
+ Yanagi.romaji("しゅうさい")
72
+ # => "shuusai"
73
+
74
+ # From Romaji to Cyrillic
75
+ Yanagi.from_romaji("shuusai")
76
+ # => "шюсай"
77
+ ```
78
+
79
+ ### Mode B: Prose Audit & Correction
80
+
81
+ ```ruby
82
+ # Scan files or directories
83
+ findings = Yanagi.audit(["path/to/chapter.org"], tier2: true, tier3: false)
84
+
85
+ # Apply approved findings
86
+ Yanagi::Audit.apply("audit/findings.yml")
87
+ ```
88
+
89
+ ### Policy Agreement Check
90
+
91
+ ```ruby
92
+ sync = Yanagi.doc_sync(path: "shared/transliteration.md")
93
+ sync.synced? # => true
94
+ sync.diff # => []
95
+ ```
96
+
97
+ ## CLI Usage
98
+
99
+ ```bash
100
+ # Romaji & Cyrillic generation
101
+ yanagi romaji しゅうさい
102
+ yanagi cyrillic しゅうさい
103
+ yanagi cyrillic しゅうさい --format json
104
+
105
+ # Prose auditing
106
+ yanagi audit books/meijin/translation/ua --format human
107
+ yanagi audit books/meijin/translation/ua --out audit/findings.yml
108
+
109
+ # Applying approved findings
110
+ yanagi apply audit/findings.yml
111
+
112
+ # Document synchronization & gold verification
113
+ yanagi doc-sync shared/transliteration.md
114
+ yanagi verify-gold
115
+ ```
116
+
117
+ ## License
118
+
119
+ MIT License. Copyright (c) 2026 shogi-dojo.
@@ -0,0 +1,59 @@
1
+ # Combinatorial and contextual transliteration rules
2
+ ---
3
+ sokuon:
4
+ rule: "っ (sokuon) doubles the following consonant before the plosives «п» and «к»; before sibilants and affricates it is not rendered"
5
+ geminating: ["п", "к"]
6
+ examples:
7
+ - { kana: "いっぺき", rendered: "іппекі" }
8
+ - { kana: "けっぺき", rendered: "кеппекі" }
9
+ - { kana: "ごじゅっぽ", rendered: "ґоджюппо" }
10
+ - { kana: "にっかい", rendered: "Ніккай" }
11
+ - { kana: "ほっけきょう", rendered: "Хоккекьо" }
12
+ - { kana: "がっかい", rendered: "Ґаккай" }
13
+ - { kana: "しっちゃく", rendered: "шічяку" }
14
+ - { kana: "てっしゅう", rendered: "Тешю" }
15
+ - { kana: "どうじんざっし", rendered: "доджін-дзаші" }
16
+
17
+ moraic_n:
18
+ rule: "ん before labials (p, b) renders as 'м', and as 'н' elsewhere (including before m)"
19
+ labials: ["п", "б"]
20
+ default: "н"
21
+ examples:
22
+ - { kana: "もんぷく", rendered: "момпуку" }
23
+ - { kana: "さんぼう", rendered: "самбо" }
24
+ - { kana: "しんぶん", rendered: "шімбун" }
25
+ - { kana: "さんまいきょう", rendered: "санмайкьо" }
26
+ - { kana: "らんま", rendered: "ранма" }
27
+ - { kana: "しんしゅう", rendered: "шіншю" }
28
+
29
+ i_after_vowel:
30
+ rule: "い (i) after any vowel within a Japanese stem renders as 'і', never 'ї'"
31
+ target: "і"
32
+ forbidden: "ї"
33
+ examples:
34
+ - { kana: "かるいざわ", rendered: "каруідзава" }
35
+ - { kana: "ごい", rendered: "ґоі" }
36
+ - { kana: "きいん", rendered: "кіін" }
37
+ - { kana: "ぬいもん", rendered: "нуімон" }
38
+ - { kana: "おてあい", rendered: "отеай" }
39
+
40
+ apostrophe_i:
41
+ rule: "After apostrophe in Japanese names/words, write 'і', not 'ї'"
42
+ target: "і"
43
+ forbidden: "ї"
44
+ examples:
45
+ - { romaji: "Jun'ichi", rendered: "Джюн'ічі" }
46
+ - { romaji: "Kan'ichi", rendered: "Кан'ічі" }
47
+
48
+ long_vowels:
49
+ rule: "Never double 'о'; double 'і' where morae genuinely collide; collapse long vowels always"
50
+ collapse: true
51
+ preserve_adjacent_i: true
52
+ examples:
53
+ - { kana: "おおさか", rendered: "осака" }
54
+ - { kana: "おおたけ", rendered: "отаке" }
55
+ - { kana: "おおしま", rendered: "ошіма" }
56
+ - { kana: "しゅうさい", rendered: "шюсай" }
57
+ - { kana: "きいん", rendered: "кіін" }
58
+ - { kana: "いいだ", rendered: "ііда" }
59
+ - { kana: "りいち", rendered: "ріічі" }
@@ -0,0 +1,15 @@
1
+ # Exceptions table for underivable pairs and documented defects.
2
+ #
3
+ # status:
4
+ # accepted - a genuine, permanent exception to the regular rules
5
+ # pending - a data defect awaiting correction; FAILS `yanagi verify-gold`
6
+ # wontfix - an intentional divergence, deliberately not corrected
7
+ #
8
+ # A `pending` entry is a build failure by design, so unresolved defects stay
9
+ # visible instead of silently accumulating. Correct the source data, or
10
+ # reclassify the entry with a written reason.
11
+ #
12
+ # The 12 original entries (8 long-vowel `оо` violations plus 4 yōon/digraph
13
+ # defects) were corrected in books/meijin/glossary.org rather than excepted.
14
+ ---
15
+ []
data/data/exonyms.yml ADDED
@@ -0,0 +1,14 @@
1
+ # Established Ukrainian exonyms (pre-empt regular phonetic transliteration)
2
+ ---
3
+ とうきょう:
4
+ romaji: "tokyo"
5
+ cyrillic: "Токіо"
6
+ note: "Standard Ukrainian geographical exonym"
7
+ きょうと:
8
+ romaji: "kyoto"
9
+ cyrillic: "Кіото"
10
+ note: "Standard Ukrainian geographical exonym (pre-empts きょ -> кьо rule)"
11
+ おおさか:
12
+ romaji: "osaka"
13
+ cyrillic: "Осака"
14
+ note: "Standard Ukrainian geographical exonym"