cyrillic 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a5904094fb9d492197e816b4dd0270aa4245e64fe3388b5a8177bef6deb01fd
4
- data.tar.gz: 2fd71ce5f7e37778beae85554c5bcfc6277b348278d931219efddbbae6175020
3
+ metadata.gz: 6ccb18a2a8380ecae221098c62b00e1d21654f99020a78a48d921763075adaac
4
+ data.tar.gz: d5ef041b472f740ab9461b67efbeb683b85120adf5a34d5d21d5fa10067a7d30
5
5
  SHA512:
6
- metadata.gz: ea05ab8fbb717eec3902faca17b7c2783fb8e5e2dd5feab4ef003f0d979d1e91dadc001cde2fdc06d86eb8beaaf503621b61df8b23137e47b798c61cf384eba0
7
- data.tar.gz: dfd8f00970bb557ed16b6d7db598365f6110ebc8815f9ab088ef164a392bc075d94c285859eb80a021d71645c27c76495454dd1f0f9f2688205f3ebc17731852
6
+ metadata.gz: 8e60b7cb7871b6b7bc0d787cf4520ef0a28dbd2ac63ddb117f1a2d44527191576037f6cf7d269b1e66a157489c56a89c32d5269cf0cd074fec68553427759e3f
7
+ data.tar.gz: 26d33008bf7af9bc70926dff63ce77d0d4cff9542cfd73dc3bd787aae2a0171a35617339684fcbb14401b94e1368f750968f9bbded55d95e9b66f72872de9b4a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-09-17
11
+
12
+ ### Added
13
+ - 5 new transliteration schemes:
14
+ - Serbian (`:serbian`) translating Vuk's Cyrillic to Gaj's Latin (`Lj`, `Nj`, `Dž`, `Đ`, `Ć`).
15
+ - GOST 7.79-2000 System B (`:gost779b`) with standard ASCII digraphs.
16
+ - BGN/PCGN 1947 geographic romanization (`:bgn_pcgn`).
17
+ - Bulgarian official Streamlined System (`:bulgarian`).
18
+ - Belarusian national / UN 2012 Łacinka (`:belarusian`).
19
+ - Reverse transliteration (detransliteration) via `Cyrillic.detransliterate` (and aliases `.det`, `.reverse_transliterate`) with reverse mapping tables and longest-token-first greedy matching.
20
+ - CLI flag `-r, --reverse` for reversing transliterated text back to Cyrillic.
21
+ - Comprehensive fixture datasets in `test/fixtures/` and automated test suite in `test/fixtures_test.rb` verifying Ukrainian, Serbian, Bulgarian, Belarusian, Mongolian, and standard international schemes.
22
+ - Comprehensive performance benchmark suite in `benchmark/run.rb` with `rake benchmark` task testing all schemes, detransliteration, and text scale using `benchmark-ips`.
23
+ - Automated YARD documentation generation and deployment to GitHub Pages via `.github/workflows/docs.yml` and `rake yard` task.
24
+ - Documentation link badge in `README.md` and `documentation_uri` gem metadata.
25
+ - CLI executable support and unit test coverage for all new schemes and detransliteration.
26
+
10
27
  ## [0.3.0] - 2026-09-17
11
28
 
12
29
  ### Added
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Cyrillic
2
2
 
