translit.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package api
  2. import (
  3. "bytes"
  4. )
  5. // ////////////////////////////////////////////////////////////////////////////////// //
  6. type specProc func(p, c, n rune, extMap map[string]string) (string, bool)
  7. // ////////////////////////////////////////////////////////////////////////////////// //
  8. var baseRuEn = map[string]string{
  9. "а": "a", "А": "A", "Б": "B", "б": "b", "В": "V", "в": "v", "Г": "G", "г": "g",
  10. "Д": "D", "д": "d", "З": "Z", "з": "z", "И": "I", "и": "i", "К": "K", "к": "k",
  11. "Л": "L", "л": "l", "М": "M", "м": "m", "Н": "N", "н": "n", "О": "O", "о": "o",
  12. "П": "P", "п": "p", "Р": "R", "р": "r", "С": "S", "с": "s", "Т": "T", "т": "t",
  13. "У": "U", "у": "u", "Ф": "F", "ф": "f",
  14. }
  15. var scientificRuEn = map[string]string{
  16. "Е": "E", "е": "e", "Ё": "Ë", "ё": "ë", "Ж": "Ž", "ж": "ž", "Й": "J", "й": "j",
  17. "Х": "Ch", "х": "ch", "Ц": "C", "ц": "c", "Ч": "Č", "ч": "č", "Ш": "Š", "ш": "š",
  18. "Щ": "Šč", "щ": "šč", "Ъ": "″", "ъ": "″", "Ы": "Y", "ы": "y", "Ь": "′", "ь": "′",
  19. "Э": "È", "э": "è", "Ю": "Ju", "ю": "ju", "Я": "Ja", "я": "ja",
  20. }
  21. var iso9ARuEn = map[string]string{
  22. "Е": "E", "е": "e", "Ё": "Ë", "ё": "ë", "Ж": "Ž", "ж": "ž", "Й": "J", "й": "j",
  23. "Х": "H", "х": "h", "Ц": "C", "ц": "c", "Ч": "Č", "ч": "č", "Ш": "Š", "ш": "š",
  24. "Щ": "Ŝ", "щ": "ŝ", "Ъ": "″", "ъ": "″", "Ы": "Y", "ы": "y", "Ь": "′", "ь": "′",
  25. "Э": "È", "э": "è", "Ю": "Û", "ю": "û", "Я": "Â", "я": "â",
  26. }
  27. var iso9BRuEn = map[string]string{
  28. "Е": "E", "е": "e", "Ё": "Yo", "ё": "yo", "Ж": "Zh", "ж": "zh", "Й": "J", "й": "j",
  29. "Х": "X", "х": "x", "Ч": "Ch", "ч": "ch", "Ш": "Sh", "ш": "sh", "Щ": "Shh",
  30. "щ": "shh", "Ъ": "``", "ъ": "``", "Ы": "Y`", "ы": "y`", "Ь": "`", "ь": "`",
  31. "Э": "E`", "э": "e`", "Ю": "Yu", "ю": "yu", "Я": "Ya", "я": "ya",
  32. }
  33. var isoR91RuEn = map[string]string{
  34. "Е": "E", "е": "e", "Ё": "Ë", "ё": "ë", "Ж": "Ž", "ж": "ž", "Й": "J", "й": "j",
  35. "Х": "H", "х": "h", "Ц": "C", "ц": "c", "Ч": "Č", "ч": "č", "Ш": "Š", "ш": "š",
  36. "Щ": "Ŝ", "щ": "ŝ", "Ъ": "″", "ъ": "″", "Ы": "Y", "ы": "y", "Ь": "′", "ь": "′",
  37. "Э": "È", "э": "è", "Ю": "Û", "ю": "û", "Я": "Â", "я": "â",
  38. }
  39. var isoR92RuEn = map[string]string{
  40. "Е": "E", "е": "e", "Ё": "Jo", "ё": "jo", "Ж": "Zh", "ж": "zh", "Й": "Jj", "й": "jj",
  41. "Х": "Kh", "х": "kh", "Ц": "C", "ц": "c", "Ч": "Ch", "ч": "ch", "Ш": "Sh", "ш": "sh",
  42. "Щ": "Shh", "щ": "shh", "Ъ": "″", "ъ": "″", "Ы": "Y", "ы": "y", "Ь": "′", "ь": "′",
  43. "Э": "Eh", "э": "eh", "Ю": "Ju", "ю": "ju", "Я": "Ja", "я": "ja",
  44. }
  45. var bgnRuEn = map[string]string{
  46. "Ж": "Zh", "ж": "zh", "И": "I", "и": "i", "Й": "Y", "й": "y", "Х": "Kh", "х": "kh",
  47. "Ц": "Ts", "ц": "ts", "Ч": "Ch", "ч": "ch", "Ш": "Sh", "ш": "sh", "Щ": "Shch",
  48. "щ": "shch", "Ъ": "″", "ъ": "″", "Ы": "Y", "ы": "y", "Ь": "′", "ь": "′", "Э": "E",
  49. "э": "e", "Ю": "Yu", "ю": "yu", "Я": "Ya", "я": "ya",
  50. }
  51. var bsRuEn = map[string]string{
  52. "Е": "e", "е": "e", "Ё": "ë", "ё": "ë", "Ж": "zh", "ж": "zh", "Й": "ĭ", "й": "ĭ",
  53. "Х": "kh", "х": "kh", "Ц": "ts", "ц": "ts", "Ч": "ch", "ч": "ch", "Ш": "sh",
  54. "ш": "sh", "Щ": "shch", "щ": "shch", "Ъ": "″", "ъ": "″", "Ы": "ȳ", "ы": "ȳ",
  55. "Ь": "′", "ь": "′", "Э": "é", "э": "é", "Ю": "yu", "ю": "yu", "Я": "ya", "я": "ya",
  56. }
  57. var alalcRuEn = map[string]string{
  58. "Е": "E", "е": "e", "Ё": "Ë", "ё": "ë", "Ж": "Zh", "ж": "zh", "Й": "Ĭ", "й": "ĭ",
  59. "Х": "Kh", "х": "kh", "Ц": "T͡s", "ц": "t͡s", "Ч": "Ch", "ч": "ch", "Ш": "Sh",
  60. "ш": "sh", "Щ": "Shch", "щ": "shch", "Ъ": "″", "ъ": "″", "Ы": "Y", "ы": "y",
  61. "Ь": "′", "ь": "′", "Э": "Ė", "э": "ė", "Ю": "I͡u", "ю": "i͡u", "Я": "I͡a", "я": "i͡a",
  62. }
  63. var icaoRuEn = map[string]string{
  64. "Е": "E", "е": "e", "Ё": "E", "ё": "e", "Ж": "Zh", "ж": "zh", "Й": "I", "й": "i",
  65. "Х": "Kh", "х": "kh", "Ц": "Ts", "ц": "ts", "Ч": "Ch", "ч": "ch", "Ш": "Sh",
  66. "ш": "sh", "Щ": "Shch", "щ": "shch", "Ъ": "Ie", "ъ": "ie", "Ы": "Y", "ы": "y",
  67. "Ь": "", "ь": "", "Э": "E", "э": "e", "Ю": "Iu", "ю": "iu", "Я": "Ia", "я": "ia",
  68. }
  69. var iso9BSpeical = map[string]string{
  70. "ц": "cz", "Ц": "Cz", "ц+": "с", "Ц+": "С",
  71. }
  72. var bgnSpecial = map[string]string{
  73. "е": "e", "Е": "E", "ё": "ë", "Ё": "Ë",
  74. "е+": "ye", "Е+": "Ye", "ё+": "yë", "Ё+": "Yë",
  75. }
  76. // ////////////////////////////////////////////////////////////////////////////////// //
  77. // EncodeToScientific encodes text with scientific mappings
  78. func EncodeToScientific(text string) string {
  79. return encode(text, scientificRuEn, nil)
  80. }
  81. // EncodeToISO9A encodes text with ISO 9:1995/A ГОСТ 7.79-2000/A mappings
  82. func EncodeToISO9A(text string) string {
  83. return encode(text, iso9ARuEn, nil)
  84. }
  85. // EncodeToISO9B encodes text with ISO 9:1995/B ГОСТ 7.79-2000/Б mappings
  86. func EncodeToISO9B(text string) string {
  87. return encode(text, iso9BRuEn, iso9BSpec)
  88. }
  89. // EncodeToBGN encodes text with BGN mappings
  90. func EncodeToBGN(text string) string {
  91. return encode(text, bgnRuEn, bgnSpec)
  92. }
  93. // EncodeToPCGN encodes text with PCGN mappings
  94. func EncodeToPCGN(text string) string {
  95. return encode(text, bgnRuEn, bgnSpec)
  96. }
  97. // EncodeToALALC encodes text with ALA-LC mappings
  98. func EncodeToALALC(text string) string {
  99. return encode(text, alalcRuEn, nil)
  100. }
  101. // EncodeToBS encodes text with BS 2979:1958 mappings
  102. func EncodeToBS(text string) string {
  103. return encode(text, alalcRuEn, nil)
  104. }
  105. // EncodeToICAO encodes text with ICAO mappings
  106. func EncodeToICAO(text string) string {
  107. return encode(text, icaoRuEn, nil)
  108. }
  109. // ////////////////////////////////////////////////////////////////////////////////// //
  110. func encode(text string, extMap map[string]string, proc specProc) string {
  111. if text == "" {
  112. return ""
  113. }
  114. var input = bytes.NewBufferString(text)
  115. var output = bytes.NewBuffer(nil)
  116. // Previous, next letter for special processor
  117. var p, n rune
  118. var rr string
  119. var ok bool
  120. for {
  121. r, _, err := input.ReadRune()
  122. if err != nil {
  123. break
  124. }
  125. if !isRussianChar(r) {
  126. output.WriteRune(r)
  127. p = r
  128. continue
  129. }
  130. if proc != nil {
  131. n, _, _ = input.ReadRune()
  132. input.UnreadRune()
  133. rr, ok = proc(p, r, n, extMap)
  134. if ok {
  135. output.WriteString(rr)
  136. continue
  137. }
  138. }
  139. p = r
  140. rr, ok = baseRuEn[string(r)]
  141. if ok {
  142. output.WriteString(rr)
  143. continue
  144. }
  145. rr, ok = extMap[string(r)]
  146. if ok {
  147. output.WriteString(rr)
  148. }
  149. }
  150. return output.String()
  151. }
  152. func iso9BSpec(p, c, n rune, extMap map[string]string) (string, bool) {
  153. if c != 'ц' && c != 'Ц' {
  154. return "", false
  155. }
  156. rr, ok := baseRuEn[string(n)]
  157. if !ok {
  158. rr = extMap[string(n)]
  159. }
  160. switch rr {
  161. case "e", "i", "y", "j", "E", "I", "Y", "J":
  162. return iso9BSpeical[string(c)+"+"], true
  163. }
  164. return iso9BSpeical[string(c)], true
  165. }
  166. func bgnSpec(p, c, n rune, extMap map[string]string) (string, bool) {
  167. switch c {
  168. case 'е', 'Е', 'ё', 'Ё':
  169. // nop
  170. default:
  171. return "", false
  172. }
  173. switch p {
  174. case 0, ' ',
  175. 'а', 'у', 'о', 'ы', 'и', 'э', 'я', 'ю',
  176. 'А', 'У', 'О', 'Ы', 'И', 'Э', 'Я', 'Ю':
  177. return bgnSpecial[string(c)+"+"], true
  178. }
  179. return bgnSpecial[string(c)], true
  180. }
  181. func isRussianChar(r rune) bool {
  182. switch {
  183. case r >= 1040 && r <= 1103,
  184. r == 1105, r == 1025:
  185. return true
  186. }
  187. return false
  188. }