Skip to content

Commit

Permalink
Add support for Serbian (Latin) (#207)
Browse files Browse the repository at this point in the history
* Add support for Serbian language

* Fix the billion strings for short scale

Used the long scale name instead of the short one. Simplified the scale and the ones geneder definition.

* Adds Serbian to README
  • Loading branch information
fatkaratekid authored and erozqba committed Nov 10, 2018
1 parent 5c9bce1 commit 1ed09f5
Show file tree
Hide file tree
Showing 4 changed files with 460 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ Besides the numerical argument, there are two main optional arguments.
* ``pl`` (Polish)
* ``pt`` (Portuguese)
* ``pt_BR`` (Portuguese - Brazilian)
* ``sl`` (Slovene)
* ``sr`` (Serbian)
* ``ro`` (Romanian)
* ``ru`` (Russian)
* ``sl`` (Slovene)
Expand Down
2 changes: 2 additions & 0 deletions num2words/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from . import lang_NL
from . import lang_UK
from . import lang_SL
from . import lang_SR
from . import lang_TH

CONVERTER_CLASSES = {
Expand All @@ -71,6 +72,7 @@
'ro': lang_RO.Num2Word_RO(),
'ru': lang_RU.Num2Word_RU(),
'sl': lang_SL.Num2Word_SL(),
'sr': lang_SR.Num2Word_SR(),
'no': lang_NO.Num2Word_NO(),
'dk': lang_DK.Num2Word_DK(),
'pt': lang_PT.Num2Word_PT(),
Expand Down
215 changes: 215 additions & 0 deletions num2words/lang_SR.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.

# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals

from .base import Num2Word_Base
from .currency import parse_currency_parts, prefix_currency
from .utils import get_digits, splitbyx

ZERO = ('nula',)

ONES = {
1: ('jedan', 'jedna'),
2: ('dva', 'dve'),
3: ('tri', 'tri'),
4: ('četiri', 'četiri'),
5: ('pet', 'pet'),
6: ('šest', 'šest'),
7: ('sedam', 'sedam'),
8: ('osam', 'osam'),
9: ('devet', 'devet'),
}

TENS = {
0: ('deset',),
1: ('jedanaest',),
2: ('dvanaest',),
3: ('trinaest',),
4: ('četrnaest',),
5: ('petnaest',),
6: ('šesnaest',),
7: ('sedamnaest',),
8: ('osamnaest',),
9: ('devetnaest',),
}

TWENTIES = {
2: ('dvadeset',),
3: ('trideset',),
4: ('četrdeset',),
5: ('pedeset',),
6: ('šezdeset',),
7: ('sedamdeset',),
8: ('osamdeset',),
9: ('devedeset',),
}

HUNDREDS = {
1: ('sto',),
2: ('dvesta',),
3: ('trista',),
4: ('četristo',),
5: ('petsto',),
6: ('šesto',),
7: ('sedamsto',),
8: ('osamsto',),
9: ('devetsto',),
}

SCALE = {
0: ('', '', '', False),
1: ('hiljada', 'hiljade', 'hiljada', True), # 10^3
2: ('milion', 'miliona', 'miliona', False), # 10^6
3: ('bilion', 'biliona', 'biliona', False), # 10^9
4: ('trilion', 'triliona', 'triliona', False), # 10^12
5: ('kvadrilion', 'kvadriliona', 'kvadriliona', False), # 10^15
6: ('kvintilion', 'kvintiliona', 'kvintiliona', False), # 10^18
7: ('sekstilion', 'sekstiliona', 'sekstiliona', False), # 10^21
8: ('septilion', 'septiliona', 'septiliona', False), # 10^24
9: ('oktilion', 'oktiliona', 'oktiliona', False), # 10^27
10: ('nonilion', 'noniliona', 'noniliona', False), # 10^30
}


class Num2Word_SR(Num2Word_Base):
CURRENCY_FORMS = {
'RUB': (
('rublja', 'rublje', 'rublji', True),
('kopejka', 'kopejke', 'kopejki', True)
),
'EUR': (
('evro', 'evra', 'evra', False),
('cent', 'centa', 'centi', False)
),
'RSD': (
('dinar', 'dinara', 'dinara', False),
('para', 'pare', 'para', True)
),
}

def setup(self):
self.negword = "minus"
self.pointword = "zapeta"

def to_cardinal(self, number, feminine=False):
n = str(number).replace(',', '.')
if '.' in n:
left, right = n.split('.')
return u'%s %s %s' % (
self._int2word(int(left), feminine),
self.pointword,
self._int2word(int(right), feminine)
)
else:
return self._int2word(int(n), feminine)

def pluralize(self, number, forms):
if number % 100 < 10 or number % 100 > 20:
if number % 10 == 1:
form = 0
elif 1 < number % 10 < 5:
form = 1
else:
form = 2
else:
form = 2
return forms[form]

def to_ordinal(self, number):
raise NotImplementedError()

def _cents_verbose(self, number, currency):
return self._int2word(
number,
self.CURRENCY_FORMS[currency][1][-1]
)

def _int2word(self, number, feminine=False):
if number < 0:
return ' '.join([self.negword, self._int2word(abs(number))])

if number == 0:
return ZERO[0]

words = []
chunks = list(splitbyx(str(number), 3))
chunk_len = len(chunks)
for chunk in chunks:
chunk_len -= 1
digit_right, digit_mid, digit_left = get_digits(chunk)

if digit_left > 0:
words.append(HUNDREDS[digit_left][0])

if digit_mid > 1:
words.append(TWENTIES[digit_mid][0])

if digit_mid == 1:
words.append(TENS[digit_right][0])
elif digit_right > 0:
is_feminine = feminine or SCALE[chunk_len][-1]
gender_idx = int(is_feminine)
words.append(
ONES[digit_right][gender_idx]
)

if chunk_len > 0 and chunk != 0:
words.append(self.pluralize(chunk, SCALE[chunk_len]))

return ' '.join(words)

def to_currency(self, val, currency='EUR', cents=True, seperator=',',
adjective=False):
"""
Args:
val: Numeric value
currency (str): Currency code
cents (bool): Verbose cents
seperator (str): Cent seperator
adjective (bool): Prefix currency name with adjective
Returns:
str: Formatted string
"""
left, right, is_negative = parse_currency_parts(val)

try:
cr1, cr2 = self.CURRENCY_FORMS[currency]

except KeyError:
raise NotImplementedError(
'Currency code "%s" not implemented for "%s"' %
(currency, self.__class__.__name__))

if adjective and currency in self.CURRENCY_ADJECTIVES:
cr1 = prefix_currency(
self.CURRENCY_ADJECTIVES[currency],
cr1
)

minus_str = "%s " % self.negword if is_negative else ""
cents_str = self._cents_verbose(right, currency) \
if cents else self._cents_terse(right, currency)

return u'%s%s %s%s %s %s' % (
minus_str,
self.to_cardinal(left, feminine=cr1[-1]),
self.pluralize(left, cr1),
seperator,
cents_str,
self.pluralize(right, cr2)
)

0 comments on commit 1ed09f5

Please sign in to comment.