diff --git a/README.md b/README.md index d119178..0a46331 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Convert numbers to words. * Turkish / Turkey [tr] 🇹🇷 * Portuguese / Portugal [pt-pt] 🇵🇹 * Polish / Poland [pl-pl] 🇵🇱 +* Russian / Russia [ru-ru] 🇷🇺 * Roman Numbers * Roman Numbers (with Unicode) Ⅷ diff --git a/ru-ru.go b/ru-ru.go new file mode 100644 index 0000000..f0fae70 --- /dev/null +++ b/ru-ru.go @@ -0,0 +1,177 @@ +package ntw + +import ( + "strings" +) + +func init() { + // register the language + Languages["ru-ru"] = Language{ + Name: "Russian", + Aliases: []string{"ru", "rus", "ru_RU", "russian"}, + Flag: "🇷🇺", + + IntegerToWords: IntegerToRuRu, + } +} + +func plural(n int, words []string) string { + index := 0 + + n %= 100 + + if n > 19 { + n %= 10 + } + + if n != 1 { + if n > 1 && n <= 4 { + index = 1 + } else { + index = 2 + } + } + + return words[index] +} + +// IntegerToRuRu converts an integer to Russian words +func IntegerToRuRu(input int) string { + var russianUnits = []string{ + "", + "один", + "два", + "три", + "четыре", + "пять", + "шесть", + "семь", + "восемь", + "девять", + } + var russianTeens = []string{ + "десять", + "одиннадцать", + "двенадцать", + "тринадцать", + "четырнадцать", + "пятнадцать", + "шестнадцать", + "семнадцать", + "восемнадцать", + "девятнадцать", + } + var russianTens = []string{ + "", + "десять", + "двадцать", + "тридцать", + "сорок", + "пятьдесят", + "шестьдесят", + "семьдесят", + "восемьдесят", + "девяносто", + } + var russianHundreds = []string{ + "", + "сто", + "двести", + "триста", + "четыреста", + "пятьсот", + "шестьсот", + "семьсот", + "восемьсот", + "девятьсот", + } + var russianMegas = [][]string{ + {"", "", ""}, + {"тысяча", "тысячи", "тысяч"}, // 10^3 + {"миллион", "миллиона", "миллионов"}, // 10^6 + {"миллиард", "миллиарда", "миллиардов"}, // 10^9 + {"триллион", "триллиона", "триллионов"}, // 10^12 + {"квадриллион", "квадриллиона", "квадриллионов"}, // 10^15 + {"квинтиллион", "квинтиллиона", "квинтиллионов"}, // 10^18 + {"секстиллион", "секстиллиона", "секстиллионов"}, // 10^21 + {"септиллион", "септиллиона", "септиллионов"}, // 10^34 + {"октиллион", "октиллиона", "октиллионов"}, // 10^27 + {"нониллион", "нониллиона", "нониллионов"}, // 10^30 + {"дециллион", "дециллиона", "дециллионов"}, // 10^33 + {"ундециллион", "ундециллиона", "ундециллионов"}, // 10^36 + } + + var words []string + + if input < 0 { + words = append(words, "минус") + input *= -1 + } + + // split integer in triplets + triplets := integerToTriplets(input) + + // zero is a special case + if len(triplets) == 0 { + return "нуль" + } + + // iterate over triplets + for idx := len(triplets) - 1; idx >= 0; idx-- { + triplet := triplets[idx] + + // nothing todo for empty triplet + if triplet == 0 { + continue + } + + // three-digits + hundreds := triplet / 100 % 10 + tens := triplet / 10 % 10 + units := triplet % 10 + + if hundreds > 0 { + words = append(words, russianHundreds[hundreds]) + } + + if tens > 0 || units > 0 { + switch tens { + case 0: + words = append(words, oneTwoUnitFix(units, idx, russianUnits)) + case 1: + words = append(words, russianTeens[units]) + break + default: + words = append(words, russianTens[tens]) + if units > 0 { + words = append(words, oneTwoUnitFix(units, idx, russianUnits)) + } + break + } + } + + // mega + if idx >= 1 && idx < len(russianMegas) { + mega := russianMegas[idx] + tens = tens * 10 + units + if len(mega) > 0 { + words = append(words, plural(tens, mega)) + } + } + } + + return strings.Join(words, " ") +} + +func oneTwoUnitFix(unit, idx int, arr []string) string { + if idx == 1 { + switch unit { + case 1: + return "одна" + case 2: + return "две" + } + } + + return arr[unit] +} diff --git a/ru-ru_test.go b/ru-ru_test.go new file mode 100644 index 0000000..a2c2872 --- /dev/null +++ b/ru-ru_test.go @@ -0,0 +1,83 @@ +package ntw + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestIntegerToRuRu(t *testing.T) { + Convey("Testing IntegerToRuRu()", t, FailureContinues, func() { + testCases := map[int]string{ + 0: "нуль", + 1: "один", + 9: "девять", + 10: "десять", + 11: "одиннадцать", + 19: "девятнадцать", + 20: "двадцать", + 21: "двадцать один", + 80: "восемьдесят", + 90: "девяносто", + 99: "девяносто девять", + 100: "сто", + 101: "сто один", + 111: "сто одиннадцать", + 120: "сто двадцать", + 121: "сто двадцать один", + 900: "девятьсот", + 909: "девятьсот девять", + 919: "девятьсот девятнадцать", + 990: "девятьсот девяносто", + 999: "девятьсот девяносто девять", + 1000: "одна тысяча", + 2000: "две тысячи", + 4000: "четыре тысячи", + 5000: "пять тысяч", + 11000: "одиннадцать тысяч", + 21000: "двадцать одна тысяча", + 999000: "девятьсот девяносто девять тысяч", + 999999: "девятьсот девяносто девять тысяч девятьсот девяносто девять", + 1000000: "один миллион", + 2000000: "два миллиона", + 4000000: "четыре миллиона", + 5000000: "пять миллионов", + 100100100: "сто миллионов сто тысяч сто", + 500500500: "пятьсот миллионов пятьсот тысяч пятьсот", + 606606606: "шестьсот шесть миллионов шестьсот шесть тысяч шестьсот шесть", + 999000000: "девятьсот девяносто девять миллионов", + 999000999: "девятьсот девяносто девять миллионов девятьсот девяносто девять", + 999999000: "девятьсот девяносто девять миллионов девятьсот девяносто девять тысяч", + 999999999: "девятьсот девяносто девять миллионов девятьсот девяносто девять тысяч девятьсот девяносто девять", + 1174315110: "один миллиард сто семьдесят четыре миллиона триста пятнадцать тысяч сто десять", + 1174315119: "один миллиард сто семьдесят четыре миллиона триста пятнадцать тысяч сто девятнадцать", + 15174315119: "пятнадцать миллиардов сто семьдесят четыре миллиона триста пятнадцать тысяч сто девятнадцать", + 35174315119: "тридцать пять миллиардов сто семьдесят четыре миллиона триста пятнадцать тысяч сто девятнадцать", + 935174315119: "девятьсот тридцать пять миллиардов сто семьдесят четыре миллиона триста пятнадцать тысяч сто девятнадцать", + 2935174315119: "два триллиона девятьсот тридцать пять миллиардов сто семьдесят четыре миллиона триста пятнадцать тысяч сто девятнадцать", + 3911760: "три миллиона девятьсот одиннадцать тысяч семьсот шестьдесят", + 27: "двадцать семь", + 95000001000: "девяносто пять миллиардов одна тысяча", + 57482: "пятьдесят семь тысяч четыреста восемьдесят два", + 5: "пять", + 16: "шестнадцать", + 30: "тридцать", + 53: "пятьдесят три", + 123: "сто двадцать три", + 204: "двести четыре", + 300: "триста", + 1400: "одна тысяча четыреста", + 83756: "восемьдесят три тысячи семьсот пятьдесят шесть", + 293111: "двести девяносто три тысячи сто одиннадцать", + 32001950: "тридцать два миллиона одна тысяча девятьсот пятьдесят", + 2018: "две тысячи восемнадцать", + 126682: "сто двадцать шесть тысяч шестьсот восемьдесят два", + } + for input, expectedOutput := range testCases { + So(IntegerToRuRu(input), ShouldEqual, expectedOutput) + } + + // testCases negative values + So(IntegerToRuRu(-1), ShouldEqual, "минус один") + }) +}