3
3
  [![Ruby](https://github.com/rovetz/cyrillic/actions/workflows/ruby.yml/badge.svg)](https://github.com/rovetz/cyrillic/actions/workflows/ruby.yml)
4
+ [![Docs](https://img.shields.io/badge/docs-rubydoc.info-blue.svg)](https://rovetz.github.io/cyrillic/)
4
5
  [![Gem Version](https://badge.fury.io/rb/cyrillic.svg)](https://badge.fury.io/rb/cyrillic)
5
6
  [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
6
7
 
@@ -9,9 +10,14 @@
9
10
  ## Supported Schemes
10
11
 
11
12
  * `:cyrillic` (Default generic Cyrillic transliteration)
12
- * `:iso9` (ISO 9:1995 standard)
13
+ * `:iso9` (ISO 9:1995 international standard)
14
+ * `:serbian` (Serbian Vuk's Cyrillic to Gaj's Latin)
15
+ * `:gost779b` (GOST 7.79-2000 System B ASCII digraphs)
16
+ * `:bgn_pcgn` (BGN/PCGN 1947 geographic romanization)
17
+ * `:bulgarian` (Bulgarian official Streamlined System)
18
+ * `:belarusian` (Belarusian national / UN 2012 Łacinka)
13
19
  * `:ukrainian` (Ukrainian national standard)
14
- * `:ua_passport` (Ukrainian international passport transliteration standard)
20
+ * `:ua_passport` (Ukrainian international passport standard)
15
21
  * `:mongolian` (Mongolian Cyrillic MNS 5217:2012)
16
22
  * `:de` (German Duden/phonetic transliteration)
17
23
 
@@ -48,6 +54,26 @@ Cyrillic.t("Кириллица")
48
54
  Cyrillic.t("Не важно, как медленно ты продвигаешься", :iso9)
49
55
  # => "Ne važno, kak medlenno ty prodvigaešʹsâ"
50
56
 
57
+ # Serbian Cyrillic to Latin (Vukovica to Gajica)
58
+ Cyrillic.t("Љубљана", :serbian)
59
+ # => "Ljubljana"
60
+
61
+ # GOST 7.79-2000 System B (ASCII digraphs)
62
+ Cyrillic.t("Щука", :gost779b)
63
+ # => "Shhuka"
64
+
65
+ # BGN/PCGN 1947 geographic romanization
66
+ Cyrillic.t("Хабаровск", :bgn_pcgn)
67
+ # => "Khabarovsk"
68
+
69
+ # Bulgarian Streamlined System
70
+ Cyrillic.t("България", :bulgarian)
71
+ # => "Balgariya"
72
+
73
+ # Belarusian national / UN 2012 Łacinka
74
+ Cyrillic.t("Мінск", :belarusian)
75
+ # => "Minsk"
76
+
51
77
  # Ukrainian scheme
52
78
  Cyrillic.t("Київ", :ukrainian)
53
79
  # => "Kyiv"
@@ -63,6 +89,14 @@ Cyrillic.t("Монгол хэл", :mongolian)
63
89
  # German transliteration scheme
64
90
  Cyrillic.t("Чайковский", :de)
65
91
  # => "Tschajkowskij"
92
+
93
+ # Reverse transliteration (Detransliteration)
94
+ Cyrillic.detransliterate("Transliteraciâ", :iso9)
95
+ # => "Транслитерация"
96
+
97
+ # Short alias .det
98
+ Cyrillic.det("Ljubljana", :serbian)
99
+ # => "Љубљана"
66
100
  ```
67
101
 
68
102
  ### Command Line Interface (CLI)
@@ -85,6 +119,21 @@ $ cyrillic text.txt
85
119
  $ cyrillic -s iso9 "Транслитерация"
86
120
  Transliteraciâ
87
121
 
122
+ $ cyrillic -s serbian "Љубљана"
123
+ Ljubljana
124
+
125
+ $ cyrillic -s gost779b "Щука"
126
+ Shhuka
127
+
128
+ $ cyrillic -s bgn_pcgn "Хабаровск"
129
+ Khabarovsk
130
+
131
+ $ cyrillic -s bulgarian "България"
132
+ Balgariya
133
+
134
+ $ cyrillic -s belarusian "Мінск"
135
+ Minsk
136
+
88
137
  $ cyrillic -s ukrainian "Київ"
89
138
  Kyiv
90
139
 
@@ -93,11 +142,18 @@ Borshchahivka
93
142
 
94
143
  $ cyrillic -s de "Весна"
95
144
  Wesna
145
+
146
+ # Reverse transliteration (-r, --reverse)
147
+ $ cyrillic -r "Transliteraciâ"
148
+ Транслитерация
149
+
150
+ $ cyrillic -r -s serbian "Ljubljana"
151
+ Љубљана
96
152
  ```
97
153
 
98
154
  ## Development
99
155
 
100
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run tests and code style checks (RuboCop). You can also run `bin/console` for an interactive prompt that will allow you to experiment.
156
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run tests and code style checks (RuboCop). You can run performance benchmarks with `bundle exec rake benchmark`. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
101
157
 
102
158
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
103
159
 
data/Rakefile CHANGED
@@ -12,4 +12,18 @@ end
12
12
 
13
13
  RuboCop::RakeTask.new(:rubocop)
14
14
 
15
+ desc "Run performance benchmarks"
16
+ task :benchmark do
17
+ ruby "benchmark/run.rb"
18
+ end
19
+
20
+ begin
21
+ require "yard"
22
+ YARD::Rake::YardocTask.new do |t|
23
+ t.files = ["lib/**/*.rb", "-", "README.md", "CHANGELOG.md", "LICENSE.txt"]
24
+ end
25
+ rescue LoadError
26
+ # YARD not available
27
+ end
28
+
15
29
  task default: %i[test rubocop]
data/benchmark/run.rb ADDED
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark/ips"
4
+ require_relative "../lib/cyrillic"
5
+
6
+ SHORT_TEXT = "Не важно, как медленно ты продвигаешься, главное, что ты не останавливаешься."
7
+ PARAGRAPH_TEXT = [
8
+ "Весна пришла в город с первыми теплыми лучами солнца и ароматом цветущих садов.",
9
+ "На центральных улицах кипела жизнь: открывались уютные кафе, люди гуляли по набережной.",
10
+ "Каждый новый день приносил вдохновение, надежду и стремление к созиданию прекрасного будущего.",
11
+ "В библиотеках и университетах студенты усердно готовились к весенним экзаменам и научным конференциям."
12
+ ].join(" ")
13
+ LONG_TEXT = "#{PARAGRAPH_TEXT} " * 30
14
+
15
+ puts "=" * 60
16
+ puts "Cyrillic Gem Performance Benchmarks"
17
+ puts "Ruby: #{RUBY_DESCRIPTION}"
18
+ puts "=" * 60
19
+
20
+ puts "\n[1] Schemes Comparison (Paragraph Text, ~350 chars)"
21
+ Benchmark.ips do |x|
22
+ x.config(time: 1, warmup: 0.5)
23
+
24
+ %i[cyrillic iso9 serbian gost779b bgn_pcgn bulgarian belarusian mongolian ukrainian ua_passport de].each do |scheme|
25
+ x.report(scheme.to_s) do
26
+ Cyrillic.t(PARAGRAPH_TEXT, scheme)
27
+ end
28
+ end
29
+
30
+ x.compare!
31
+ end
32
+
33
+ puts "\n[2] Forward vs Reverse Transliteration (ISO 9 & Serbian)"
34
+ iso9_latin = Cyrillic.t(PARAGRAPH_TEXT, :iso9)
35
+ serbian_cyr = "Београд, Нови Сад, Ниш, Крагујевац, Суботица, Зрењанин, Панчево, Чачак, Краљево, Ваљево."
36
+ serbian_lat = Cyrillic.t(serbian_cyr, :serbian)
37
+
38
+ Benchmark.ips do |x|
39
+ x.config(time: 1, warmup: 0.5)
40
+
41
+ x.report("iso9 forward") { Cyrillic.t(PARAGRAPH_TEXT, :iso9) }
42
+ x.report("iso9 reverse") { Cyrillic.det(iso9_latin, :iso9) }
43
+ x.report("serbian fwd") { Cyrillic.t(serbian_cyr, :serbian) }
44
+ x.report("serbian rev") { Cyrillic.det(serbian_lat, :serbian) }
45
+
46
+ x.compare!
47
+ end
48
+
49
+ puts "\n[3] Scalability by Text Size (ISO 9: short, medium, long)"
50
+ Benchmark.ips do |x|
51
+ x.config(time: 1, warmup: 0.5)
52
+
53
+ x.report("short (~75 chars)") { Cyrillic.t(SHORT_TEXT, :iso9) }
54
+ x.report("medium (~350 chars)") { Cyrillic.t(PARAGRAPH_TEXT, :iso9) }
55
+ x.report("long (~10,500 chars)") { Cyrillic.t(LONG_TEXT, :iso9) }
56
+
57
+ x.compare!
58
+ end
data/cyrillic.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.metadata["source_code_uri"] = "https://github.com/rovetz/cyrillic"
21
21
  spec.metadata["changelog_uri"] = "https://github.com/rovetz/cyrillic/blob/main/CHANGELOG.md"
22
22
  spec.metadata["bug_tracker_uri"] = "https://github.com/rovetz/cyrillic/issues"
23
+ spec.metadata["documentation_uri"] = "https://rovetz.github.io/cyrillic/"
23
24
  spec.metadata["rubygems_mfa_required"] = "true"
24
25
 
25
26
  spec.files = Dir.chdir(__dir__) do
@@ -32,10 +33,13 @@ Gem::Specification.new do |spec|
32
33
  spec.executables = ["cyrillic"]
33
34
  spec.require_paths = ["lib"]
34
35
 
36
+ spec.add_development_dependency "benchmark"
37
+ spec.add_development_dependency "benchmark-ips"
35
38
  spec.add_development_dependency "irb"
36
39
  spec.add_development_dependency "minitest", ">= 5.25"
37
40
  spec.add_development_dependency "rake", ">= 13.2"
38
41
  spec.add_development_dependency "rdoc"
39
42
  spec.add_development_dependency "rubocop", ">= 1.64"
40
43
  spec.add_development_dependency "simplecov", ">= 0.22"
44
+ spec.add_development_dependency "yard", ">= 0.9.36"
41
45
  end
data/exe/cyrillic CHANGED
@@ -5,29 +5,33 @@ require "optparse"
5
5
  require "cyrillic"
6
6
 
7
7
  scheme = nil
8
+ reverse = false
8
9
 
9
10
  parser = OptionParser.new do |opts|
10
11
  opts.banner = "Usage: cyrillic [options] [text | file...]"
11
12
  opts.separator ""
12
- opts.separator "Transliterate Cyrillic script to Roman (Latin) script."
13
+ opts.separator "Transliterate Cyrillic script to Roman (Latin) script, or reverse with -r."
13
14
  opts.separator ""
14
15
  opts.separator "Examples:"
15
16
  opts.separator " cyrillic 'Кириллица'"
16
17
  opts.separator " echo 'Кириллица' | cyrillic"
17
- opts.separator " cyrillic text.txt"
18
- opts.separator " cyrillic -s iso9 'Транслитерация'"
19
- opts.separator " cyrillic -s ukrainian 'Київ'"
20
- opts.separator " cyrillic -s ua_passport 'Борщагівка'"
21
- opts.separator " cyrillic -s mongolian 'Монгол'"
22
- opts.separator " cyrillic -s de 'Весна'"
18
+ opts.separator " cyrillic -r 'Transliteraciâ'"
19
+ opts.separator " cyrillic -s serbian 'Љубљана'"
20
+ opts.separator " cyrillic -r -s serbian 'Ljubljana'"
21
+ opts.separator " cyrillic -r -s gost779b 'Shhuka'"
23
22
  opts.separator ""
24
23
  opts.separator "Options:"
25
24
 
26
- opts.on("-s", "--scheme SCHEME", %w[default cyrillic iso9 mongolian ukrainian ua_passport de],
27
- "Transliteration scheme (default, cyrillic, iso9, mongolian, ukrainian, ua_passport, de)") do |s|
25
+ schemes = %w[default cyrillic iso9 mongolian ukrainian ua_passport de serbian gost779b bgn_pcgn bulgarian belarusian]
26
+ opts.on("-s", "--scheme SCHEME", schemes,
27
+ "Transliteration scheme (#{schemes.join(', ')})") do |s|
28
28
  scheme = s == "default" ? nil : s.to_sym
29
29
  end
30
30
 
31
+ opts.on("-r", "--reverse", "Reverse transliteration (Latin to Cyrillic)") do
32
+ reverse = true
33
+ end
34
+
31
35
  opts.on("-v", "--version", "Show version") do
32
36
  puts "cyrillic #{Cyrillic::VERSION}"
33
37
  exit
@@ -41,8 +45,12 @@ end
41
45
 
42
46
  parser.parse!
43
47
 
44
- transliterate = lambda do |text|
45
- Cyrillic.transliterate(text, scheme || :cyrillic)
48
+ process = lambda do |text|
49
+ if reverse
50
+ Cyrillic.detransliterate(text, from: scheme || :iso9)
51
+ else
52
+ Cyrillic.transliterate(text, scheme || :cyrillic)
53
+ end
46
54
  end
47
55
 
48
56
  if ARGV.empty?
@@ -51,17 +59,17 @@ if ARGV.empty?
51
59
  exit 1
52
60
  else
53
61
  $stdin.each_line do |line|
54
- puts transliterate.call(line.chomp)
62
+ puts process.call(line.chomp)
55
63
  end
56
64
  end
57
65
  else
58
66
  ARGV.each do |arg|
59
67
  if File.file?(arg)
60
68
  File.foreach(arg) do |line|
61
- puts transliterate.call(line.chomp)
69
+ puts process.call(line.chomp)
62
70
  end
63
71
  else
64
- puts transliterate.call(arg)
72
+ puts process.call(arg)
65
73
  end
66
74
  end
67
75
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cyrillic
4
+ # Belarusian national / UN 2012 Łacinka transliteration.
5
+ module Belarusian
6
+ k = %w[
7
+ А Б В Г Д Е Ё Ж З І Й К Л М Н О П Р С Т У Ў Ф Х Ц Ч Ш Ы Ь Э Ю Я
8
+ а б в г д е ё ж з і й к л м н о п р с т у ў ф х ц ч ш ы ь э ю я
9
+ ].freeze
10
+
11
+ v = %w[
12
+ A B V H D E Jo Ž Z I J K L M N O P R S T U Ŭ F Ch C Č Š Y ' E Ju Ja
13
+ a b v h d e jo ž z i j k l m n o p r s t u ŭ f ch c č š y ' e ju ja
14
+ ].freeze
15
+
16
+ CHARACTER_TABLE = k.zip(v).to_h.freeze
17
+ REGEXP = Regexp.union(CHARACTER_TABLE.keys).freeze
18
+ end
19
+ end
20
+
21
+ Belarusian = Cyrillic::Belarusian unless defined?(Belarusian)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cyrillic
4
+ # BGN/PCGN 1947 geographic romanization system.
5
+ module BgnPcgn
6
+ k = %w[
7
+ А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я
8
+ а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я
9
+ ].freeze
10
+
11
+ v = %w[
12
+ A B V G D E Yo Zh Z I Y K L M N O P R S T U F Kh Ts Ch Sh Shch " Y ' E Yu Ya
13
+ a b v g d e yo zh z i y k l m n o p r s t u f kh ts ch sh shch " y ' e yu ya
14
+ ].freeze
15
+
16
+ CHARACTER_TABLE = k.zip(v).to_h.freeze
17
+ REGEXP = Regexp.union(CHARACTER_TABLE.keys).freeze
18
+ end
19
+ end
20
+
21
+ BgnPcgn = Cyrillic::BgnPcgn unless defined?(BgnPcgn)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cyrillic
4
+ # Bulgarian official Streamlined System (2006/2009) transliteration.
5
+ module Bulgarian
6
+ k = %w[
7
+ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ь Ю Я
8
+ а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ь ю я
9
+ ].freeze
10
+
11
+ v = %w[
12
+ A B V G D E Zh Z I Y K L M N O P R S T U F H Ts Ch Sh Sht A Y Yu Ya
13
+ a b v g d e zh z i y k l m n o p r s t u f h ts ch sh sht a y yu ya
14
+ ].freeze
15
+
16
+ CHARACTER_TABLE = k.zip(v).to_h.freeze
17
+ REGEXP = Regexp.union(CHARACTER_TABLE.keys).freeze
18
+ end
19
+ end
20
+
21
+ Bulgarian = Cyrillic::Bulgarian unless defined?(Bulgarian)
data/lib/cyrillic/de.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cyrillic
4
+ # German (Duden/phonetic) transliteration system.
4
5
  module De
5
6
  k = %w[А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я].freeze
6
7
  v = %w[A B W G D E Jo Sch S I J K L M N O P R S T U F Ch Z Tsch Sch Schtsch "" Y "" E Ju Ja a b w g d e jo sch s i j k l m n o p r s t u f ch z tsch sch schtsch "" y "" e ju ja].freeze
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cyrillic
4
+ # GOST 7.79-2000 System B (ISO 9 System B) ASCII digraphs transliteration.
5
+ module Gost779b
6
+ k = %w[
7
+ А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я
8
+ а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я
9
+ ].freeze
10
+
11
+ v = %w[
12
+ A B V G D E Yo Zh Z I J K L M N O P R S T U F X Cz Ch Sh Shh " Y ' E' Yu Ya
13
+ a b v g d e yo zh z i j k l m n o p r s t u f x cz ch sh shh " y ' e' yu ya
14
+ ].freeze
15
+
16
+ CHARACTER_TABLE = k.zip(v).to_h.freeze
17
+ REGEXP = Regexp.union(CHARACTER_TABLE.keys).freeze
18
+ end
19
+ end
20
+
21
+ Gost779b = Cyrillic::Gost779b unless defined?(Gost779b)
data/lib/cyrillic/iso9.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cyrillic
4
+ # ISO 9:1995 international standard transliteration.
4
5
  module Iso9
5
6
  CHARACTER_TABLE = {
6
7
  "А" => "A",
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cyrillic
4
+ # Mongolian Cyrillic standard (MNS 5217:2012) romanization.
4
5
  module Mongolian
5
6
  k = %w[А Б В Г Д Е Ё Ж З И Й К Л М Н О Ө П Р С Т У Ү Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ё ж з и й к л м н о ө п р с т у ү ф х ц ч ш щ ъ ы ь э ю я].freeze
6
7
  v = %w[A B V G D Ye Yo J Z I I K L M N O O P R S T U U F Kh Ts Ch Sh Sh I Y I E Yu Ya a b v g d ye yo j z i i k l m n o o p r s t u u f kh ts ch sh sh i y i e yu ya].freeze
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cyrillic
4
+ # Serbian Cyrillic to Gaj's Latin transliteration (Vukovica to Gajica).
5
+ module Serbian
6
+ k = %w[
7
+ А Б В Г Д Ђ Е Ж З И Ј К Л Љ М Н Њ О П Р С Т Ћ У Ф Х Ц Ч Џ Ш
8
+ а б в г д ђ е ж з и ј к л љ м н њ о п р с т ћ у ф х ц ч џ ш
9
+ ].freeze
10
+
11
+ v = %w[
12
+ A B V G D Đ E Ž Z I J K L Lj M N Nj O P R S T Ć U F H C Č Dž Š
13
+ a b v g d đ e ž z i j k l lj m n nj o p r s t ć u f h c č dž š
14
+ ].freeze
15
+
16
+ CHARACTER_TABLE = k.zip(v).to_h.freeze
17
+ REGEXP = Regexp.union(CHARACTER_TABLE.keys).freeze
18
+ end
19
+ end
20
+
21
+ Serbian = Cyrillic::Serbian unless defined?(Serbian)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cyrillic
4
+ # Ukrainian national transliteration system.
4
5
  module Ukrainian
5
6
  k = %w[А Б В Г Ґ Д Е Є Ж З И І Ї Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ь Ю Я а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ь ю я].freeze
6
7
  v = %w[A B V H G D E Ye Zh Z Y I Yi I K L M N O P R S T U F Kh Ts Ch Sh Shch ʹ Iu Ia a b v h g d e ye zh z y i yi i k l m n o p r s t u f kh ts ch sh shch ʹ iu ia].freeze
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cyrillic
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/cyrillic.rb CHANGED
@@ -6,6 +6,11 @@ require "cyrillic/mongolian"
6
6
  require "cyrillic/ukrainian"
7
7
  require "cyrillic/ua_passport"
8
8
  require "cyrillic/de"
9
+ require "cyrillic/serbian"
10
+ require "cyrillic/gost779b"
11
+ require "cyrillic/bgn_pcgn"
12
+ require "cyrillic/bulgarian"
13
+ require "cyrillic/belarusian"
9
14
 
10
15
  module Cyrillic
11
16
  k = %w[
@@ -27,14 +32,44 @@ module Cyrillic
27
32
  mongolian: [Mongolian::CHARACTER_TABLE, Mongolian::REGEXP].freeze,
28
33
  ukrainian: [Ukrainian::CHARACTER_TABLE, Ukrainian::REGEXP].freeze,
29
34
  ua_passport: :ua_passport,
30
- de: [De::CHARACTER_TABLE, De::REGEXP].freeze
35
+ de: [De::CHARACTER_TABLE, De::REGEXP].freeze,
36
+ serbian: [Serbian::CHARACTER_TABLE, Serbian::REGEXP].freeze,
37
+ gost779b: [Gost779b::CHARACTER_TABLE, Gost779b::REGEXP].freeze,
38
+ bgn_pcgn: [BgnPcgn::CHARACTER_TABLE, BgnPcgn::REGEXP].freeze,
39
+ bulgarian: [Bulgarian::CHARACTER_TABLE, Bulgarian::REGEXP].freeze,
40
+ belarusian: [Belarusian::CHARACTER_TABLE, Belarusian::REGEXP].freeze
41
+ }.freeze
42
+
43
+ def self.build_reverse_mapping(table, extra = {})
44
+ rev = table.reject { |_cyr, lat| lat.nil? || lat.empty? }.to_h { |cyr, lat| [lat, cyr] }
45
+ rev.merge!(extra)
46
+ sorted_keys = rev.keys.sort_by { |key| -key.length }
47
+ [rev.freeze, Regexp.union(sorted_keys).freeze].freeze
48
+ end
49
+
50
+ REVERSE_SCHEMES = {
51
+ iso9: build_reverse_mapping(Iso9::CHARACTER_TABLE),
52
+ serbian: build_reverse_mapping(
53
+ Serbian::CHARACTER_TABLE,
54
+ { "LJ" => "Љ", "NJ" => "Њ", "DŽ" => "Џ", "DZ" => "Џ", "dz" => "џ" }
55
+ ),
56
+ gost779b: build_reverse_mapping(Gost779b::CHARACTER_TABLE),
57
+ bgn_pcgn: build_reverse_mapping(BgnPcgn::CHARACTER_TABLE),
58
+ bulgarian: build_reverse_mapping(Bulgarian::CHARACTER_TABLE),
59
+ belarusian: build_reverse_mapping(Belarusian::CHARACTER_TABLE),
60
+ mongolian: build_reverse_mapping(Mongolian::CHARACTER_TABLE),
61
+ ukrainian: build_reverse_mapping(Ukrainian::CHARACTER_TABLE),
62
+ de: build_reverse_mapping(De::CHARACTER_TABLE),
63
+ cyrillic: build_reverse_mapping(CHARACTER_TABLE),
64
+ default: build_reverse_mapping(CHARACTER_TABLE)
31
65
  }.freeze
32
66
 
33
67
  class << self
34
68
  # Transliterates Cyrillic text into Roman (Latin) script.
35
69
  #
36
70
  # @param string [String, #to_s] The text to transliterate.
37
- # @param to [Symbol, String] The transliteration scheme (:cyrillic, :iso9, :mongolian, :ukrainian, :ua_passport, :de).
71
+ # @param to [Symbol, String] The transliteration scheme (:cyrillic, :iso9, :mongolian,
72
+ # :ukrainian, :ua_passport, :de, :serbian, :gost779b, :bgn_pcgn, :bulgarian, :belarusian).
38
73
  # @return [String] The transliterated Latin script string.
39
74
  # @raise [ArgumentError] If the given transliteration scheme is unknown.
40
75
  #
@@ -44,8 +79,8 @@ module Cyrillic
44
79
  # @example Short alias .t with ISO 9 scheme
45
80
  # Cyrillic.t("Кириллица", :iso9) #=> "Kirillica"
46
81
  #
47
- # @example Ukrainian Passport scheme
48
- # Cyrillic.t("Борщагівка", :ua_passport) #=> "Borshchahivka"
82
+ # @example Serbian Cyrillic to Latin (Vukovica to Gajica)
83
+ # Cyrillic.t("Љубљана", :serbian) #=> "Ljubljana"
49
84
  def transliterate(string = "", to = :cyrillic)
50
85
  return "" if string.nil? || string.empty?
51
86
 
@@ -62,5 +97,37 @@ module Cyrillic
62
97
  string.to_s.gsub(regexp, table)
63
98
  end
64
99
  alias t transliterate
100
+
101
+ # Detransliterates (reverses) Latin script text back into Cyrillic script.
102
+ #
103
+ # @param string [String, #to_s] The Latin script text to detransliterate.
104
+ # @param from [Symbol, String] The scheme to reverse (:iso9, :serbian, :gost779b, etc.).
105
+ # @return [String] The Cyrillic script string.
106
+ # @raise [ArgumentError] If the given scheme is unknown.
107
+ #
108
+ # @example Reverse ISO 9:1995 transliteration
109
+ # Cyrillic.detransliterate("Transliteraciâ", from: :iso9) #=> "Транслитерация"
110
+ #
111
+ # @example Reverse Serbian transliteration (Gajica to Vukovica)
112
+ # Cyrillic.det("Ljubljana", from: :serbian) #=> "Љубљана"
113
+ #
114
+ # @example Reverse GOST 7.79 System B
115
+ # Cyrillic.det("Shhuka", from: :gost779b) #=> "Щука"
116
+ def detransliterate(string = "", scheme = nil, from: nil)
117
+ return "" if string.nil? || string.empty?
118
+
119
+ chosen = from || scheme || :iso9
120
+ scheme_key = chosen.to_s.downcase.to_sym
121
+ scheme_data = REVERSE_SCHEMES[scheme_key]
122
+ unless scheme_data
123
+ available = (REVERSE_SCHEMES.keys - [:default]).map(&:inspect).join(", ")
124
+ raise ArgumentError, "Unknown detransliteration scheme: #{chosen.inspect}. Available schemes: #{available}"
125
+ end
126
+
127
+ table, regexp = scheme_data
128
+ string.to_s.gsub(regexp, table)
129
+ end
130
+ alias det detransliterate
131
+ alias reverse_transliterate detransliterate
65
132
  end
66
133
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cyrillic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rovetz
@@ -9,6 +9,34 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: benchmark
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: benchmark-ips
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
12
40
  - !ruby/object:Gem::Dependency
13
41
  name: irb
14
42
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +121,20 @@ dependencies:
93
121
  - - ">="
94
122
  - !ruby/object:Gem::Version
95
123
  version: '0.22'
124
+ - !ruby/object:Gem::Dependency
125
+ name: yard
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 0.9.36
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 0.9.36
96
138
  description: Converts Cyrillic text to the Roman (Latin) script
97
139
  email:
98
140
  - zakbox@gmail.com
@@ -107,12 +149,18 @@ files:
107
149
  - LICENSE.txt
108
150
  - README.md
109
151
  - Rakefile
152
+ - benchmark/run.rb
110
153
  - cyrillic.gemspec
111
154
  - exe/cyrillic
112
155
  - lib/cyrillic.rb
156
+ - lib/cyrillic/belarusian.rb
157
+ - lib/cyrillic/bgn_pcgn.rb
158
+ - lib/cyrillic/bulgarian.rb
113
159
  - lib/cyrillic/de.rb
160
+ - lib/cyrillic/gost779b.rb
114
161
  - lib/cyrillic/iso9.rb
115
162
  - lib/cyrillic/mongolian.rb
163
+ - lib/cyrillic/serbian.rb
116
164
  - lib/cyrillic/ua_passport.rb
117
165
  - lib/cyrillic/ukrainian.rb
118
166
  - lib/cyrillic/version.rb
@@ -123,6 +171,7 @@ metadata:
123
171
  source_code_uri: https://github.com/rovetz/cyrillic
124
172
  changelog_uri: https://github.com/rovetz/cyrillic/blob/main/CHANGELOG.md
125
173
  bug_tracker_uri: https://github.com/rovetz/cyrillic/issues
174
+ documentation_uri: https://rovetz.github.io/cyrillic/
126
175
  rubygems_mfa_required: 'true'
127
176
  rdoc_options: []
128
177
  require_paths: